欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

操作系统移植(一)--启动分析

程序员文章站 2022-05-22 18:59:32
...

前言

从本节开始,开始讲述一些关于OS移植的内容;与Linux移植不同,本次讲的是嵌入式实时操作系统的移植,类似于ucos-ii这种,所以后面在说到任务抢占时,可能和熟知的Linux有所区别。

启动代码

众所周知操作系统的运行依赖于底层硬件环境,无论是Windows还是Linux,操作系统运行前的操作肯定是一系列初始化操作;自己装过Windows的人一定都见过一个叫BIOS的界面,在这里面为Windows分配系统盘,设置启动方式等。为什么要指定系统盘?我们开机后,计算机是如何从系统盘检测到操作系统而不是其他硬盘分区?在操作系统启动前,计算机还做了哪些事?这些就是本章将要讲述的内容。

startup.s

在操作系统运行前,都会先对CPU进行初始化操作,各个厂家都会提供类似于startup.s一类的文件,用于CPU初始化,也即启动代码。我们知道操作系统一般都是用C语音写的,但C语言的执行需要堆栈环境,CPU刚启动时,并没有分配堆栈区域,C语言是无法执行的,所以启动代码都是汇编实现的。

                PUBWEAK Reset_Handler
                SECTION .text:CODE:NOROOT:REORDER(2)
Reset_Handler
                LDR     R0, =SystemInit
                BLX     R0
                LDR     R0, =__iar_program_start
                BX      R0

如上代码,在startup.s文件中,会定义一系列中断,其中Reset_Handler是复位中断服务函数,即系统复位时执行的第一个中断,SystemInit是系统提供的初始化函数,CPU使用的时钟初始化操作,一般放在此处进行。__iar_program_start是编译器相关的,经过一些操作后,最终指向main()函数入口。
在操作系统移植时,根据处理器或操作系统要求,通常会修改Reset_Handler,例如添加中断向量表重定向操作,指定MSP(主堆栈指针)和PSP(进程堆栈指针)等。

                PUBWEAK NMI_Handler
                SECTION .text:CODE:NOROOT:REORDER(1)
NMI_Handler
                B       NMI_Handler

                PUBWEAK HardFault_Handler
                SECTION .text:CODE:NOROOT:REORDER(1)
HardFault_Handler
                B       HardFault_Handler

                PUBWEAK MemManage_Handler
                SECTION .text:CODE:NOROOT:REORDER(1)
MemManage_Handler
                B       MemManage_Handler

                PUBWEAK BusFault_Handler
                SECTION .text:CODE:NOROOT:REORDER(1)
BusFault_Handler
                B       BusFault_Handler

                PUBWEAK UsageFault_Handler
                SECTION .text:CODE:NOROOT:REORDER(1)
UsageFault_Handler
                B       UsageFault_Handler

                PUBWEAK SVC_Handler
                SECTION .text:CODE:NOROOT:REORDER(1)
SVC_Handler
                B       SVC_Handler

后面是其它的中断服务例程,操作系统移植时一般不需要修改,默认使用官方的就行。

中断向量表重定向

对于Cortex-M3系列的处理器来说,当中断产生时,默认是从0地址处查找中断向量表;但通常情况下,0地址处一般存放的是BOOT(例如U-BOOT)代码,用于系统运行前的相关配置操作。如果0地址处存放的是BOOT代码,当中断产生时,中断指针指向的就是BOOT,这样就会因为找不到相应中断服务例程,而产生异常。所以需要把中断向量表重定向,让中断产生时,从其他地址处查找中断向量表。

                PUBWEAK Reset_Handler
                SECTION .text:CODE:NOROOT:REORDER(2)
Reset_Handler
                mrs     r0, CONTROL
                orr     r0, r0, #0x2
                msr     CONTROL, r0
                isb

                ldr     r0, =SFE(CSTACK)
                msr     psp, r0

                LDR     R0, =__vector_table
                LDR     R1, =SCB_VTOR
                STR     R0, [R1]

                LDR     R0, =SystemInit
                BLX     R0
                LDR     R0, =__iar_program_start
                BX      R0

在上面的代码中

                LDR     R0, =__vector_table
                LDR     R1, =SCB_VTOR
                STR     R0, [R1]

SCB_VTOR是中断向量表重定向寄存器地址,__vector_table是重定向的地址,__vector_table在启动配置文件.icf中指定;添加这三步操作后,当有中断产生时,处理器就会从地址__vector_table处查找中断向量表。当然关于__vector_table也不是随意指定的,应合理划分存储器,防止因地址出错,而导致系统奔溃。

指定MSP/PSP

操作系统移植(一)--启动分析

C语言的运行,需要堆栈环境,通常栈生长方向向下的处理器,内存分配如上图所示。
在Cortex-M3系列处理器中,将SP(堆栈指针)分成了MSP和PSP两种。

  • MSP:主堆栈指针,用于操作系统系统内核以及异常处理例程(包括中断服务例程)
  • PSP:进程堆栈指针,用户的应用程序使用的堆栈

提供两种堆栈指针的目的是,将用户程序(或者说上层应用程序)的堆栈区域和操作系统的堆栈区域分开。为了防止用户程序的堆栈错误从而破坏OS的堆栈,致使OS奔溃,Cortex-M提供了两种堆栈供我们使用,MSP–主堆栈、PSP–进程堆栈。
Cortex-M复位时,默认操作系统和用户程序使用的是同一个堆栈区,为了防止上诉情况的发生,我们可以设置使OS的堆栈和用户程序的堆栈分开使用。让操作系统使用MSP,用户程序使用PSP,这样就可以避免因为用户程序的堆栈错误从而破坏OS的堆栈,致使OS奔溃。
操作系统使用的堆栈和用户程序使用的堆栈,可以理解为内核使用的堆栈和上层应用使用的堆栈,简单的可以理解为,开辟了两个堆栈区,用两个指针指向这两个区域。

                mrs     r0, CONTROL
                orr     r0, r0, #0x2
                msr     CONTROL, r0
                isb

                ldr     r0, =SFE(CSTACK)
                msr     psp, r0

CONTROL:控制寄存器,定义特权状态 ,并且决定使用哪一个堆栈指针

操作系统移植(一)--启动分析

通过向CONTROL控制寄存器写入不同值,可以决定使用哪个堆栈指针。SFE(CSTACK)是堆栈区域的地址。

总结

各处理器厂家,一般都会提供类似于 startup.s这种的启动文件,用于CPU初始化;但处理器厂家提供的一般都是通用或者示例代码,使用时要根据操作系统或具体的处理器做相应的修改。通常所做的操作就是指定堆栈指针、中断向量表重定向。
操作系统移植时,除了要修改启动文件外,还有一个至关重要的文件–配置文件.icf,用于指定内存分配。链接器 Linker 就是根据该文件来为变量分配对应的区域。.icf文件的作用及如何修改,在下面的文章将做解释。