linuxSMP啟動過程學(xué)習(xí)筆記_第1頁
linuxSMP啟動過程學(xué)習(xí)筆記_第2頁
linuxSMP啟動過程學(xué)習(xí)筆記_第3頁
linuxSMP啟動過程學(xué)習(xí)筆記_第4頁
linuxSMP啟動過程學(xué)習(xí)筆記_第5頁
已閱讀5頁,還剩31頁未讀 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

linuxSMP啟動過程學(xué)習(xí)筆記

1.SMP硬件體系結(jié)構(gòu):

對于SMP最簡單可以理解為系統(tǒng)存在多個(gè)完全相同的CPU,所

有CPU共享總線,擁有自己的寄存器。對于內(nèi)存和外部設(shè)備訪問,由

于共享總線,所以是共享的。Linux操作系統(tǒng)多個(gè)CPU共享在系統(tǒng)空

間上映射相同,是完全又樗的。

由于系統(tǒng)中存在多個(gè)CPU,這是就引入一個(gè)問題,當(dāng)外部設(shè)備產(chǎn)

生中斷的時(shí)候,具體有哪一個(gè)CPU進(jìn)行處理?

為此,intel公司提出了10APCI和LOCALAPCI的體系結(jié)構(gòu)。

10APIC連接各個(gè)外部設(shè)備,并可以設(shè)置分發(fā)類型,根據(jù)設(shè)定的

分發(fā)類型,中斷信號發(fā)送的對應(yīng)CPU的LOCALAPIC上。

LOCALAPIC負(fù)責(zé)本地CPU的中斷處理,LOCALAPIC不僅可以

接受10APIC的中斷,也需要處理本地CPU產(chǎn)生的異常。同時(shí)

LOCALAPIC還提供了一個(gè)定時(shí)器。

如何確定那個(gè)CPU是引導(dǎo)CPU?

根據(jù)intel公司中的資料,系統(tǒng)上電后,會根據(jù)MPInitialization

Protocol隨機(jī)選擇一個(gè)CPU作為BSP,只有BSP會運(yùn)行BIOS程序,

其他AP都進(jìn)入等待狀態(tài),BSP發(fā)送IPI中斷觸發(fā)后才可以運(yùn)行。具

體的MPInitializationProtocol細(xì)節(jié),可以參考Intel?64andIA-32

ArchitecturesSoftwareDeveloper'sManualVolume3A:System

ProgrammingGuide,Part1第8章。

引導(dǎo)CPU如何控制其他CPU開始運(yùn)行?

BSP可以通過IPI消息控制AP從指定的起始地址運(yùn)行。CPU中

集成的LOCALAPIC提供了這個(gè)功能??梢酝ㄟ^寫LOCALAPIC中提

供的相關(guān)寄存器,發(fā)送IPI消息到指定的CPU上。

如何獲取系統(tǒng)硬件CPU信息的?

在系統(tǒng)初始化后,硬件會在內(nèi)存的規(guī)定位置提供關(guān)于CPU,總線,

等的信息,即在初始化的過程,會

10APICSMPMPtable0linux

讀取該位置,獲取系統(tǒng)相關(guān)的硬件信息。

2.linuxSMP啟動過程流程簡介

setup_arch()

setup_memory();

reserve_bootmem(PAGE_SIZEzPAGE_SIZE);

find_smp_config();//查找smpmptable的位置

smp_alloc_memory();

trampoline_base=(void*)

alloc_bootmem_low_pages(PAGE_SIZE);〃分酉己trampoline,用于

啟動AP的引導(dǎo)代碼。

get_smp_config();//根據(jù)smpmptable,獲取具體的硬件信

trap_init()

init_apic_mappings();

mem_init()

zap_low_mappings();如果沒有定義SMP的話,清楚用戶空間

的地址映射。

rest_init();

kernel_thread(initNULL,CLONE_FS|CLONE_SIGHAND);

init();

set_cpus_a11owed(current,CPU_MASK_ALL);

smp_prepare_cpus(max_cpus);

smp_boot_cpus(max_cpus);

connect_bsp_APIC();

//初始化的

setup_local_APIC();BSPLOCALAPCIo

map_cpu_to_logical_apicid();

針對每個(gè)調(diào)用

CPUdo_boot_cpu(apicidzcpu)

