aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/kernel/head.S44
-rw-r--r--arch/arm/kernel/smp.c107
-rw-r--r--include/asm-arm/smp.h14
3 files changed, 165 insertions, 0 deletions
diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S
index 4733877296d4..bd4823c74645 100644
--- a/arch/arm/kernel/head.S
+++ b/arch/arm/kernel/head.S
@@ -2,6 +2,8 @@
2 * linux/arch/arm/kernel/head.S 2 * linux/arch/arm/kernel/head.S
3 * 3 *
4 * Copyright (C) 1994-2002 Russell King 4 * Copyright (C) 1994-2002 Russell King
5 * Copyright (c) 2003 ARM Limited
6 * All Rights Reserved
5 * 7 *
6 * This program is free software; you can redistribute it and/or modify 8 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as 9 * it under the terms of the GNU General Public License version 2 as
@@ -165,6 +167,48 @@ __mmap_switched:
165 stmia r6, {r0, r4} @ Save control register values 167 stmia r6, {r0, r4} @ Save control register values
166 b start_kernel 168 b start_kernel
167 169
170#if defined(CONFIG_SMP)
171 .type secondary_startup, #function
172ENTRY(secondary_startup)
173 /*
174 * Common entry point for secondary CPUs.
175 *
176 * Ensure that we're in SVC mode, and IRQs are disabled. Lookup
177 * the processor type - there is no need to check the machine type
178 * as it has already been validated by the primary processor.
179 */
180 msr cpsr_c, #PSR_F_BIT | PSR_I_BIT | MODE_SVC
181 bl __lookup_processor_type
182 movs r10, r5 @ invalid processor?
183 moveq r0, #'p' @ yes, error 'p'
184 beq __error
185
186 /*
187 * Use the page tables supplied from __cpu_up.
188 */
189 adr r4, __secondary_data
190 ldmia r4, {r5, r6, r13} @ address to jump to after
191 sub r4, r4, r5 @ mmu has been enabled
192 ldr r4, [r6, r4] @ get secondary_data.pgdir
193 adr lr, __enable_mmu @ return address
194 add pc, r10, #12 @ initialise processor
195 @ (return control reg)
196
197 /*
198 * r6 = &secondary_data
199 */
200ENTRY(__secondary_switched)
201 ldr sp, [r6, #4] @ get secondary_data.stack
202 mov fp, #0
203 b secondary_start_kernel
204
205 .type __secondary_data, %object
206__secondary_data:
207 .long .
208 .long secondary_data
209 .long __secondary_switched
210#endif /* defined(CONFIG_SMP) */
211
168 212
169 213
170/* 214/*
diff --git a/arch/arm/kernel/smp.c b/arch/arm/kernel/smp.c
index ecc8c3332408..45ed036336e0 100644
--- a/arch/arm/kernel/smp.c
+++ b/arch/arm/kernel/smp.c
@@ -24,6 +24,9 @@
24#include <asm/atomic.h> 24#include <asm/atomic.h>
25#include <asm/cacheflush.h> 25#include <asm/cacheflush.h>
26#include <asm/cpu.h> 26#include <asm/cpu.h>
27#include <asm/mmu_context.h>
28#include <asm/pgtable.h>
29#include <asm/pgalloc.h>
27#include <asm/processor.h> 30#include <asm/processor.h>
28#include <asm/tlbflush.h> 31#include <asm/tlbflush.h>
29#include <asm/ptrace.h> 32#include <asm/ptrace.h>
@@ -37,6 +40,13 @@ cpumask_t cpu_present_mask;
37cpumask_t cpu_online_map; 40cpumask_t cpu_online_map;
38 41
39/* 42/*
43 * as from 2.5, kernels no longer have an init_tasks structure
44 * so we need some other way of telling a new secondary core
45 * where to place its SVC stack
46 */
47struct secondary_data secondary_data;
48
49/*
40 * structures for inter-processor calls 50 * structures for inter-processor calls
41 * - A collection of single bit ipi messages. 51 * - A collection of single bit ipi messages.
42 */ 52 */
@@ -71,6 +81,8 @@ static DEFINE_SPINLOCK(smp_call_function_lock);
71int __init __cpu_up(unsigned int cpu) 81int __init __cpu_up(unsigned int cpu)
72{ 82{
73 struct task_struct *idle; 83 struct task_struct *idle;
84 pgd_t *pgd;
85 pmd_t *pmd;
74 int ret; 86 int ret;
75 87
76 /* 88 /*
@@ -84,9 +96,54 @@ int __init __cpu_up(unsigned int cpu)
84 } 96 }
85 97
86 /* 98 /*
99 * Allocate initial page tables to allow the new CPU to
100 * enable the MMU safely. This essentially means a set
101 * of our "standard" page tables, with the addition of
102 * a 1:1 mapping for the physical address of the kernel.
103 */
104 pgd = pgd_alloc(&init_mm);
105 pmd = pmd_offset(pgd, PHYS_OFFSET);
106 *pmd = __pmd((PHYS_OFFSET & PGDIR_MASK) |
107 PMD_TYPE_SECT | PMD_SECT_AP_WRITE);
108
109 /*
110 * We need to tell the secondary core where to find
111 * its stack and the page tables.
112 */
113 secondary_data.stack = (void *)idle->thread_info + THREAD_SIZE - 8;
114 secondary_data.pgdir = virt_to_phys(pgd);
115 wmb();
116
117 /*
87 * Now bring the CPU into our world. 118 * Now bring the CPU into our world.
88 */ 119 */
89 ret = boot_secondary(cpu, idle); 120 ret = boot_secondary(cpu, idle);
121 if (ret == 0) {
122 unsigned long timeout;
123
124 /*
125 * CPU was successfully started, wait for it
126 * to come online or time out.
127 */
128 timeout = jiffies + HZ;
129 while (time_before(jiffies, timeout)) {
130 if (cpu_online(cpu))
131 break;
132
133 udelay(10);
134 barrier();
135 }
136
137 if (!cpu_online(cpu))
138 ret = -EIO;
139 }
140
141 secondary_data.stack = 0;
142 secondary_data.pgdir = 0;
143
144 *pmd_offset(pgd, PHYS_OFFSET) = __pmd(0);
145 pgd_free(pgd);
146
90 if (ret) { 147 if (ret) {
91 printk(KERN_CRIT "cpu_up: processor %d failed to boot\n", cpu); 148 printk(KERN_CRIT "cpu_up: processor %d failed to boot\n", cpu);
92 /* 149 /*
@@ -98,6 +155,56 @@ int __init __cpu_up(unsigned int cpu)
98} 155}
99 156
100/* 157/*
158 * This is the secondary CPU boot entry. We're using this CPUs
159 * idle thread stack, but a set of temporary page tables.
160 */
161asmlinkage void __init secondary_start_kernel(void)
162{
163 struct mm_struct *mm = &init_mm;
164 unsigned int cpu = smp_processor_id();
165
166 printk("CPU%u: Booted secondary processor\n", cpu);
167
168 /*
169 * All kernel threads share the same mm context; grab a
170 * reference and switch to it.
171 */
172 atomic_inc(&mm->mm_users);
173 atomic_inc(&mm->mm_count);
174 current->active_mm = mm;
175 cpu_set(cpu, mm->cpu_vm_mask);
176 cpu_switch_mm(mm->pgd, mm);
177 enter_lazy_tlb(mm, current);
178
179 cpu_init();
180
181 /*
182 * Give the platform a chance to do its own initialisation.
183 */
184 platform_secondary_init(cpu);
185
186 /*
187 * Enable local interrupts.
188 */
189 local_irq_enable();
190 local_fiq_enable();
191
192 calibrate_delay();
193
194 smp_store_cpu_info(cpu);
195
196 /*
197 * OK, now it's safe to let the boot CPU continue
198 */
199 cpu_set(cpu, cpu_online_map);
200
201 /*
202 * OK, it's off to the idle thread for us
203 */
204 cpu_idle();
205}
206
207/*
101 * Called by both boot and secondaries to move global data into 208 * Called by both boot and secondaries to move global data into
102 * per-processor storage. 209 * per-processor storage.
103 */ 210 */
diff --git a/include/asm-arm/smp.h b/include/asm-arm/smp.h
index f21fd8f6bcdd..bd44f894690f 100644
--- a/include/asm-arm/smp.h
+++ b/include/asm-arm/smp.h
@@ -55,4 +55,18 @@ extern void smp_cross_call(cpumask_t callmap);
55 */ 55 */
56extern int boot_secondary(unsigned int cpu, struct task_struct *); 56extern int boot_secondary(unsigned int cpu, struct task_struct *);
57 57
58/*
59 * Perform platform specific initialisation of the specified CPU.
60 */
61extern void platform_secondary_init(unsigned int cpu);
62
63/*
64 * Initial data for bringing up a secondary CPU.
65 */
66struct secondary_data {
67 unsigned long pgdir;
68 void *stack;
69};
70extern struct secondary_data secondary_data;
71
58#endif /* ifndef __ASM_ARM_SMP_H */ 72#endif /* ifndef __ASM_ARM_SMP_H */