您的位置:首页 > 大数据 > 人工智能

SMP多核处理器omap4启动分析 笔记版 转载请注明出处---crosskernel@gmail.com

2014-03-13 14:25 232 查看
Omap4 是Ti在移动市场上的绝唱。在没有通信modem的支援下,依赖于omap4,Ti硬是在手机处理器市场赢得最后一站。

Omap4是双核Cortex A9架构的处理器,本文分析其启动表现。

代码执行顺序如黑体:

static int __init kernel_init(void * unused)

{



smp_prepare_cpus(setup_max_cpus);



}

void __init smp_prepare_cpus(unsigned int max_cpus)

{



scu_enable(scu_base);

wakeup_secondary();

}

}

static void __init wakeup_secondary(void)

{

//cpu0 将omap_secondary_startup的物理地址写入omap4的AuxCoreBoot 1寄存器

//这时cpu1执行rom指令WFE处在等待状态

omap_auxcoreboot_addr(virt_to_phys(omap_secondary_startup));

smp_wmb();

dsb();

//cpu0 发出sev指令,sev指令激活cpu1,使之推出的WFE状态,cpu1继续执行rom的指令,其启动顺寻是:1查看AuxCoreBoot 0寄存器的状态是否满足要求 2 若AuxCoreBoot 0寄存器状态满足要求,则取出AuxCoreBoot 1寄存器的地址,跳转之

set_event();

mb();

}

//在omap4的芯片内部存在着一个代码rom,其代码运行在arm架构monitor状态。这段代码只使用master cpu及cpu0里的资源。这样就可以避免寻址的问题,cpu可以不管mmu是否打开关闭,都能访问这个rom。

//cpu0 将物理地址写入omap4的AuxCoreBoot 1寄存器

ENTRY(omap_auxcoreboot_addr)

//这是在cpu0执行的代码,而rom代码也运行在cpu0上,所以要进行寄存器的保存。

stmfd sp!, {r2-r12, lr}

//0x105是rom代码的调用代号,通过r12传递。

ldr r12, =0x105

Dsb

//smc是进入monitor状态的专用指令,一旦执行这个指令cpu就像发生了一次异常一样,进入monitor状态

smc #0

//rom代码完成服务工作,cpu0被恢复原来状态,显然rom代码尽量精简,留下了寄存器恢复的工作。这里回复寄存器状态。

ldmfd sp!, {r2-r12, pc}

END(omap_auxcoreboot_addr)

执行到这里,cpu1被激活了,但是他还不能往前跑,因为这个时候内核还没有为其准备好相关管理结构,这时cpu1检查AuxCoreBoot 0寄存器状态,如果还没有满足要求,cpu1执行WFE仍旧进入WFE状态。cpu0要在完成所有准备工作之后才会改写AuxCoreBoot 0寄存器,释放cpu1。

Cpu0在调用void __init smp_prepare_cpus(setup_max_cpus)叫醒cpu1之后,会继续执行一些内核初始化工作,以及smp相关的初始化工作,其中最重要的是通过调用static void __init smp_init(void)->...->int __cpuinit __cpu_up(unsigned int cpu)

int __cpuinit __cpu_up(unsigned int cpu)

{

...

//首先为cpu1 fork出一个idle线程

/*

* Spawn a new process manually, if not already done.

* Grab a pointer to its task struct so we can mess with it

*/

if (!idle) {

idle = fork_idle(cpu);

if (IS_ERR(idle)) {

printk(KERN_ERR "CPU%u: fork() failed\n", cpu);

return PTR_ERR(idle);

}

ci->idle = idle;

} else {

/*

* Since this idle thread is being re-used, call

* init_idle() to reinitialize the thread structure.

*/

init_idle(idle, cpu);

}

/*

* Allocate initial page tables to allow the new CPU to

* enable the MMU safely. This essentially means a set

* of our "standard" page tables, with the addition of

* a 1:1 mapping for the physical address of the kernel.

*/

//为cpu1创建一个struct mm_struct

pgd = pgd_alloc(&init_mm);

pmd = pmd_offset(pgd + pgd_index(PHYS_OFFSET), PHYS_OFFSET);

*pmd = __pmd((PHYS_OFFSET & PGDIR_MASK) |

PMD_TYPE_SECT | PMD_SECT_AP_WRITE);

flush_pmd_entry(pmd);

outer_clean_range(__pa(pmd), __pa(pmd + 1));

/*

* We need to tell the secondary core where to find

* its stack and the page tables.

*/

secondary_data.stack = task_stack_page(idle) + THREAD_START_SP;

secondary_data.pgdir = virt_to_phys(pgd);

__cpuc_flush_dcache_area(&secondary_data, sizeof(secondary_data));

outer_clean_range(__pa(&secondary_data), __pa(&secondary_data + 1));

/*

* Now bring the CPU into our world.

*/

//叫醒cpu1,这里cpu1会真正运行起来

ret = boot_secondary(cpu, idle);

//到了这里,完成了cpu1的激活工作,cpu1将执行自己相关到初始化工作,等到cpu1完成自己的工作,cpu1会在cpu_online_mask位图上将自己对应的标志位置位,而cpu0在这里等待那一刻的到来

if (ret == 0) {

unsigned long timeout;

/*

* CPU was successfully started, wait for it

* to come online or time out.

*/

timeout = jiffies + HZ;

while (time_before(jiffies, timeout)) {

//如果对应位有效,说明cpu1完成了初始化工作,真正成为了系统中的一颗对称多处理器

if (cpu_online(cpu))

break;

//代码跑到这里说明,cpu1工作还没完成,再等一会。

udelay(10);

barrier();

}

if (!cpu_online(cpu))

ret = -EIO;

}

......

}

