1 | /* |
---|
2 | * This program is free software; you can redistribute it and/or |
---|
3 | * modify it under the terms of the GNU General Public License as |
---|
4 | * published by the Free Software Foundation; either version 2 of the |
---|
5 | * License, or (at your option) any later version. |
---|
6 | * |
---|
7 | * This program is distributed in the hope that it will be useful, |
---|
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
10 | * GNU General Public License for more details. |
---|
11 | * |
---|
12 | * You should have received a copy of the GNU General Public License |
---|
13 | * along with this program; if not, write to the Free Software |
---|
14 | * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
---|
15 | * |
---|
16 | * Copyright IBM Corp. 2005, 2006, 2007 |
---|
17 | * |
---|
18 | * Authors: Jimi Xenidis <jimix@watson.ibm.com> |
---|
19 | * Amos Waterland <apw@us.ibm.com> |
---|
20 | * Hollis Blanchard <hollisb@us.ibm.com> |
---|
21 | */ |
---|
22 | |
---|
23 | #include <xen/config.h> |
---|
24 | #include <xen/init.h> |
---|
25 | #include <xen/lib.h> |
---|
26 | #include <xen/cpumask.h> |
---|
27 | #include <xen/sched.h> |
---|
28 | #include <xen/multiboot.h> |
---|
29 | #include <xen/serial.h> |
---|
30 | #include <xen/softirq.h> |
---|
31 | #include <xen/console.h> |
---|
32 | #include <xen/trace.h> |
---|
33 | #include <xen/mm.h> |
---|
34 | #include <xen/domain.h> |
---|
35 | #include <xen/gdbstub.h> |
---|
36 | #include <xen/symbols.h> |
---|
37 | #include <xen/keyhandler.h> |
---|
38 | #include <xen/numa.h> |
---|
39 | #include <xen/rcupdate.h> |
---|
40 | #include <xen/version.h> |
---|
41 | #include <acm/acm_hooks.h> |
---|
42 | #include <public/version.h> |
---|
43 | #include <asm/mpic.h> |
---|
44 | #include <asm/processor.h> |
---|
45 | #include <asm/desc.h> |
---|
46 | #include <asm/cache.h> |
---|
47 | #include <asm/debugger.h> |
---|
48 | #include <asm/delay.h> |
---|
49 | #include <asm/percpu.h> |
---|
50 | #include <asm/io.h> |
---|
51 | #include "exceptions.h" |
---|
52 | #include "of-devtree.h" |
---|
53 | #include "oftree.h" |
---|
54 | #include "rtas.h" |
---|
55 | |
---|
56 | #define DEBUG |
---|
57 | |
---|
58 | /* opt_noht: If true, Hyperthreading is ignored. */ |
---|
59 | int opt_noht = 0; |
---|
60 | boolean_param("noht", opt_noht); |
---|
61 | |
---|
62 | int opt_earlygdb = 0; |
---|
63 | boolean_param("earlygdb", opt_earlygdb); |
---|
64 | |
---|
65 | /* opt_nosmp: If true, secondary processors are ignored. */ |
---|
66 | static int opt_nosmp = 0; |
---|
67 | boolean_param("nosmp", opt_nosmp); |
---|
68 | |
---|
69 | /* maxcpus: maximum number of CPUs to activate. */ |
---|
70 | static unsigned int max_cpus = NR_CPUS; |
---|
71 | integer_param("maxcpus", max_cpus); |
---|
72 | |
---|
73 | u32 tlbflush_clock = 1U; |
---|
74 | DEFINE_PER_CPU(u32, tlbflush_time); |
---|
75 | |
---|
76 | unsigned int watchdog_on; |
---|
77 | unsigned long wait_init_idle; |
---|
78 | ulong oftree; |
---|
79 | ulong oftree_len; |
---|
80 | ulong oftree_end; |
---|
81 | |
---|
82 | uint cpu_hard_id[NR_CPUS] __initdata; |
---|
83 | cpumask_t cpu_present_map; |
---|
84 | |
---|
85 | /* XXX get this from ISA node in device tree */ |
---|
86 | char *vgabase; |
---|
87 | ulong isa_io_base; |
---|
88 | struct ns16550_defaults ns16550; |
---|
89 | |
---|
90 | extern char __per_cpu_start[], __per_cpu_data_end[], __per_cpu_end[]; |
---|
91 | |
---|
92 | static struct domain *idle_domain; |
---|
93 | |
---|
94 | volatile struct processor_area * volatile global_cpu_table[NR_CPUS]; |
---|
95 | |
---|
96 | static void __init do_initcalls(void) |
---|
97 | { |
---|
98 | initcall_t *call; |
---|
99 | for (call = &__initcall_start; call < &__initcall_end; call++) { |
---|
100 | (*call)(); |
---|
101 | } |
---|
102 | } |
---|
103 | |
---|
104 | |
---|
105 | void noinline __attn(void) |
---|
106 | { |
---|
107 | /* To continue the probe will step over the ATTN instruction. The |
---|
108 | * NOP is there to make sure there is something sane to "step |
---|
109 | * over" to. */ |
---|
110 | console_start_sync(); |
---|
111 | asm volatile(".long 0x200;nop"); |
---|
112 | console_end_sync(); |
---|
113 | } |
---|
114 | |
---|
115 | static void key_hw_probe_attn(unsigned char key) |
---|
116 | { |
---|
117 | __attn(); |
---|
118 | } |
---|
119 | |
---|
120 | static void key_ofdump(unsigned char key) |
---|
121 | { |
---|
122 | printk("ofdump:\n"); |
---|
123 | /* make sure the OF devtree is good */ |
---|
124 | ofd_walk((void *)oftree, "devtree", OFD_ROOT, |
---|
125 | ofd_dump_props, OFD_DUMP_ALL); |
---|
126 | } |
---|
127 | |
---|
128 | static void percpu_init_areas(void) |
---|
129 | { |
---|
130 | unsigned int i, data_size = __per_cpu_data_end - __per_cpu_start; |
---|
131 | |
---|
132 | BUG_ON(data_size > PERCPU_SIZE); |
---|
133 | |
---|
134 | for ( i = 1; i < NR_CPUS; i++ ) |
---|
135 | memcpy(__per_cpu_start + (i << PERCPU_SHIFT), |
---|
136 | __per_cpu_start, |
---|
137 | data_size); |
---|
138 | } |
---|
139 | |
---|
140 | static void percpu_free_unused_areas(void) |
---|
141 | { |
---|
142 | unsigned int i, first_unused; |
---|
143 | |
---|
144 | /* Find first unused CPU number. */ |
---|
145 | for ( i = 0; i < NR_CPUS; i++ ) |
---|
146 | if ( !cpu_online(i) ) |
---|
147 | break; |
---|
148 | first_unused = i; |
---|
149 | |
---|
150 | /* Check that there are no holes in cpu_online_map. */ |
---|
151 | for ( ; i < NR_CPUS; i++ ) |
---|
152 | BUG_ON(cpu_online(i)); |
---|
153 | |
---|
154 | init_xenheap_pages((ulong)__per_cpu_start + (first_unused << PERCPU_SHIFT), |
---|
155 | (ulong)__per_cpu_end); |
---|
156 | } |
---|
157 | |
---|
158 | static void __init start_of_day(void) |
---|
159 | { |
---|
160 | init_IRQ(); |
---|
161 | |
---|
162 | scheduler_init(); |
---|
163 | |
---|
164 | /* create idle domain */ |
---|
165 | idle_domain = domain_create(IDLE_DOMAIN_ID, 0, 0); |
---|
166 | if ((idle_domain == NULL) || (alloc_vcpu(idle_domain, 0, 0) == NULL)) |
---|
167 | BUG(); |
---|
168 | set_current(idle_domain->vcpu[0]); |
---|
169 | idle_vcpu[0] = current; |
---|
170 | |
---|
171 | initialize_keytable(); |
---|
172 | /* Register another key that will allow for the the Harware Probe |
---|
173 | * to be contacted, this works with RiscWatch probes and should |
---|
174 | * work with Chronos and FSPs */ |
---|
175 | register_keyhandler('^', key_hw_probe_attn, "Trap to Hardware Probe"); |
---|
176 | |
---|
177 | /* allow the dumping of the devtree */ |
---|
178 | register_keyhandler('D', key_ofdump , "Dump OF Devtree"); |
---|
179 | |
---|
180 | timer_init(); |
---|
181 | rcu_init(); |
---|
182 | serial_init_postirq(); |
---|
183 | do_initcalls(); |
---|
184 | } |
---|
185 | |
---|
186 | void startup_cpu_idle_loop(void) |
---|
187 | { |
---|
188 | struct vcpu *v = current; |
---|
189 | |
---|
190 | ASSERT(is_idle_vcpu(v)); |
---|
191 | cpu_set(smp_processor_id(), v->domain->domain_dirty_cpumask); |
---|
192 | cpu_set(smp_processor_id(), v->vcpu_dirty_cpumask); |
---|
193 | |
---|
194 | /* Finally get off the boot stack. */ |
---|
195 | reset_stack_and_jump(idle_loop); |
---|
196 | } |
---|
197 | |
---|
198 | /* The boot_pa is enough "parea" for the boot CPU to get thru |
---|
199 | * initialization, it will ultimately get replaced later */ |
---|
200 | static __init void init_boot_cpu(void) |
---|
201 | { |
---|
202 | static struct processor_area boot_pa; |
---|
203 | boot_pa.whoami = 0; |
---|
204 | parea = &boot_pa; |
---|
205 | } |
---|
206 | |
---|
207 | static void init_parea(int cpuid) |
---|
208 | { |
---|
209 | /* Be careful not to shadow the global variable. */ |
---|
210 | volatile struct processor_area *pa; |
---|
211 | void *stack; |
---|
212 | |
---|
213 | pa = xmalloc(struct processor_area); |
---|
214 | if (pa == NULL) |
---|
215 | panic("%s: failed to allocate parea for cpu #%d\n", __func__, cpuid); |
---|
216 | |
---|
217 | stack = alloc_xenheap_pages(STACK_ORDER); |
---|
218 | if (stack == NULL) |
---|
219 | panic("%s: failed to allocate stack (order %d) for cpu #%d\n", |
---|
220 | __func__, STACK_ORDER, cpuid); |
---|
221 | |
---|
222 | pa->whoami = cpuid; |
---|
223 | pa->hard_id = cpu_hard_id[cpuid]; |
---|
224 | pa->hyp_stack_base = (void *)((ulong)stack + STACK_SIZE); |
---|
225 | mb(); |
---|
226 | |
---|
227 | /* This store has the effect of invoking secondary_cpu_init. */ |
---|
228 | global_cpu_table[cpuid] = pa; |
---|
229 | mb(); |
---|
230 | } |
---|
231 | |
---|
232 | static int kick_secondary_cpus(int maxcpus) |
---|
233 | { |
---|
234 | int cpuid; |
---|
235 | |
---|
236 | for_each_present_cpu(cpuid) { |
---|
237 | int threads; |
---|
238 | int i; |
---|
239 | |
---|
240 | threads = cpu_threads(cpuid); |
---|
241 | for (i = 0; i < threads; i++) |
---|
242 | cpu_set(i, cpu_sibling_map[cpuid]); |
---|
243 | |
---|
244 | /* For now everything is single core */ |
---|
245 | cpu_set(cpuid, cpu_core_map[cpuid]); |
---|
246 | |
---|
247 | rcu_online_cpu(cpuid); |
---|
248 | |
---|
249 | numa_set_node(cpuid, 0); |
---|
250 | numa_add_cpu(cpuid); |
---|
251 | |
---|
252 | if (cpuid == 0) |
---|
253 | continue; |
---|
254 | if (cpuid >= maxcpus) |
---|
255 | break; |
---|
256 | init_parea(cpuid); |
---|
257 | smp_generic_give_timebase(); |
---|
258 | |
---|
259 | /* wait for it */ |
---|
260 | while (!cpu_online(cpuid)) |
---|
261 | cpu_relax(); |
---|
262 | } |
---|
263 | |
---|
264 | return 0; |
---|
265 | } |
---|
266 | |
---|
267 | /* This is the first C code that secondary processors invoke. */ |
---|
268 | void secondary_cpu_init(int cpuid, unsigned long r4) |
---|
269 | { |
---|
270 | struct vcpu *vcpu; |
---|
271 | |
---|
272 | cpu_initialize(cpuid); |
---|
273 | smp_generic_take_timebase(); |
---|
274 | |
---|
275 | /* If we are online, we must be able to ACK IPIs. */ |
---|
276 | mpic_setup_this_cpu(); |
---|
277 | cpu_set(cpuid, cpu_online_map); |
---|
278 | |
---|
279 | vcpu = alloc_vcpu(idle_domain, cpuid, cpuid); |
---|
280 | BUG_ON(vcpu == NULL); |
---|
281 | |
---|
282 | set_current(idle_domain->vcpu[cpuid]); |
---|
283 | idle_vcpu[cpuid] = current; |
---|
284 | startup_cpu_idle_loop(); |
---|
285 | |
---|
286 | panic("should never get here\n"); |
---|
287 | } |
---|
288 | |
---|
289 | static void __init __start_xen(multiboot_info_t *mbi) |
---|
290 | { |
---|
291 | char *cmdline; |
---|
292 | module_t *mod = (module_t *)((ulong)mbi->mods_addr); |
---|
293 | ulong dom0_start, dom0_len; |
---|
294 | ulong initrd_start, initrd_len; |
---|
295 | |
---|
296 | memcpy(0, exception_vectors, exception_vectors_end - exception_vectors); |
---|
297 | synchronize_caches(0, exception_vectors_end - exception_vectors); |
---|
298 | |
---|
299 | ticks_per_usec = timebase_freq / 1000000ULL; |
---|
300 | |
---|
301 | /* Parse the command-line options. */ |
---|
302 | if ((mbi->flags & MBI_CMDLINE) && (mbi->cmdline != 0)) |
---|
303 | cmdline_parse(__va((ulong)mbi->cmdline)); |
---|
304 | |
---|
305 | /* we need to be able to identify this CPU early on */ |
---|
306 | init_boot_cpu(); |
---|
307 | |
---|
308 | /* We initialise the serial devices very early so we can get debugging. */ |
---|
309 | ns16550.io_base = 0x3f8; |
---|
310 | ns16550_init(0, &ns16550); |
---|
311 | ns16550.io_base = 0x2f8; |
---|
312 | ns16550_init(1, &ns16550); |
---|
313 | serial_init_preirq(); |
---|
314 | |
---|
315 | init_console(); |
---|
316 | /* let synchronize until we really get going */ |
---|
317 | console_start_sync(); |
---|
318 | |
---|
319 | /* Check that we have at least one Multiboot module. */ |
---|
320 | if (!(mbi->flags & MBI_MODULES) || (mbi->mods_count == 0)) { |
---|
321 | panic("FATAL ERROR: Require at least one Multiboot module.\n"); |
---|
322 | } |
---|
323 | |
---|
324 | /* OF dev tree is the last module */ |
---|
325 | oftree = mod[mbi->mods_count-1].mod_start; |
---|
326 | oftree_end = mod[mbi->mods_count-1].mod_end; |
---|
327 | oftree_len = oftree_end - oftree; |
---|
328 | |
---|
329 | /* remove it from consideration */ |
---|
330 | mod[mbi->mods_count-1].mod_start = 0; |
---|
331 | mod[mbi->mods_count-1].mod_end = 0; |
---|
332 | --mbi->mods_count; |
---|
333 | |
---|
334 | if (rtas_entry) { |
---|
335 | rtas_init((void *)oftree); |
---|
336 | /* remove rtas module from consideration */ |
---|
337 | mod[mbi->mods_count-1].mod_start = 0; |
---|
338 | mod[mbi->mods_count-1].mod_end = 0; |
---|
339 | --mbi->mods_count; |
---|
340 | } |
---|
341 | memory_init(mod, mbi->mods_count); |
---|
342 | |
---|
343 | #ifdef OF_DEBUG |
---|
344 | key_ofdump(0); |
---|
345 | #endif |
---|
346 | percpu_init_areas(); |
---|
347 | |
---|
348 | init_parea(0); |
---|
349 | cpu_initialize(0); |
---|
350 | |
---|
351 | #ifdef CONFIG_GDB |
---|
352 | initialise_gdb(); |
---|
353 | if (opt_earlygdb) |
---|
354 | debugger_trap_immediate(); |
---|
355 | #endif |
---|
356 | |
---|
357 | start_of_day(); |
---|
358 | |
---|
359 | mpic_setup_this_cpu(); |
---|
360 | |
---|
361 | /* Deal with secondary processors. */ |
---|
362 | if (opt_nosmp || ofd_boot_cpu == -1) { |
---|
363 | printk("nosmp: leaving secondary processors spinning forever\n"); |
---|
364 | } else { |
---|
365 | printk("spinning up at most %d total processors ...\n", max_cpus); |
---|
366 | kick_secondary_cpus(max_cpus); |
---|
367 | } |
---|
368 | |
---|
369 | /* This cannot be called before secondary cpus are marked online. */ |
---|
370 | percpu_free_unused_areas(); |
---|
371 | |
---|
372 | /* Create initial domain 0. */ |
---|
373 | dom0 = domain_create(0, 0, DOM0_SSIDREF); |
---|
374 | if (dom0 == NULL) |
---|
375 | panic("Error creating domain 0\n"); |
---|
376 | |
---|
377 | /* The Interrupt Controller will route everything to CPU 0 so we |
---|
378 | * need to make sure Dom0's vVCPU 0 is pinned to the CPU */ |
---|
379 | dom0->vcpu[0]->cpu_affinity = cpumask_of_cpu(0); |
---|
380 | |
---|
381 | dom0->is_privileged = 1; |
---|
382 | |
---|
383 | cmdline = (char *)(mod[0].string ? __va((ulong)mod[0].string) : NULL); |
---|
384 | |
---|
385 | /* scrub_heap_pages() requires IRQs enabled, and we're post IRQ setup... */ |
---|
386 | local_irq_enable(); |
---|
387 | /* Scrub RAM that is still free and so may go to an unprivileged domain. */ |
---|
388 | scrub_heap_pages(); |
---|
389 | |
---|
390 | dom0_start = mod[0].mod_start; |
---|
391 | dom0_len = mod[0].mod_end - mod[0].mod_start; |
---|
392 | if (mbi->mods_count > 1) { |
---|
393 | initrd_start = mod[1].mod_start; |
---|
394 | initrd_len = mod[1].mod_end - mod[1].mod_start; |
---|
395 | } else { |
---|
396 | initrd_start = 0; |
---|
397 | initrd_len = 0; |
---|
398 | } |
---|
399 | if (construct_dom0(dom0, dom0_start, dom0_len, |
---|
400 | initrd_start, initrd_len, |
---|
401 | cmdline) != 0) { |
---|
402 | panic("Could not set up DOM0 guest OS\n"); |
---|
403 | } |
---|
404 | |
---|
405 | init_xenheap_pages(ALIGN_UP(dom0_start, PAGE_SIZE), |
---|
406 | ALIGN_DOWN(dom0_start + dom0_len, PAGE_SIZE)); |
---|
407 | if (initrd_start) |
---|
408 | init_xenheap_pages(ALIGN_UP(initrd_start, PAGE_SIZE), |
---|
409 | ALIGN_DOWN(initrd_start + initrd_len, PAGE_SIZE)); |
---|
410 | |
---|
411 | init_trace_bufs(); |
---|
412 | |
---|
413 | console_endboot(); |
---|
414 | |
---|
415 | /* Hide UART from DOM0 if we're using it */ |
---|
416 | serial_endboot(); |
---|
417 | |
---|
418 | console_end_sync(); |
---|
419 | |
---|
420 | domain_unpause_by_systemcontroller(dom0); |
---|
421 | #ifdef DEBUG_IPI |
---|
422 | ipi_torture_test(); |
---|
423 | #endif |
---|
424 | startup_cpu_idle_loop(); |
---|
425 | } |
---|
426 | |
---|
427 | void __init __start_xen_ppc( |
---|
428 | ulong r3, ulong r4, ulong r5, ulong r6, ulong r7, ulong orig_msr) |
---|
429 | { |
---|
430 | multiboot_info_t *mbi = NULL; |
---|
431 | |
---|
432 | /* clear bss */ |
---|
433 | memset(__bss_start, 0, (ulong)_end - (ulong)__bss_start); |
---|
434 | |
---|
435 | if (r5 > 0) { |
---|
436 | /* we were booted by OpenFirmware */ |
---|
437 | mbi = boot_of_init(r3, r4, r5, r6, r7, orig_msr); |
---|
438 | |
---|
439 | } else { |
---|
440 | /* booted by someone else that hopefully has a trap handler */ |
---|
441 | __builtin_trap(); |
---|
442 | } |
---|
443 | |
---|
444 | __start_xen(mbi); |
---|
445 | |
---|
446 | } |
---|
447 | |
---|
448 | extern void arch_get_xen_caps(xen_capabilities_info_t *info); |
---|
449 | void arch_get_xen_caps(xen_capabilities_info_t *info) |
---|
450 | { |
---|
451 | /* Interface name is always xen-3.0-* for Xen-3.x. */ |
---|
452 | int major = 3, minor = 0; |
---|
453 | char s[32]; |
---|
454 | |
---|
455 | (*info)[0] = '\0'; |
---|
456 | |
---|
457 | snprintf(s, sizeof(s), "xen-%d.%d-powerpc64 ", major, minor); |
---|
458 | safe_strcat(*info, s); |
---|
459 | } |
---|
460 | |
---|
461 | |
---|
462 | |
---|
463 | /* |
---|
464 | * Local variables: |
---|
465 | * mode: C |
---|
466 | * c-set-style: "BSD" |
---|
467 | * c-basic-offset: 4 |
---|
468 | * tab-width: 4 |
---|
469 | * indent-tabs-mode: nil |
---|
470 | * End: |
---|
471 | */ |
---|