开源软件|嵌入式开发:技巧和窍门——引导加载程序跳转到应用程序代码

开源软件|嵌入式开发:技巧和窍门——引导加载程序跳转到应用程序代码

引导加载程序几乎包含在每个嵌入式系统中 , 并提供了一种在现场更新应用程序代码的好方法 , 而无需访问编程端口 。 与引导加载程序一样重要的是 , 嵌入式开发人员在尝试从引导加载程序跳转到他们的应用程序代码时经常会被出错 。 跳跃需要干净利落 , 但有几个因素会导致问题 , 例如:

l 一次写入寄存器(例如看门狗寄存器)
l 时钟设置
l 堆栈和程序指针
l 外围设置
开发人员可以通过两种不同的方式从引导加载程序干净地过渡到应用程序代码 。 第一种方法涉及开发人员仔细匹配他们的应用程序代码和引导程序设置 。 例如 , 开发人员将匹配看门狗寄存器、时钟设置 , 甚至可能匹配UART等外设 。 引导加载程序将所有这些组件初始化为已知的系统状态 , 并简单地将程序执行交给应用程序代码 。 这样做的问题是 , 应用程序代码中的任何更改也需要在引导装载程序中进行更改 。
处理引导加载程序和应用程序代码之间跳转的第二种也是更干净的方法是遵循一个简单的过程 , 将引导加载程序所涉及的任何设置恢复到它们最初的重置状态 。 这将包括外设 , 如GPIO、时钟 , 甚至修改堆栈和程序计数器 。 当嵌入式开发人员这样做时 , 应用程序代码完全不知道内存中正在执行另一个应用程序 , 唯一需要匹配的设置是只能写入一次的寄存器!
准备从引导程序跳转到应用程序代码的过程很简单 , 可以在下面找到:
l 确认应用复位向量已经编程
l 验证应用程序校验和及安全凭证
l 对外设进行去初始化 , 并将其置于复位状态
l 将向量表寄存器设置为应用复位向量(ARM)
l 将堆栈指针寄存器设置为应用程序起始地址
l 将程序计数器设置为复位向量(跳转到应用程序)

让我们简单讨论一下每一步 。 在引导加载程序做任何事情之前 , 它需要验证应用程序的完整性 。 第一步是验证复位向量已经编程 , 并且不是0xFFFFFFFF或0x00000000 。 通常 , 擦除的闪存会设置其所有位 , 因此如果我们看到这种状态 , 则引导加载程序知道有问题 , 应该保持在引导加载程序中 , 这是一种已知的安全状态 。 请记住 , 我们假设两者之间的任何编程值都是正确的 , 这可能是一个糟糕的假设 , 但对于今天嵌入式开发人员来说已经足够好了 。
接下来 , 引导装载程序应该对应用程序空间执行校验和计算 , 以确保应用程序是有效的 。 同样 , 如果在更新过程中出现问题 , 可能会有一个带有复位向量的部分程序 , 如果没有校验和 , 就无法知道这一点 。 最重要的是 , 引导装载程序还应该检查任何数字签名或安全措施 , 以确保不仅应用程序是完整的 , 而且其来源也是正确的 , 它不是恶意软件或修改过的程序 。
一旦引导装载程序验证了应用程序是完整的并且来自正确的来源 , 就该回到初始状态了 。 任何被触摸的非一次写入的外设都应该被放回到它们的复位状态 。 最好的方法是查看所用的驱动器、驱动器初始化和访问的寄存器 , 然后在数据手册中查找这些寄存器 。 每个数据手册都显示了上电复位寄存器的值 。 对于每个被修改的寄存器 , 它们可以被复位到这些状态 。 通常 , 我会避免更改时钟寄存器、看门狗和一次写入寄存器 。 我只是在应用程序和引导装载程序之间匹配这些状态 。
此时 , 微控制器回到复位状态 , 并准备运行应用程序代码 。 在此之前 , 需要更新向量表寄存器 , 以指向应用程序的向量表位置 , 而不是引导加载程序 。 中断向量表可以位于任何地方 , 因此引导装载程序和应用程序链接器文件之间需要协调 。 例如 , 嵌入式开发人员将编写如下单行代码 , 其中PROGRAM_FLASH_BASE是应用程序的第一个向量表位置: