Index: arch/arm/kernel/head.S =================================================================== --- d81a8e028cccd80a52fbe9b6386e2751949b978f/arch/arm/kernel/head.S (mode:100644) +++ c553955752fd769e8b71d50ed0ed32324d9fd73f/arch/arm/kernel/head.S (mode:100644) @@ -2,6 +2,8 @@ * linux/arch/arm/kernel/head.S * * Copyright (C) 1994-2002 Russell King + * Copyright (c) 2003 ARM Limited + * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -165,6 +167,48 @@ stmia r6, {r0, r4} @ Save control register values b start_kernel +#if defined(CONFIG_SMP) + .type secondary_startup, #function +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. + */ + msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | MODE_SVC + 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, r6, r13} @ address to jump to after + sub r4, r4, r5 @ mmu has been enabled + ldr r4, [r6, r4] @ get secondary_data.pgdir + adr lr, __enable_mmu @ return address + add pc, r10, #12 @ initialise processor + @ (return control reg) + + /* + * r6 = &secondary_data + */ +ENTRY(__secondary_switched) + ldr sp, [r6, #4] @ get secondary_data.stack + mov fp, #0 + b secondary_start_kernel + + .type __secondary_data, %object +__secondary_data: + .long . + .long secondary_data + .long __secondary_switched +#endif /* defined(CONFIG_SMP) */ + /* Index: arch/arm/kernel/smp.c =================================================================== --- d81a8e028cccd80a52fbe9b6386e2751949b978f/arch/arm/kernel/smp.c (mode:100644) +++ c553955752fd769e8b71d50ed0ed32324d9fd73f/arch/arm/kernel/smp.c (mode:100644) @@ -24,6 +24,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -37,6 +40,13 @@ cpumask_t cpu_online_map; /* + * as from 2.5, kernels no longer have an init_tasks structure + * so we need some other way of telling a new secondary core + * where to place its SVC stack + */ +struct secondary_data secondary_data; + +/* * structures for inter-processor calls * - A collection of single bit ipi messages. */ @@ -71,6 +81,8 @@ int __init __cpu_up(unsigned int cpu) { struct task_struct *idle; + pgd_t *pgd; + pmd_t *pmd; int ret; /* @@ -84,9 +96,54 @@ } /* + * 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. + */ + pgd = pgd_alloc(&init_mm); + pmd = pmd_offset(pgd, PHYS_OFFSET); + *pmd = __pmd((PHYS_OFFSET & PGDIR_MASK) | + PMD_TYPE_SECT | PMD_SECT_AP_WRITE); + + /* + * We need to tell the secondary core where to find + * its stack and the page tables. + */ + secondary_data.stack = (void *)idle->thread_info + THREAD_SIZE - 8; + secondary_data.pgdir = virt_to_phys(pgd); + wmb(); + + /* * Now bring the CPU into our world. */ ret = boot_secondary(cpu, idle); + 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)) { + if (cpu_online(cpu)) + break; + + udelay(10); + barrier(); + } + + if (!cpu_online(cpu)) + ret = -EIO; + } + + secondary_data.stack = 0; + secondary_data.pgdir = 0; + + *pmd_offset(pgd, PHYS_OFFSET) = __pmd(0); + pgd_free(pgd); + if (ret) { printk(KERN_CRIT "cpu_up: processor %d failed to boot\n", cpu); /* @@ -98,6 +155,56 @@ } /* + * This is the secondary CPU boot entry. We're using this CPUs + * idle thread stack, but a set of temporary page tables. + */ +asmlinkage void __init 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; + cpu_set(cpu, mm->cpu_vm_mask); + cpu_switch_mm(mm->pgd, mm); + enter_lazy_tlb(mm, current); + + cpu_init(); + + /* + * Give the platform a chance to do its own initialisation. + */ + platform_secondary_init(cpu); + + /* + * Enable local interrupts. + */ + local_irq_enable(); + local_fiq_enable(); + + calibrate_delay(); + + smp_store_cpu_info(cpu); + + /* + * OK, now it's safe to let the boot CPU continue + */ + cpu_set(cpu, cpu_online_map); + + /* + * OK, it's off to the idle thread for us + */ + cpu_idle(); +} + +/* * Called by both boot and secondaries to move global data into * per-processor storage. */ Index: arch/arm/mach-integrator/Makefile =================================================================== --- d81a8e028cccd80a52fbe9b6386e2751949b978f/arch/arm/mach-integrator/Makefile (mode:100644) +++ c553955752fd769e8b71d50ed0ed32324d9fd73f/arch/arm/mach-integrator/Makefile (mode:100644) @@ -12,3 +12,4 @@ obj-$(CONFIG_PCI) += pci_v3.o pci.o obj-$(CONFIG_CPU_FREQ_INTEGRATOR) += cpu.o obj-$(CONFIG_INTEGRATOR_IMPD1) += impd1.o +obj-$(CONFIG_SMP) += platsmp.o headsmp.o Index: arch/arm/mach-integrator/core.c =================================================================== --- d81a8e028cccd80a52fbe9b6386e2751949b978f/arch/arm/mach-integrator/core.c (mode:100644) +++ c553955752fd769e8b71d50ed0ed32324d9fd73f/arch/arm/mach-integrator/core.c (mode:100644) @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -221,7 +222,24 @@ */ timer1->TimerClear = 1; - timer_tick(regs); + /* + * the clock tick routines are only processed on the + * primary CPU + */ + if (hard_smp_processor_id() == 0) { + nmi_tick(); + timer_tick(regs); +#ifdef CONFIG_SMP + smp_send_timer(); +#endif + } + +#ifdef CONFIG_SMP + /* + * this is the ARM equivalent of the APIC timer interrupt + */ + update_process_times(user_mode(regs)); +#endif /* CONFIG_SMP */ write_sequnlock(&xtime_lock); Index: arch/arm/mach-integrator/leds.c =================================================================== --- d81a8e028cccd80a52fbe9b6386e2751949b978f/arch/arm/mach-integrator/leds.c (mode:100644) +++ c553955752fd769e8b71d50ed0ed32324d9fd73f/arch/arm/mach-integrator/leds.c (mode:100644) @@ -22,6 +22,8 @@ */ #include #include +#include +#include #include #include @@ -85,4 +87,4 @@ return 0; } -__initcall(leds_init); +core_initcall(leds_init); Index: include/asm-arm/smp.h =================================================================== --- d81a8e028cccd80a52fbe9b6386e2751949b978f/include/asm-arm/smp.h (mode:100644) +++ c553955752fd769e8b71d50ed0ed32324d9fd73f/include/asm-arm/smp.h (mode:100644) @@ -55,4 +55,18 @@ */ extern int boot_secondary(unsigned int cpu, struct task_struct *); +/* + * Perform platform specific initialisation of the specified CPU. + */ +extern void platform_secondary_init(unsigned int cpu); + +/* + * Initial data for bringing up a secondary CPU. + */ +struct secondary_data { + unsigned long pgdir; + void *stack; +}; +extern struct secondary_data secondary_data; + #endif /* ifndef __ASM_ARM_SMP_H */