uC/OS-II Porting to Intel X86 Platform

32
uC/OS-II Porting to Intel X86 Platform 2010/04/29 Yufeng Lin

description

uC/OS-II Porting to Intel X86 Platform. 2010/04/29 Yufeng Lin. Outline. uC/OS-II Porting Multicore Boot Demo SMP uC/OS-II. uC/OS-II. uC/OS-II Porting Limit. Hardware limit 處理器的 C 編譯器能產生可重入程式碼。 用 C 語言就可以打開和關閉中斷。 - PowerPoint PPT Presentation

Transcript of uC/OS-II Porting to Intel X86 Platform

Page 1: uC/OS-II  Porting to Intel X86 Platform

uC/OS-II Porting to Intel X86 Platform

2010/04/29 Yufeng Lin

Page 2: uC/OS-II  Porting to Intel X86 Platform

2/32

Outline

uC/OS-II Porting Multicore Boot Demo SMP uC/OS-II

Page 3: uC/OS-II  Porting to Intel X86 Platform

3/32

uC/OS-II

Page 4: uC/OS-II  Porting to Intel X86 Platform

4/32

uC/OS-II Porting Limit

Hardware limit 處理器的 C 編譯器能產生可重入程式碼。 用 C 語言就可以打開和關閉中斷。 處理器支援中斷,並且能產生定時中斷 ( 通常在 10 至 100Hz 之間 ) 。 處理器支援能夠容納一定數量的資料的硬體堆疊 ( 可能是幾千位元組 ) 。 處理器有將堆疊指標和其他 CPU 暫存器讀出和儲存到堆疊或記憶體中的指令。

Page 5: uC/OS-II  Porting to Intel X86 Platform

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

Page 6: uC/OS-II  Porting to Intel X86 Platform

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 入口

Page 7: uC/OS-II  Porting to Intel X86 Platform

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

Page 8: uC/OS-II  Porting to Intel X86 Platform

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)

Page 9: uC/OS-II  Porting to Intel X86 Platform

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)

Page 10: uC/OS-II  Porting to Intel X86 Platform

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)

Page 11: uC/OS-II  Porting to Intel X86 Platform

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)

Page 12: uC/OS-II  Porting to Intel X86 Platform

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()

Page 13: uC/OS-II  Porting to Intel X86 Platform

13/32

uC/OS-II Porting OS_CPU_C.c

OSTaskStkInit() OSTaskCreateHook() OSTaskDelHook() OSTaskSwHook() OSTaskStatHook() OSTimeTickHook()

Page 14: uC/OS-II  Porting to Intel X86 Platform

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;

Page 15: uC/OS-II  Porting to Intel X86 Platform

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

Page 16: uC/OS-II  Porting to Intel X86 Platform

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

Page 17: uC/OS-II  Porting to Intel X86 Platform

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; }

Page 18: uC/OS-II  Porting to Intel X86 Platform

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)

Page 19: uC/OS-II  Porting to Intel X86 Platform

19/32

若是此函式完成後 可以觀察到 taskA 的動作 若是沒有預期動作,則表示先前的準備工作尚未全部完成 Ex.bootloader 、 OS_CPU.h……

OS_CPU_A.asm --- OSStartHighRdy(3/3)

Page 20: uC/OS-II  Porting to Intel X86 Platform

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 的堆疊中恢復出來 ;  執行中斷返回指令 ;

}

Page 21: uC/OS-II  Porting to Intel X86 Platform

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)

Page 22: uC/OS-II  Porting to Intel X86 Platform

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)

Page 23: uC/OS-II  Porting to Intel X86 Platform

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)

Page 24: uC/OS-II  Porting to Intel X86 Platform

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 持續到來,表示成功

Page 25: uC/OS-II  Porting to Intel X86 Platform

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

Page 26: uC/OS-II  Porting to Intel X86 Platform

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

Page 27: uC/OS-II  Porting to Intel X86 Platform

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`

Page 28: uC/OS-II  Porting to Intel X86 Platform

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

Page 29: uC/OS-II  Porting to Intel X86 Platform

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

Page 30: uC/OS-II  Porting to Intel X86 Platform

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

Page 31: uC/OS-II  Porting to Intel X86 Platform

31/32

Demo

Page 32: uC/OS-II  Porting to Intel X86 Platform

32/32

Questions?