linuxSMP启动过程学习笔记_第1页
linuxSMP启动过程学习笔记_第2页
linuxSMP启动过程学习笔记_第3页
linuxSMP启动过程学习笔记_第4页
linuxSMP启动过程学习笔记_第5页
已阅读5页,还剩31页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

linuxSMP启动过程学习笔记

1.SMP硬件体系结构:

对于SMP最简单可以理解为系统存在多个完全相同的CPU,所

有CPU共享总线,拥有自己的寄存器。对于内存和外部设备访问,由

于共享总线,所以是共享的。Linux操作系统多个CPU共享在系统空

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

由于系统中存在多个CPU,这是就引入一个问题,当外部设备产

生中断的时候,具体有哪一个CPU进行处理?

为此,intel公司提出了10APCI和LOCALAPCI的体系结构。

10APIC连接各个外部设备,并可以设置分发类型,根据设定的

分发类型,中断信号发送的对应CPU的LOCALAPIC上。

LOCALAPIC负责本地CPU的中断处理,LOCALAPIC不仅可以

接受10APIC的中断,也需要处理本地CPU产生的异常。同时

LOCALAPIC还提供了一个定时器。

如何确定那个CPU是引导CPU?

根据intel公司中的资料,系统上电后,会根据MPInitialization

Protocol随机选择一个CPU作为BSP,只有BSP会运行BIOS程序,

其他AP都进入等待状态,BSP发送IPI中断触发后才可以运行。具

体的MPInitializationProtocol细节,可以参考Intel?64andIA-32

ArchitecturesSoftwareDeveloper'sManualVolume3A:System

ProgrammingGuide,Part1第8章。

引导CPU如何控制其他CPU开始运行?

BSP可以通过IPI消息控制AP从指定的起始地址运行。CPU中

集成的LOCALAPIC提供了这个功能。可以通过写LOCALAPIC中提

供的相关寄存器,发送IPI消息到指定的CPU上。

如何获取系统硬件CPU信息的?

在系统初始化后,硬件会在内存的规定位置提供关于CPU,总线,

等的信息,即在初始化的过程,会

10APICSMPMPtable0linux

读取该位置,获取系统相关的硬件信息。

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的引导代码。

get_smp_config();//根据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();

针对每个调用

CPUdo_boot_cpu(apicidzcpu)

smpJnitQ;//每个CPU开始进行调度

trampoline.SAP引导代码,为16进制代码,启用保护模式

head.s为AP创建分页管理

initialize_secondary根据之前fork创建设置的信息,跳转到

start_secondary处

start-secondary判断BSP是否启动,如果启动AP进行任务调

度。

3.代码学习总结

find_smp_config();,查找MPtable在内存中的位置。具体协议

可以参考MP协议的第4章。

这个表的作用在于描述系统CPU,总线,10APIC等的硬件信息。

相关的两个全局变量:smp_found_config是否找到SMPMP

table,mpf_foundSMPMPtable的线性地址。

smp_alloc_memory()为启动AP的启动程序分配内存空间。相

关全局变量trampoline.base,分配的启动地址的线性地址。

get_smp_config()根据MPtable中提供的内容,获取硬件的信

息。

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

址。

z叩」ow_mappings();如果没有定义SMP的话,清楚用户空间

的地址映射。将sw叩per_pg_dir中表项清零。

harmlessforothers

mov%cs,%ax#Codeanddatainthesameplace

mov%ax,%ds

cli#Weshouldbesafeanyway

movl$0xA5A5A5A5,trampoline_data-r_base

这个是设置标识,以便BSP知道AP运行到这里了。

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)

启动保护模式,翳阵专到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:

在这段代码中,设置标识,以便BSP知道该AP已经运行到这段

代码,加载GDT和LDT表基址。

然后启动保护模式,励阵专到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设置为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执行fork创建的进程内核堆栈部分,以便后续跳转到

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启动的话,就调用initialize_secondary函数。

void_devinitinitialize_secondary(void)

asmvolatile(

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

"jmp*%1"

*

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

)

设置堆栈为fork创建时的堆栈,ip为fork时的ip,这样就却桀

的了start_secondary。

start_secondary函数中处理如下:

while(!cpu_isset(smp_processorjd()/

smp_commenced_mask))

rep_nop();

进行smp_commenced_mask判断,是否启动AP运行。

smp_commenced_mask在smp_init()中设置。

cpu_idle();

如果启动了,调用cpujdle进行任务调度。

SMP(对称多处理器)启动流程--转载

本文系本站原创,欢迎转载!

转载请注明出处:

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

〃根据bx值指示是主cpu(bx=0)还是次cpu(bx=l)

〃然后会有不同的执行路径

/这里是其他次cpu执行路径

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的执行路径

#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

这个其实就是跳到start_kernel函数。

asmlinkagevoidstart_kernel(void)

(

char*commandline;

#ifdef_SMP_

staticintfirst_cpu=l;

〃这个不是函数局部变量,是函数静态变量,主cpu执行这个函

数时复位为1,其他cpu为0,因为主cpu总是第一个执行这个函数

的。

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

〃这个等待主cpu给大家发送开始信号

cpujdle(NULL);

〃这个是ide进程。

)

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

〃这个可以看成是自旋锁,等待主cpu发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发开始信号是在init函数中调用smp.begin

函数:

staticvoidsmp_begin(){

smp_threads_ready=l;

smp_commence();

〃这个会通过IPI给各个次cpu发送相关中断来通信

)

每个cpu有一个current指针。

刚开始的时候由主cpu赋值为init.task;

在主cpu调用schedjnit赋值。

voidsched_init(void)

(

intcpu=smp_processor_id();〃这个为0,因为是主cpu才调用。

#ifndef_SMP_

current_set[cpu]=&init_task;

#else

init_cessor=cpu;

〃这个是将init.task标志为主cpu在运行。

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

)

同时这些还会在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);

〃这个其实就是创建线程然后这个线程体现在task[i]±T,因为

创建的时候的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();

)

)

上面执行完后就给每个cpu加了一个idle任务。

然后kernel_thread(init/NULL,0)创建的init任务。

〃每个cpu在时间中断时都可能调用这个共同的函数。

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;

〃获取进程双向链表的一个节点。

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

〃当回到init_task时说明已经查找为所有的了。

intweight=goodness(p/prevzthis_cpu);

if(weight>c)

c=weight,next=p;

p=p->next_run;

)

〃这个是查找所有的task,找出最合适的task来调度。

if(!c){

for_each_task(p)

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

)

#ifdef_SMP__

next->processor=this_cpu;

〃将这个将要被执行的processor标识为这个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变量,在单核中肯定就是一个变量,在

多核中肯定是各个cpu有自己的current:

其定义如下:

#definecurrent(0+current_set[smp_processor_id()]

在smp中current是current.set数组中的一个元素,是指具体

一个cpu的当前进程。

从上面可以看出一个cpu是从全局task找一个task来运行,每

个cpu有一个idle.task,这个task的编号是固定的。

所有的task可以通过init_task来找到,因为创建新进程(内核线

程)的时候,会将新建的挂到链表上。

而init_task是静态挂在这上面的。

附上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

);

故这个p=init_task.next_run;

p可以获取到所有在就绪状态的task;

#linux#smp#多核#多cpu#调度#启动#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. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论