int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)

{

struct clockdomain *cpu1_clkdm;

static bool booted;

/*

* Set synchronisation state between this boot processor

* and the secondary one

*/

spin_lock(&boot_lock);

/*

* Update the AuxCoreBoot0 with boot state for secondary core.

* omap_secondary_startup() routine will hold the secondary core till

* the AuxCoreBoot1 register is updated with cpu state

* A barrier is added to ensure that write buffer is drained

*/

//这里cpu0改写AuxCoreBoot0的状态,告诉cpu1可以跑了

omap_modify_auxcoreboot0(0x200, 0xfffffdff);

flush_cache_all();

smp_wmb();

/*

* SGI isn't wakeup capable from low power states. This is

* known limitation and can be worked around by using software

* forced wake-up. After the wakeup, the CPU will restore it

* to hw_auto. This code also gets initialised but pm init code

* initialises the CPUx clockdomain to hw-auto mode

*/

if (booted) {

cpu1_clkdm = clkdm_lookup("mpu1_clkdm");

omap2_clkdm_wakeup(cpu1_clkdm);

smp_cross_call(cpumask_of(cpu));

} else {

//第一次启动会跑到这里,因为上次wakeup_secondary函数虽然用SEV指令激活的cpu1,但是cpu1发现AuxCoreBoot0的状态不满足,又进入WFE状态了。所以这里要在捅一次cpu1。

set_event();

booted = true;

}

/*

* Now the secondary core is starting up let it run its

* calibrations, then wait for it to finish

*/

spin_unlock(&boot_lock);

return 0;

}

//omap-headsmp.s

ENTRY(omap_secondary_startup)

//cpu1终于跑出了rom,第一件事是再检查一下AuxCoreBoot0,如果不满足要求就hold下来。在这之前rom里已经检查了AuxCoreBoot0,并且把ENTRY(omap_secondary_startup)地址从AuxCoreBoot0取出来。

hold: ldr
r12,=0x103

dsb

smc #0@ read from AuxCoreBoot0

mov r0, r0, lsr #9

mrc p15, 0, r4, c0, c0, 5

and r4, r4, #0x0f

cmp r0, r4

bne hold

/*

* we've been released from the wait loop,secondary_stack

* should now contain the SVC stack for this core

*/

b secondary_startup

END(omap_secondary_startup)

ENTRY(omap_modify_auxcoreboot0)

stmfd sp!, {r1-r12, lr}

ldr r12, =0x104

dsb

smc #0

ldmfd sp!, {r1-r12, pc}

END(omap_modify_auxcoreboot0)

Head.s

#if defined(CONFIG_SMP)

ENTRY(secondary_startup)

/*

* Common entry point for secondary CPUs.

*

* Ensure that we're in SVC mode, and IRQs are disabled. Lookup

* the processor type - there is no need to check the machine type

* as it has already been validated by the primary processor.

*/

setmode
PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9

mrc p15, 0, r9, c0, c0@ get processor id

bl __lookup_processor_type

movs r10, r5@ invalid processor?

moveq r0, #'p'@ yes, error 'p'

beq __error

/*

* Use the page tables supplied from __cpu_up.

*/

adr r4, __secondary_data

ldmia r4, {r5, r7, r12}@ address to jump to after

sub r4, r4, r5@ mmu has been enabled

ldr r4, [r7, r4]@ get secondary_data.pgdir

adr lr, BSYM(__enable_mmu)@ return address

mov r13, r12@ __secondary_switched address

ARM( add
pc, r10, #PROCINFO_INITFUNC ) @ initialise processor

@ (return control reg)

THUMB( add
r12, r10, #PROCINFO_INITFUNC )

THUMB( mov
pc, r12 )

ENDPROC(secondary_startup)

ENTRY(__secondary_switched)

ldr sp, [r7, #4]@ get secondary_data.stack

mov fp, #0

b secondary_start_kernel

ENDPROC(__secondary_switched)

asmlinkage void __cpuinit secondary_start_kernel(void)

{

struct mm_struct *mm = &init_mm;

unsigned int cpu = smp_processor_id();

printk("CPU%u: Booted secondary processor\n", cpu);

/*

* All kernel threads share the same mm context; grab a

* reference and switch to it.

*/

atomic_inc(&mm->mm_users);

atomic_inc(&mm->mm_count);

current->active_mm = mm;

cpumask_set_cpu(cpu, mm_cpumask(mm));

cpu_switch_mm(mm->pgd, mm);

enter_lazy_tlb(mm, current);

local_flush_tlb_all();

cpu_init();

preempt_disable();

/*

* Give the platform a chance to do its own initialisation.

*/

platform_secondary_init(cpu);

/*

* Enable local interrupts.

*/

notify_cpu_starting(cpu);

local_irq_enable();

local_fiq_enable();

/*

* Setup the percpu timer for this CPU.

*/

percpu_timer_setup();

calibrate_delay();

smp_store_cpu_info(cpu);

/*

* OK, now it's safe to let the boot CPU continue

*/

set_cpu_online(cpu, true);

/*

* OK, it's off to the idle thread for us

*/

cpu_idle();

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