smpJnitQ;//每個(gè)CPU開始進(jìn)行調(diào)度

trampoline.SAP引導(dǎo)代碼,為16進(jìn)制代碼,啟用保護(hù)模式

head.s為AP創(chuàng)建分頁管理

initialize_secondary根據(jù)之前fork創(chuàng)建設(shè)置的信息,跳轉(zhuǎn)到

start_secondary處

start-secondary判斷BSP是否啟動,如果啟動AP進(jìn)行任務(wù)調(diào)

度。

3.代碼學(xué)習(xí)總結(jié)

find_smp_config();,查找MPtable在內(nèi)存中的位置。具體協(xié)議

可以參考MP協(xié)議的第4章。

這個(gè)表的作用在于描述系統(tǒng)CPU,總線,10APIC等的硬件信息。

相關(guān)的兩個(gè)全局變量:smp_found_config是否找到SMPMP

table,mpf_foundSMPMPtable的線性地址。

smp_alloc_memory()為啟動AP的啟動程序分配內(nèi)存空間。相

關(guān)全局變量trampoline.base,分配的啟動地址的線性地址。

get_smp_config()根據(jù)MPtable中提供的內(nèi)容,獲取硬件的信

息。

init_apic_mappings();獲取10APIC和LOCALAPIC的映射地

址。

z叩」ow_mappings();如果沒有定義SMP的話,清楚用戶空間

的地址映射。將sw叩per_pg_dir中表項(xiàng)清零。

harmlessforothers

mov%cs,%ax#Codeanddatainthesameplace

mov%ax,%ds

cli#Weshouldbesafeanyway

movl$0xA5A5A5A5,trampoline_data-r_base

這個(gè)是設(shè)置標(biāo)識,以便BSP知道AP運(yùn)行到這里了。

lidtlbootjdt-r_base#loadidtwith0,0

Igdtlboot_gdt-r_base#loadgdtwithwhateveris

appropriate

加載Idt和gdt

xor%ax,%ax

inc%ax#protectedmode(PE)bit

Imsw%ax#intoprotectedmode

#flushprefetchandjumptostartup_32_smpin

arch/i386/kernel/head.S

Ijmpl$_BOOT_CSf$(startup_32_smp-_PAGE_OFFSET)

啟動保護(hù)模式,翳陣專到startup_32_smp處

#Theseneedtobeinthesame64Ksegmentastheabove;

#hencewedon'tusetheboot_gdt_descrdefinedinhead.S

boot_gdt:

.word_BOOT_DS+7#gdtlimit

Jongboot_gdt_table-_PAGE_OFFSET#gdtbase

bootjdt:

.word0#idtlimit=0

Jong0#idtbase=OL

.globltrampoline_end

trampoline_end:

在這段代碼中,設(shè)置標(biāo)識,以便BSP知道該AP已經(jīng)運(yùn)行到這段

代碼,加載GDT和LDT表基址。

然后啟動保護(hù)模式,勵(lì)陣專到startup_32_smp處。

Head.s部分代碼:

ENTRY(startup_32_smp)

cld

movl$(_BOOT_DS),%eax

movl%eax,%ds

movl%eaxz%es

movl%eaxz%fs

movl%eaxf%gs

xorl%ebxz%ebx

incl%ebx

如果是AP的話,將bx設(shè)置為1

movl$swapper_pg_dir-_PAGE_OFFSET,%eax

movl%eaxz%cr3

movl%crOz%eax

orl$0x80000000,%eax

movl%eaxf%crO

Ijmp$_BOOT_CS,$lf

啟用分頁,

Issstack_start%esp

使esp執(zhí)行fork創(chuàng)建的進(jìn)程內(nèi)核堆棧部分,以便后續(xù)跳轉(zhuǎn)到

start_secondary

#ifdefCONFIG_SMP

movbready,%cl

movb$1,ready

cmpb$0,%cl

jeIf#thefirstCPUcallsstart_kernel

#allotherCPUscallinitialize_secondary

callinitialize_secondary

jmpL6

1:

#endif

callstart_kernel

如果是AP啟動的話,就調(diào)用initialize_secondary函數(shù)。

void_devinitinitialize_secondary(void)

asmvolatile(

"movl%Oz%%esp\n\t"

"jmp*%1"

*

:"r"(current->thread.esp)/"r"(current->thread.eip));

)

