uC/OS-II Porting to Intel X86 Platform
description
Transcript of uC/OS-II Porting to Intel X86 Platform
uC/OS-II Porting to Intel X86 Platform
2010/04/29 Yufeng Lin
2/32
Outline
uC/OS-II Porting Multicore Boot Demo SMP uC/OS-II
3/32
uC/OS-II
4/32
uC/OS-II Porting Limit
Hardware limit 處理器的 C 編譯器能產生可重入程式碼。 用 C 語言就可以打開和關閉中斷。 處理器支援中斷,並且能產生定時中斷 ( 通常在 10 至 100Hz 之間 ) 。 處理器支援能夠容納一定數量的資料的硬體堆疊 ( 可能是幾千位元組 ) 。 處理器有將堆疊指標和其他 CPU 暫存器讀出和儲存到堆疊或記憶體中的指令。
5/32
1. 裝置啟動後,執行完硬體的初始設定或是硬體狀態檢查,就直接跳轉到 AP 執行的位置 2. uC/OS-II 一般啟動流程
main OSInit
Task create
OSStatrt
OSStart
OSStartHigeReady
Task 1
Do something
TimeDly
Other Tasks
Context switch
uC/OS-II Porting
6/32
uC/OS-II Porting
Porting = bootloader + OS Bootloader
Initialize hardware, vectors, memory, stack, register value There are two ways
分開成兩個檔案 (bin) , bootloader 裡面要設定 OS Image 存放的位址 (OS 入口 ) ,需要兩者一致方可成功啟動 OS 合成一個 bin 檔,在 bootloader 執行完以後,透過跳轉 __main 進入 OS 入口
…
7/32
Real mode
Protected mode
Build GDT
Set PE in CR0
Clear instr. queue
Reset
16 bit_Gdt: DD 0 ; GDT[0]: Null entry, never used. DD 0
; GDT[1]: Executable, read-only code, base address of 0,;limit of FFFFFh, granularity bit (G) set (making the limit 4GB)DW 0FFFFh ; Limit[15..0]DW 0000h ; Base[15..0]DB 00h ; Base[23..16]DB 10011010b ; P(1) DPL(00) S(1) 1 C(0) R(1) A(0)DB 11001111b ; G(1) D(1) 0 0 Limit[19..16]DB 00h ; Base[31..24] ; GDT[2]: Writable data segment DW 0FFFFh ; Limit[15..0] DW 0000h ; Base[15..0] DB 00h ; Base[23..16] DB 10010010b ; P(1) DPL(00) S(1) 0 E(0) W(1) A(0) DB 11001111b ; G(1) B(1) 0 0 Limit[19..16] DB 00h ; Base[31..24]
GDT = Global Descriptor Table
LGDT GDT
32 bit Linear Base Address 16 bit Table Limit
47 16 15 0
GDTR
GDT address GDT Size
.386p
LDT(Local Descriptor Table) is the same
mov eax,cr0or eax,1mov cr0,eax
16 bit Protected mode
Jump could clear instruction queueJmp EntryPoint32
EntryPoint32 PROC NEAR
; Initialize all segment registers to 10h (entry #2 in the GDT)
mov ax,10h ; entry #2 in GDT mov ds,ax ; ds = 10h mov es,ax ; es = 10h mov fs,ax ; fs = 10h mov gs,ax ; gs = 10h mov ss,ax ; ss = 10h ; Set the top of stack to allow stack operations. mov esp,8000h ; arbitrary top of stack ; Call main(), which is not expected to return.call _main
uC/OS-II Main Function
32 bit
uC/OS-II Porting --- Bootloader
8/32
The rest of the hardware initialization is performed in the application. Once in main(), a call is done to OsCpuInit(), in order to perform the following: Enable the address line 20 Relocate the IRQ interrupts Initialize the interrupt table
The clock handler is installed as the interrupt 20h handler The uC/OS-II context switch handler is installed as the inter
rupt 30h handler
uC/OS-II Porting --- Hardware Init. (1/4)
9/32
void OSCpuInit() { int IntNo;
InitA20(); // Enable address line 20 InitPIC(); // Relocate IRQs to 0x20-0x2F
// Install a default handler for all supported interrupts: // a) 0x00-0x1F: Intel-reserved // b) 0x20-0x2F: IRQ (relocated) // c) 0x30-0x3F: Available for uCOS and application.
for (IntNo = 0; IntNo < 0x40; IntNo++) SetIntVector(IntNo, DefIntHandler);
SetIntVector(0x20, OSTickISR); // Install the tick handler SetIntVector(0x30, OSCtxSw); // Install uCOS-II's context switch handler }
uC/OS-II Porting --- Hardware Init. (2/4)
10/32
The A20 line is enabled by sending a few commands to the Intel 8042 keyboard controller.
InitA20 do {status = inportb(0x64);} while (status & 2); // Wait until i8042 can receive the command. outportb(0x64, 0xd1); // Send the 'write' command to the i8042. do { status = inportb(0x64);} while (status & 2); // Wait until i8042 can receive the command. outportb(0x60, 0xdf); // Send the new set-up. do { status = inportb(0x64);} while (status & 2); // Wait until i8042 has received the command.
uC/OS-II Porting --- Hardware Init. (3/4)
11/32
Relocate the IRQ lanes from 0x00 to 0x20 to prevent conflicts between IRQ and CPU's exceptions
InitPIC // Reprogram the master 8259. outportb(I8259_A0, 0x11);
outportb(I8259_A1, 0x20); outportb(I8259_A1, 0x04); outportb(I8259_A1, 0x01); outportb(I8259_A1, 0x00);
// Reprogram the slave 8259. outportb(I8259_B0, 0x11);
outportb(I8259_B1, 0x28); outportb(I8259_B1, 0x02); outportb(I8259_B1, 0x01); outportb(I8259_B1, 0x00);
uC/OS-II Porting --- Hardware Init. (4/4)
12/32
uC/OS-II Porting OS_CPU.h
Define Data type & register value depend on hardware specification & compiler
OS_CPU_A.asm OSStartHighRdy() OSCtwSw() OSTickISR() OSIntCtxSw()
13/32
uC/OS-II Porting OS_CPU_C.c
OSTaskStkInit() OSTaskCreateHook() OSTaskDelHook() OSTaskSwHook() OSTaskStatHook() OSTimeTickHook()
14/32
OS_CPU.h (1/2) Compiler dependent( 請查看 compiler 手冊 )
typedef unsigned char BOOLEAN; typedef unsigned char INT8U; typedef char INT8S; typedef unsigned short INT16U; typedef short INT16S; typedef unsigned long INT32U; typedef long INT32S; typedef float FP32; typedef double FP64; typedef INT32U OS_STK;
15/32
OS_CPU.h (2/2) Processor dependent
#define OS_ENTER_CRITICAL() __asm PUSHFD __asm CLI #define OS_EXIT_CRITICAL() __asm POPFD #define OS_STK_GROWTH 1 #define OS_TASK_SW() __asm INT 0x30
16/32
OS_CPU_A.asm 一般寫完 bootloader 後,常要觀察其與 OS 搭配後,是否可以順利進入 OS 為了往後可以測試 task context switch 是否成功,因此建立 2個 task ,不同的 priority ,分別進行不同動作,例如輸出不同字元 taskA priority 5 並在 while 迴圈中加入 OSTimeDly taskB priority 10 並在 while 迴圈中加入 OSTimeDly
17/32
OS_CPU_A.asm --- OSStartHighRdy(1/3)
OSStartHighRdy() 由於 OSstart() 後,會去執行 schedule ,並且挑出最高 priority 的 task 執行 Pseudo code
void OSStartHighRdy (void) {
Call user definable OSTaskSwHook(); Get the stack pointer of the task to resume: Stack pointer = OSTCBHighRdy->OSTCBStkPtr; OSRunning = TRUE; Restore all processor registers from the new task's stack; Execute a return from interrupt instruction; }
18/32
OSStartHighRdy call _OSTaskSwHook ; Call OSTaskSwHook();
OSRunning = TRUE mov eax, 1h mov [ _OSRunning ], eax
Load the processor stack pointer with OSTCBHighRdy->OSTCBStkPtr mov eax,[_OSTCBHighRdy] mov esp,[eax]
Pop all the processor registers from the stack popad
Execute a Return from interrupt intruction iretd
OS_CPU_A.asm --- OSStartHighRdy(2/3)
19/32
若是此函式完成後 可以觀察到 taskA 的動作 若是沒有預期動作,則表示先前的準備工作尚未全部完成 Ex.bootloader 、 OS_CPU.h……
OS_CPU_A.asm --- OSStartHighRdy(3/3)
20/32
OS_CPU_A.asm --- OSCtxSw(1/4) OSCtxSw()
若是目前 task ready queue 中有更高 priority 的 task ,則使用此函式將目前正在執行的 task 與之交換
Pseudo code void OSCtxSw(void) {
保存處理器暫存器 ; 將當前 task 的堆疊指標保存到當前 task 的 OS_TCB 中 : OSTCBCur->OSTCBStkPtr = Stack pointer; 呼叫使用者定義的 OSTaskSwHook(); OSTCBCur = OSTCBHighRdy; OSPrioCur = OSPrioHighRdy; 得到需要恢復的 task 的堆疊指標 : Stack pointer = OSTCBHighRdy->OSTCBStkPtr; 將所有處理器暫存器從新 task 的堆疊中恢復出來 ; 執行中斷返回指令 ;
}
21/32
OSCtxSw 保存處理器暫存器
pushad OSTCBCur->OSTCBStkPtr = Stack pointer
mov eax,[_OSTCBCur] mov [eax],esp ; Stack pointer is ESP
OS_CPU_A.asm --- OSCtxSw(2/4)
22/32
呼叫使用者定義的 OSTaskSwHook()
call _OSTaskSwHook OSTCBCur = OSTCBHighRdy
mov al,[_OSPrioHighRdy] ; AL is OSPrioHighRdy
mov [_OSPrioCur],al OSPrioCur = OSTCBHighRdy
mov eax,[_OSTCBHighRdy] ; EAX is OSTCBHighRdy
mov [_OSTCBCur],eax
OS_CPU_A.asm --- OSCtxSw(3/4)
23/32
Stack pointer = OSTCBHighRdy->OSTCBStkPtr mov esp,[eax] ; ESP = OSTCBHighRdy->OSTC
BStkPtr
將所有處理器暫存器從新 task 的堆疊中恢復出來 執行中斷返回指令
popad iretd
此函式成功,則在 taskA 動作結束後, taskB 會隨之動作
OS_CPU_A.asm --- OSCtxSw(4/4)
24/32
OS_CPU_A.asm --- OSTickISR OSTickISR
保存處理器暫存器 pushad
Send an end-of-interrupt to the i8259 mov al,20h out 20h,al
呼叫 OSIntEnter() 或者直接將 OSIntNesting 加 1 call _OSIntEnter
呼叫 OSTimeTick() call _OSTimeTick
呼叫 OSIntExit() call _OSIntExit
恢復處理器暫存器 popad
執行中斷返回指令 iretd
此函式完成後,可以在此函式中加入輸出, 若是此輸出可以依照 interval 持續到來,表示成功
25/32
Multicore Boot
Multicore BSP (Bootstrap Processor) AP (Application Processor)
How to boot AP (Application Processor) ? Disable 8259 Initialize Local APIC Initialize I/O APIC
26/32
CPU 1 CPU 2 CPU 3
Local APIC
0
Local APIC
1
Local APIC
2
Interrupt8259 I/O
APIC
IMCR
BSP AP AP
NMI
LINTIN1LINTIN0
RESET
ICC BUS
INT
PIC Mode
27/32
Interrupt Mode
CPU 1 CPU 2 CPU 3
Local APIC
0
Local APIC
1
Local APIC
2
Interrupt8259 I/O
APIC
IMCR
BSP AP AP
NMI
LINTIN1LINTIN0
RESET
ICC BUS
APIC Mode
INT`
28/32
Disable 8259 It means leave PIC mode
IMCR ( Interrupt Mode Control Register) Write 0x70 on 0x22 (choose IMCR) Write 0x01 on 0x23 (not use PIC mode) Disable(mask) 8259 all IRQ pins
Multicore Boot
29/32
Intel specBSP初始化過程1. 初始化 memory2. Load microcode到處理器3. 初始化內存範圍寄存器 (MTRRs)4. enable cache5. 確定 BSP是否是 "GenuineIntel"6. 執行 CPUID,保存 CPU信息為將來使用7. load AP的啟動代碼到低 1M的一個 4K的頁裡8. 切換到保護模式9. 轉換 4K的頁基址為一個 8位的向量 . 例如 0x000BD000H --> 0xBDH10.設置 APIC的 SVR的 bit8來 enable local APIC11.建立錯誤處理 handler12.初始化鎖信號量13.探測系統中的 AP, 方法如下: - 設置處理器 COUNT為 1 - 啟動一個 timer, BSP開始等待 - 此時 AP開始初始化,並將 COUNT加 1 - timer到期, BSP檢查 COUNT,如果沒有增加,就表示系統中沒有 AP.14. 等 timer中斷,檢查 COUNT並建立處理器數目
AP初始化1. 獲取信號量,開始初始化2. load microcode到處理器3. 初始化內存範圍寄存器 (MTRRs)4. enable cache5. 檢查 AP是否是 "GenuineIntel"6. 保存 CPUID信息,為將來使用7. 切換到保護模式8. 配置 AP的共存內存接口執行環境9. 將處理器個數加 110.釋放信號量11. 執行 CLI並且進入 halt狀態12.等待 INIT IPI
Multicore Boot
30/32
Broadcasts an INIT-SIPI-SIPI IPI sequence to the APs to wake them up and initialize
① send twice IPI (Inter-Processor Interrupt) (INIT)1. make AP initialize 2. set AP Arb ID register
② broadcast SIPI (start-up IPI)1. Number of times depend on CPU 2. Tell AP the address where it start execute
Multicore Boot
31/32
Demo
32/32
Questions?