AP初始化 - RSWiki
Download
Report
Transcript AP初始化 - RSWiki
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
2/32
uC/OS-II
3/32
uC/OS-II Porting Limit
Hardware limit
處理器的 C 編譯器能產生可重入程式碼。
用 C 語言就可以打開和關閉中斷。
處理器支援中斷,並且能產生定時中斷 (通常在 10 至 100Hz 之間)。
處理器支援能夠容納一定數量的資料的硬體堆疊 (可能是幾千位元
組)。
處理器有將堆疊指標和其他 CPU 暫存器讀出和儲存到堆疊或記憶
體中的指令。
4/32
uC/OS-II Porting
1. 裝置啟動後,執行完硬體的初始設定或是硬體狀態檢查,就
main
OSInit
OSStartHigeReady
直接跳轉到 AP 執行的位置
2. uC/OS-II 一般啟動流程
OSStart
Task create
Do something
Task 1
OSStatrtTimeDly
Context switch
Other Tasks
5/32
uC/OS-II Porting
Porting = bootloader + OS
Bootloader
There are two ways
Initialize hardware, vectors, memory, stack, register value
分開成兩個檔案(bin),bootloader裡面要設定OS Image存放的位址
(OS入口),需要兩者一致方可成功啟動OS
合成一個bin檔,在bootloader執行完以後,透過跳轉__main進入
OS入口
…
6/32
uC/OS-II Porting --- Bootloader
Reset
EntryPoint32
Real mode
47
Set PE in CR0
Clear instr.
queue
Protected mode
NEAR
; Initialize allDescriptor
segment registersTable)
to 10h (entry
#2
16 GDTR
bit
LDT(Local
is
GDT
= Global
Descriptor
Table
in the GDT)
_Gdt: the same
16 15
mov
ax,10h
; entry #2 in GDT
DD
DD
32
Build GDT
PROC
0 ; GDT[0]: Null entry, never used.
16 bit Table
ds,ax
; ds = 10h
0 mov
bit Linear
Base
Address
mov
es,ax
; es = 10h Limit
mov read-only
fs,axcode, base
; fs address
= 10h of 0,
; GDT[1]: Executable,
mov
gs,ax
;
gs
= 10h
;limit of FFFFFh, granularity bit (G) set (making
the limit 4GB)
mov
ss,ax
; ss = 10h
DW
0FFFFh
; Limit[15..0]
DW
DB
DB
DB
DB
mov
eax,cr0
GDT or
addresseax,1
mov
cr0,eax
GDT Size
0000h
; Base[15..0]
; Set the top
of stack to allow stack operations.
00h
; Base[23..16]
10011010b ; P(1) DPL(00) S(1) 1 C(0) R(1) A(0)
mov
esp,8000h
; arbitrary top of stack
11001111b
; G(1)
D(1) 0 0 Limit[19..16]
00h
; Base[31..24]
; Call main(), which is not expected to return.
calldata segment
_main
; GDT[2]: Writable
32 bit
16 clear
bit Protected
Jump.386p
could
mode
LGDTqueue
GDT
DW instruction
0FFFFh
; Limit[15..0]
DW
DB
DB
DB
DB
; Base[15..0]
Jmp 0000h
EntryPoint32
00h
; Base[23..16]
10010010b ; P(1)
DPL(00) S(1)
0 E(0) W(1)
A(0)
uC/OS-II
Main
Function
11001111b ; G(1) B(1) 0 0 Limit[19..16]
00h
; Base[31..24]
7/32
0
uC/OS-II Porting --- Hardware Init. (1/4)
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
interrupt 30h handler
8/32
uC/OS-II Porting --- Hardware Init. (2/4)
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);
SetIntVector(0x30, OSCtxSw);
}
// Install the tick handler
// Install uCOS-II's context switch handler
9/32
uC/OS-II Porting --- Hardware Init. (3/4)
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.
10/32
uC/OS-II Porting --- Hardware Init. (4/4)
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);
11/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()
12/32
uC/OS-II Porting
OS_CPU_C.c
OSTaskStkInit()
OSTaskCreateHook()
OSTaskDelHook()
OSTaskSwHook()
OSTaskStatHook()
OSTimeTickHook()
13/32
OS_CPU.h (1/2)
Compiler dependent(請查看compiler手冊)
typedef unsigned char
typedef unsigned char
typedef char
typedef unsigned short
typedef short
typedef unsigned long
typedef long
typedef float
typedef double
typedef INT32U
BOOLEAN;
INT8U;
INT8S;
INT16U;
INT16S;
INT32U;
INT32S;
FP32;
FP64;
OS_STK;
14/32
OS_CPU.h (2/2)
Processor dependent
#define
#define
#define
#define
OS_ENTER_CRITICAL()
OS_EXIT_CRITICAL()
OS_STK_GROWTH
OS_TASK_SW()
__asm PUSHFD __asm CLI
__asm POPFD
1
__asm INT 0x30
15/32
OS_CPU_A.asm
一般寫完bootloader後,常要觀察其與OS搭配後,是否可以順
利進入OS
為了往後可以測試task context switch 是否成功,因此建立2個
task,不同的priority,分別進行不同動作,例如輸出不同字元
taskA priority 5 並在while迴圈中加入OSTimeDly
taskB priority 10並在while迴圈中加入OSTimeDly
16/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;
}
17/32
OS_CPU_A.asm --- OSStartHighRdy(2/3)
OSStartHighRdy
OSRunning = TRUE
mov eax,[_OSTCBHighRdy]
mov esp,[eax]
Pop all the processor registers from the stack
mov eax, 1h
mov [ _OSRunning ], eax
Load the processor stack pointer with OSTCBHighRdy->OSTCBStkPtr
call _OSTaskSwHook ; Call OSTaskSwHook();
popad
Execute a Return from interrupt intruction
iretd
18/32
OS_CPU_A.asm --- OSStartHighRdy(3/3)
若是此函式完成後
可以觀察到taskA的動作
若是沒有預期動作,則表示先前的準備工作尚未全部完成
Ex.bootloader、OS_CPU.h……
19/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 的堆疊中恢復出來;
20/32
執行中斷返回指令;
OS_CPU_A.asm --- OSCtxSw(2/4)
OSCtxSw
保存處理器暫存器
pushad
OSTCBCur->OSTCBStkPtr = Stack pointer
mov eax,[_OSTCBCur]
mov [eax],esp
; Stack pointer is ESP
21/32
OS_CPU_A.asm --- OSCtxSw(3/4)
呼叫使用者定義的 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
22/32
OS_CPU_A.asm --- OSCtxSw(4/4)
Stack pointer = OSTCBHighRdy->OSTCBStkPtr
mov esp,[eax]
; ESP = OSTCBHighRdy->OSTCBStkPtr
將所有處理器暫存器從新 task 的堆疊中恢復出來
執行中斷返回指令
popad
iretd
此函式成功,則在taskA動作結束後,taskB會隨之動作
23/32
OS_CPU_A.asm --- OSTickISR
OSTickISR
保存處理器暫存器
Send an end-of-interrupt to the i8259
_OSIntEnter
call
_OSTimeTick
call
_OSIntExit
恢復處理器暫存器
call
呼叫 OSIntExit()
al,20h
20h,al
呼叫 OSTimeTick()
mov
out
呼叫 OSIntEnter() 或者直接將 OSIntNesting 加 1
pushad
popad
執行中斷返回指令
iretd
此函式完成後,可以在此函式中加入輸出,
若是此輸出可以依照interval持續到來,表示成功
24/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
25/32
IMC
R
BSP
PIC Mode CPU 1
AP
CPU 2
AP
CPU 3
Local
APIC
0
Local
APIC
1
Local
APIC
2
NMI
LINTIN1
LINTIN0
RESET
ICC BUS
8259
I/O
Interrupt
INT
APIC
26/32
Interrupt Mode
IMC
R
APIC
Mode
NMI
BSP
CPU 1
AP
CPU 2
AP
CPU 3
Local
APIC
0
Local
APIC
1
Local
APIC
2
LINTIN1
LINTIN0
RESET
ICC BUS
8259
I/O
Interrupt
INT`
APIC
27/32
Multicore Boot
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
28/32
Multicore Boot
BSP初始化過程
Intel spec
AP初始化
1. 初始化memory
2. Load microcode到處理器
1. 獲取信號量,開始初始化
3. 初始化內存範圍寄存器(MTRRs)
2. load microcode到處理器
4. enable cache
3. 初始化內存範圍寄存器(MTRRs)
5. 確定BSP是否是"GenuineIntel"
4. enable cache
6. 執行CPUID,保存CPU信息為將來使用
5. 檢查AP是否是"GenuineIntel"
7. load AP的啟動代碼到低1M的一個4K的頁裡
6. 保存CPUID信息,為將來使用
8. 切換到保護模式
7. 切換到保護模式
9. 轉換4K的頁基址為一個8位的向量. 例如
8. 配置AP的共存內存接口執行環境
0x000BD000H --> 0xBDH
9. 將處理器個數加1
10.設置APIC的SVR的bit8來enable local APIC
10.釋放信號量
11.建立錯誤處理handler
11. 執行CLI並且進入halt狀態
12.初始化鎖信號量
12.等待INIT IPI
13.探測系統中的AP, 方法如下:
- 設置處理器COUNT為1
- 啟動一個timer,BSP開始等待
- 此時AP開始初始化,並將COUNT加1
- timer到期,BSP檢查COUNT,如果沒有增加,就表示系統中沒有AP.
29/32
14. 等timer中斷,檢查COUNT並建立處理器數目
Multicore Boot
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
30/32
Demo
31/32
Questions?
32/32