設(shè)置堆棧為fork創(chuàng)建時(shí)的堆棧,ip為fork時(shí)的ip,這樣就卻桀

的了start_secondary。

start_secondary函數(shù)中處理如下:

while(!cpu_isset(smp_processorjd()/

smp_commenced_mask))

rep_nop();

進(jìn)行smp_commenced_mask判斷,是否啟動AP運(yùn)行。

smp_commenced_mask在smp_init()中設(shè)置。

cpu_idle();

如果啟動了,調(diào)用cpujdle進(jìn)行任務(wù)調(diào)度。

SMP(對稱多處理器)啟動流程--轉(zhuǎn)載

本文系本站原創(chuàng),歡迎轉(zhuǎn)載!

轉(zhuǎn)載請注明出處:

startup_32:

cld

cli

movl$(KERNEL_DS)z%eax

mov%axz%ds

mov%axz%es

mov%axz%fs

mov%axz%gs

#ifdef_SMP_

orw%bxz%bx#WhatstateareweinBX=1forSMP

#0forboot

jz2f#Initialboot

〃根據(jù)bx值指示是主cpu(bx=0)還是次cpu(bx=l)

〃然后會有不同的執(zhí)行路徑

/這里是其他次cpu執(zhí)行路徑

mov%ax,%ss

xorl%eax,%eax#Backto0

mov%cx,%ax#SPlow16bits

movl%eax/%esp

pushl0#ClearNT

popfl

Ijmp$(KERNEL_CS)Z$0x100000#IntoCandsanity

2:〃這里是主cpu的執(zhí)行路徑

#endif

IssSYMBOL_NAME(stack_start)z%esp

xorl%eax,%eax

1:incl%eax#checkthatA20reallyISenabled

movl%eax/0x000000#loopforeverifitisn't

cmpl%eax/0xl00000

jelb

pushl$0

popfl

xorl%eaxz%eax

movl$SYMBOL_NAME(_edata)z%edi

movl$SYMBOL_NAME(_end),%ecx

subl%edi,%ecx

cld

rep

stosb

subl$16z%esp#placeforstructureonthestack

pushl%esp#addressofstructureasfirstarg

callSYMBOL_NAME(decompress_kernel)

orl%eaxz%eax

jnz3f

xorl%ebxz%ebx

Ijmp$(KERNEL_CS),$0x100000

Ijmp$(KERNEL_CS),$0x100000

這個(gè)其實(shí)就是跳到start_kernel函數(shù)。

asmlinkagevoidstart_kernel(void)

(

char*commandline;

#ifdef_SMP_

staticintfirst_cpu=l;

〃這個(gè)不是函數(shù)局部變量,是函數(shù)靜態(tài)變量,主cpu執(zhí)行這個(gè)函

數(shù)時(shí)復(fù)位為1,其他cpu為0,因?yàn)橹鱟pu總是第一個(gè)執(zhí)行這個(gè)函數(shù)

的。

if(!first_cpu)

start_secondary();

〃對于

first_cpu=O;

#endif

setup_arch(&command_line,&memory_start/

&memory_end);

memory_start=paging_init(memory_start/memory_end);

trapjnit();

initJRQO;

sched_init();

time_init();

parse_options(command_line);

#ifdefCONFIG_MODULES

init_modules();

#endif

#ifdefCONFIG_PROFILE

if(!prof_shift)

#ifdefCONFIG_PROFILE_SHIFT

prof_shift=CONFIG_PROFILE_SHIFT;

#else

prof_shift=2;

#endif

#endif

if(prof_shift){

prof_buffer=(unsignedint*)memory_start;

prof_len=(unsignedlong)&_etext-(unsignedlong)&_stext;

prof_len>>=prof_shift;

memory_start+=prof_len*sizeof(unsignedint);

)

memory_start=console_init(memory_startzmemory_end);

#ifdefCONFIG_PCI

memory_start=pci_init(memory_start,memory_end);

#endif

memory_start=kmalloc_init(memory_start,memory_end);

sti();

calibrate_delay();

memory_start=inode_init(memory_start/memory_end);

memory_start=file_table_init(memory_startzmemory_end);

memory_start=

name_cachejnit(memory_start/memory_end);

#ifdefCONFIG_BLK_DEVJNITRD

if(initrd_start&&initrd_start<memory_start){

printk(KERN_CRIT"initrdoverwritten(0x%08lx<0x%08lx)-

II

"disablingit.\rT,initrd_start,memory_start);

initrd_start=0;

)

#endif

mem_init(memory_start/memory_end);

bufferJnitO;

sock_init();

#ifdefined(CONFIG_SYSVIPC)||defined(CONFIG_KERNELD)

ipc_init();

#endif

dquot_init();

arch_syms_export();

sti();

check_bugs();

printk(linux_banner);

#ifdef_SMP_

smp_init();

#endif

sysctlJnitQ;

kernel_thread(init,NULL,0);

cpu_idle(NULL);

)

asmlinkagevoidstart_secondary(void)

(

trap_init();

init」RQ();

〃初始化自己的irq

smp_callin();

〃這個(gè)等待主cpu給大家發(fā)送開始信號

cpujdle(NULL);

〃這個(gè)是ide進(jìn)程。

)

voidsmp_callin(void)

(

externvoidcalibrate_delay(void);

intcpuid=GET_APIC_ID(apic_read(APICJD));

unsignedlongI;

SMP_PRINTK(("CALLIN%d\n"/smp_processor_id()));

I=apic_read(APIC_SPIV);

l|=d<<8);

apic_write(APIC_SPIVJ);

sti();

calibrate_delay();

smp_store_cpu_info(cpuid);

set_bit(cpuidz(unsignedlong*)&cpu_callin_map[O]);

load_ldt(0);

local_flush_tlb();

while(!smp_commenced);

〃這個(gè)可以看成是自旋鎖,等待主cpu發(fā)smp_commenced信號

即開始信號。

if(cpu_number_map[cpuid]==-1)

while(l);

local_flush_tlb();

SMP_PRINTK((”Commenced..\n"));

load_TR(cpu_number_map[cpuid]);

)

intcpu_idle(void*unused)

(

for(;;)

idle。;

)

主cpu給各次cpu發(fā)開始信號是在init函數(shù)中調(diào)用smp.begin

函數(shù):

staticvoidsmp_begin(){

smp_threads_ready=l;

smp_commence();

〃這個(gè)會通過IPI給各個(gè)次cpu發(fā)送相關(guān)中斷來通信

)

每個(gè)cpu有一個(gè)current指針。

剛開始的時(shí)候由主cpu賦值為init.task;

在主cpu調(diào)用schedjnit賦值。

voidsched_init(void)

(

intcpu=smp_processor_id();〃這個(gè)為0,因?yàn)槭侵鱟pu才調(diào)用。

#ifndef_SMP_

current_set[cpu]=&init_task;

#else

init_cessor=cpu;

〃這個(gè)是將init.task標(biāo)志為主cpu在運(yùn)行。

for(cpu=0;cpu<NR_CPUS;cpu++)

current_set[cpu]=&init_task;

#endif

init_bh(TIMER_BH,timer_bh);

init_bh(TQUEUE_BH,tqueue_bh);

init_bh(IMMEDIATE_BH/immediate_bh);

)

同時(shí)這些還會在smp_init豐富。

staticvoidsmpjnit(void)

(

inti,j;

smp_boot_cpus();

for(i=l;i<smp_num_cpus;i++)

structtask_struct*n,*p;

j=cpu_logical_map[i];

kerneLthread^puJdle,NULL,CLONE_PID);

〃這個(gè)其實(shí)就是創(chuàng)建線程然后這個(gè)線程體現(xiàn)在task[i]±T,因?yàn)?/p>

創(chuàng)建的時(shí)候的task_struct就是從task[i]取的。

current_set[j]=task[i];

current_set[j]->processor=j;

cli();

n=task[i]->next_run;

p=task[i]->prev_run;

nr_running—;

n->prev_run=p;

p->next_run=n;

task[i]->next_run=task[i]->prev_run=task[i];

sti();

)

)

上面執(zhí)行完后就給每個(gè)cpu加了一個(gè)idle任務(wù)。

然后kernel_thread(init/NULL,0)創(chuàng)建的init任務(wù)。

〃每個(gè)cpu在時(shí)間中斷時(shí)都可能調(diào)用這個(gè)共同的函數(shù)。

asmlinkagevoidschedule(void)

(

intc;

structtask_struct*p;

structtask_struct*prev,*next;

unsignedlongtimeout=0;

intthis_cpu=smp_processor_id();

〃獲取cpu.id;

if(intr_count)

gotoscheduling_in_interrupt;

if(bh_active&bh_mask){

intr_count=1;

do_bottom_half();

intr_count=0;

)

run_task_queue(&tq_scheduler);

need_resched=0;

prev=current;

cli();

if(!prev->counter&&prev->policy==SCHED_RR){

prev->counter=prev->priority;

move_last_runqueue(prev);

)

switch(prev->state){

caseTASKJNTERRUPTIBLE:

if(prev->signal&-prev->blocked)

gotomakerunnable;

timeout=prev->timeout;

if(timeout&&(timeout<=jiffies)){

prev->timeout=0;

timeout=0;

makerunnable:

prev->state=TASK_RUNNING;

break;

)

default:

del_from_runqueue(prev);

caseTASK_RUNNING:

)

p=init_task.next_run;

〃獲取進(jìn)程雙向鏈表的一個(gè)節(jié)點(diǎn)。

sti();

#ifdef_SMP_

prev->processor=NO_PROC_ID;

#defineidle_task(task[cpu_number_map[this_cpu]])

#else

#defineidle_task(&init_task)

#endif

c=-1000;

next=idle_task;

while(p!=&init_task){

//p初始值為init_task.next_run

〃當(dāng)回到init_task時(shí)說明已經(jīng)查找為所有的了。

intweight=goodness(p/prevzthis_cpu);

if(weight>c)

c=weight,next=p;

p=p->next_run;

)

〃這個(gè)是查找所有的task,找出最合適的task來調(diào)度。

if(!c){

for_each_task(p)

p->counter=(p->counter>>1)+p->priority;

)

#ifdef_SMP__

next->processor=this_cpu;

〃將這個(gè)將要被執(zhí)行的processor標(biāo)識為這個(gè)cpu

next->last_processor=this_cpu;

#endif

#ifdef_SMP_PROF_

if(0==next->pid)

set_bit(this_cpuz&smp_idle_map);

else

cleajb什(this_cpu,&smp_idle_map);

#endif

if(prev!=next){

structtimer_listtimer;

kstat.context_swtch++;

if(timeout){

init_timer(&timer);

timer.expires=timeout;

timer.data=(unsignedlong)prev;

timer.function=process_timeout;

add_timer(&timer);

)

get_mmu_context(next);

switch_to(prev/next);

if(timeout)

del_timer(&timer);

)

return;

scheduling_in_interrupt:

printk("Aiee:schedulingininterrupt%p\n'\

_builtin_return_address(O));

)

上面需要注意的是current變量,在單核中肯定就是一個(gè)變量,在

多核中肯定是各個(gè)cpu有自己的current:

其定義如下:

#definecurrent(0+current_set[smp_processor_id()]

在smp中current是current.set數(shù)組中的一個(gè)元素,是指具體

一個(gè)cpu的當(dāng)前進(jìn)程。

從上面可以看出一個(gè)cpu是從全局task找一個(gè)task來運(yùn)行,每

個(gè)cpu有一個(gè)idle.task,這個(gè)task的編號是固定的。

所有的task可以通過init_task來找到,因?yàn)閯?chuàng)建新進(jìn)程(內(nèi)核線

程)的時(shí)候,會將新建的掛到鏈表上。

而init_task是靜態(tài)掛在這上面的。

附上task_struct:

structtask_struct{

volatilelongstate;

longcounter;

longpriority;

unsignedlongsignal;

unsignedlongblocked;

unsignedlongflags;

interrno;

longdebugreg[8];

structexec_domain*exec_domain;

structlinux_binfmt*binfmt;

structtask_struct*next_taskz*prev_task;

structtask_struct*next_run,*prev_run;

unsignedlongsaved_kernel_stack;

unsignedlongkernel_stack_page;

intexit_code,exit.signal;

unsignedlongpersonality;

intdumpablel;

intdid_exec:l;

intpid;

intpgrp;

inttty_old_pgrp;

intsession;

intleader;

intgroups[NGROUPS];

structtask_struct*p_opptrz*p_pptrz*p_cptrz*p_ysptr;

*p_osptr;

structwait_queue*wait_chldexit;

unsignedshortuid,euid,suid,fsuid;

unsignedshortgid,egid,sgidjsgid;

unsignedlongtimeout,policy,rt_priority;

unsignedlongit_real_valuezit_prof_valuezit_virt_value;

unsignedlongit_real_incrzit_prof_incr;it_virt_incr;

structtimer_listreal_timer;

longutime,stimezcutime,cstime,start_time;

unsignedlongmin_fltzmaj_fltznswap,cmin_flt,cmaj_fltz

cnswap;

intswappable:l;

unsignedlongswap_address;

unsignedlongold_maj_flt;

unsignedlongdec_flt;

unsignedlongswap_cnt;

structrlimitrlim[RLIM_NLIMITS];

unsignedshortused_math;

charcomm[16];

intlink_count;

structtty_struct*tty;

structsem_undo*semundo;

structsem_queue*semsleeping;

structdesc_struct*ldt;

structthread_structtss;

structfs_struct*fs;

structfiles_struct*files;

structmm_struct*mm;

structsignal_struct*sig;

#ifdef_SMP__

intprocessor;

intlast_processor;

intlock_depth;

#endif

);

故這個(gè)p=init_task.next_run;

p可以獲取到所有在就緒狀態(tài)的task;

#linux#smp#多核#多cpu#調(diào)度#啟動#it

3年前

SMP(對稱多處理器)的啟動流程

ThereareafewSMPrelatedmacros,likeCONFIG_SMR

CONFIG_X86_LOCAL_APICZCONFIG_X86_IO_APICZ

CONFIG_MULTIQUADandCONFIG_VISWS.Iwillignorecodethat

requiresCONFIG_MULTIQUADorCONFIG_VISWSZwhichmost

peopledon'tcare(ifnotusingIBMhigh-endmultiprocessor

serverorSGIVisualWorkstation).

BSPexecutesstart_kernel()->smp_init()->smp_bootcpus()

->do_boot_cpu()->wakeup_secondary_via_INIT()totriggerAPs.

CheckMultiProcessorSpecificationandIA-32ManualVol.3(Ch.7.

Multile-ProcessorManagement,andCh.8.Advanced

ProgrammableInterruptController)fortechnicaldetails.

8.1.Beforesmp_init()

Beforecallingsmp_init()rstart_kernel()didsomethingto

setupSMPenvironment:

start_kernel()

|—setup_arch()

||—parse_cmdline_early();//SMPlooksfor"nohtMand

"acpismp=force"

II-

||if(!memcmp(from,"noht",4)){

||disable_x86_ht=1;

||set_bit(X86_FEATURE_HTzdisabled_x86_caps);

II}

II

n

elseif(!niemcmp(from/"acpismp=force,13))

enable_acpi_smp_table=1;

|—setup_memory();//reservememoryforMP

configurationtable

|||—reserve_bootmem(PAGE_SIZEzPAGE_SIZE);

||find_smp_config();

find_intel_smp();

||smp_scan_config();

|—setflagsmp_found_config

|—setMPfloatingpointermpf_found

reserve_bootmem(mpf_found,PAGE_SIZE);

|—if(disable_x86_ht){//ifHyperThreadingfeature

disabled

||clear_bit(X86_FEATURE_HTz

&boot_cpu_data.x86_capability[0]);

||set_bit(X86_FEATURE_HTzdisabled_x86_caps);

||enable_acpi_smp_table=0;

II}

||-if(test_bit(X86_FEATURE_HT/

&boot_cpu_data.x86_capability[0]))

enable_acpi_smp_table=1;

|smp_alloc_memory();

II-

||trampoline_base=(void*)

alloc_bootmem_low_pages(PAGE_SIZE);

|get_smp_config();

|—config_acpi_tables();

||—memset(&acpi_boot_ops/0,sizeof(acpi_boot_ops));

|||—acpi_boot_ops[ACPI_APIC]=acpi_parse_madt;

II

if(enable_acpi_smp_table&&!acpi_tables_init())

have_acpi_tables=1;

|—setpic_mode

II

|—savelocalAPICaddressinmp_lapic_addr

scanforMPconfigurationtableentries,like

|MP_PROCESSORZMP_BUSZMPOAPIC,MPJNTSRC

andMP_LINTSRC.

|-trap_init();

init_apic_mappings();//setupPTEforAPIC

if(!smp_found_config&&detect_init_APIC()){

apic_phys=(unsignedlong)

alloc_bootmem_pages(PAGE_SIZE);

II叩ijphys=_pa(apic_phys);

||}else

||apic_phys=mp_lapic_addr;

set_fixmap_nocache(FIX_APIC_BASE,ijphys);

|if(boot_cpu_physical_apicid==-1U)

boot_cpu_physical_apicid=

GET_APIC」D(apic_read(APIC」D));

//mapIOAPICaddresstouncacheablelinearaddress

set_fixmap_nocache(idxzioapic_phys);

//NowwecanuselinearaddresstoaccessAPICspace.

I-init」RQ();

I|-init_ISA_irqs();

III--

IIIinit_bsp_APIC();

||init_8259A(auto_eoi=0);

|setupSMP/APICinterrupthandlers,esp.IPL

mem_init();

、

IPI(InterProcessorInterrupt),CPU-to-CPUinterruptthrough

localAPIC,isthemechanismusedbyBSPtotriggerAPs.

Beawarethat"onelocalAPICperCPUisrequired"inanMP-

compliantsystem.ProcessorsdonotshareAPIClocalunits

addressspace(physicaladdressOxFEEOOOOO-OxFEEFFFFF),but

willshareAPICI/Ounits(OxFECOOOOO-OxFECFFFFF).Both

addressspacesareuncacheable.

8.2.smp_init()

BSPcallsstart_kernel()->smp_init()->smp_boot_cpus()to

setupdatastructuresforeachCPUandactivatetherestAPs.

///////////////I///////////////////////I///////////////////////I///

////////////

staticvoid_initsmp_init(void)

(

smp_boot_cpus();

wait_init_idle=cpu_online_map;

clear_bit(current->processor,&wait_init_idle);

smp_threads_ready=l;

smp_commence(){

Dprintk("Settingcommenced=l/gogogo\n");

wmb();

atomic_set(&smp_commencedzl);

)

printk("Waitingonwait_init_idle(map=Ox%lx)\nn,

wait_init_idle);

while(wait_init_idle){

cpu_relax();//i.e."rep;nop"

barrier();

)

printk("AIIprocessorshavedoneinit_idle\n");

)

〃〃〃〃//〃〃〃/〃〃〃〃〃/〃〃〃/〃/〃〃〃〃〃〃〃〃〃〃〃〃/〃〃

////////////

void_initsmp_boot_cpus(void)

(

〃...somethingnotveryinteresting:-)

prof_counter[O..NR_CPUS-l]=0;

prof_old_multiplier[O..NR_CPUS-l]=0;

prof_multiplier[O..NR_CPUS-l]=0;

init_cpu_to_apicid(){

physical_apicid_2_cpu[0..MAX_APICID-l]=-1;

logical_apicid_2_cpu[0..MAX_APICID-l]=-1;

cpu_2_physical_apicid[0..NR_CPUS-l]=0;

cpu_2_logical_apicid[0..NR_CPUS-l]=0;

)

smp_store_cpu_info(0);

printk(nCPU%d:",0);

print_cpu_info(&cpu_data[0]);

set_bit(0,&cpu_online_map);

boot_cpu_logical_apicid=logical_smp_processor_id(){

GET_APIC_LOGICALJD(*(unsignedlong

*)(APIC_BASE+APIC_LDR));

)

map_cpu_to_boot_apicid(0/boot_cpu_apicid){

physical_apicid_2_cpu[boot_cpu_apicid]=0;

cpu_2_physical_apicid[0]=boot_cpu_apicid;

)

global_irq_holder=0;

current->processor=0;

init_idle();//willclearcorrespondingbitinwait_init_idle

smp_tune_scheduling();

//...someconditionschecked

connect_bsp_APIC();//enableAPICmodeifusedtobePIC

mode

setup_local_APIC();

if(GET_APIC_ID(apic_read(APIC_ID))!=

boot_cpu_physical_apicid)

BUG();

Dprintk("CPUpresentmap:%lx\n”,phys_cpu_present_map);

for(bit=0;bit<NR_CPUS;bit++){

apicid=cpu_present_to_apicid(bit);

if(apicid==boot_cpu_apicid)

continue;

if(!(phys_cpu_present_map&(1<<bit)))

continue;

if((max_cpus>=0)&&(max_cpus<=cpucount+1))

continue;

do_boot_cpu(apicid);

if((boot_apicid_to_cpu(apicid)==-1)&&

(phys_cpu_present_map&(1<<bit)))

printk("CPU#%dnotresponding-cannotuseit.\n"z

apicid);

)

//...SMPBogoMIPS

//...Bsteppingprocessorwarning

//...HyperThreadinghandling

setup_APIC_clocks();

if(cpu_has_tsc&&cpucount)

synchronize_tsc_bp();

smp_done:

zap_low_mappings();

)

〃〃〃〃〃/〃〃〃〃/〃/〃〃/〃〃〃〃〃〃〃/〃〃/〃/〃〃〃//〃〃/〃/

////////////

staticvoid_initdo_boot_cpu(intapicid)

cpu=++cpucount;

//1.prepare"idleprocess"taskstructfornextAP

if(fork_by_hand()<0)

panic("failedforkforCPU%d",cpu);

idle=init_task.prev_task;

if(!idle)

panic(HNoidleprocessforCPU%dn,cpu);

idle->processor=cpu;

idle->cpus_runnable=1<<cpu;〃onlyonthisAP!

map_cpu_to_boot_apicid(cpu,apicid){

physical_apicid_2_cpu[apicid]=cpu;

cpu_2_physical_apicid[cpu]=apicid;

)

idle->thread.eip=(unsignedlong)start_secondary;

del_from_runqueue(idle);

unhash_process(idle);

init_tasks[cpu]=idle;

//2.preparestackandcode(CS:IP)fornextAP

start_eip=setup_trampoline(){

memcpy(trampoline_base,trampoline_data,

trampoline_end-trampoline_data);

returnvirt_to_phys(trampoline_base);

)

nn

printk(Bootingprocessor%d/%deip%lx\n#cpu,apicid,

start_eip);

stack_start.esp=(void*)(1024+PAGE_SIZE+(char*)idle);

atomic_set(&init_deassertedz0);

Dprintk("Settingwarmresetcodeandvector.\n");

CMOS.WRITECOxa,Oxf);

local_flush_tlb();

Dprintk("l.\nn);

*((volatileunsignedshort*)TRAMPOLINE_HIGH)=

start_eip>>4;

Dprintk("2.\nn);

*((volatileunsignedshort*)TRAMPOLINE_LOW)=start_eip

&Oxf;

Dprintk("3.\nn);

//wehavesetup0:467tostart_eip(trampoline_base)

//3.kickAPtorun(APgetsCS:IPfrom0:467)

//StartingactualIPIsequence...

boot_error=wakeup_secondary_via_INIT(apicid/start_eip);

if(!boot_error){//looksOK

set_bit(cpuz&cpu_callout_map);

//bitcpuincpu_callin_mapissetbyAPinsmp_callin()

if(test_bit(cpu,&cpu_callin_map)){

print_cpu_info(&cpu_data[cpu]);

}else{

boot_error=1;

//marker0xA5setbyAPintrampoline_data()

if(*((volatileunsignedchar*)phys_to_virt(8192))

==0xA5)

printk(nStuck??\n");

else

printk("Notresponding.\nn);

)

)

if(boot_error){

unmap_cpu_to_boot_apicid(cpu,apicid);

clear_bit(cpu#&cpu_callout_map);

clear_bit(cpu,&cpu_initialized);

clear_bit(cpu,&cpu_online_map);

cpucount--;

)

*((volatileunsignedlong*)phys_to_virt(8192))=0;

)

Don'tconfusestart_secondary()withtrampoline_data().The

formerisAP"idle'processtaskstructEIPvalue,andthelatteris

thereal-modecodethatAPrunsafterBSPkicksit(using

wakeup_secondary_via_INIT()).

8.3.linux/arch/i386/kernel/trampoline.S

Thisfilecontainsthe16-bitreal-modeAPstartupcode.BSP

reservedmemoryspacetrampoline_baseinstart_kernel()->

setup_arch()->smp_alloc_memory().BeforeBSPtriggersARit

copiesthetrampolinecode,betweentrampoline_dataand

trampoline_end,totrampoline_base(indo_boot_cpu()->

setup_trampolineO).BSPsetsup0:

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論