2b37549a416ece0994fdbfd3506b22f2631bd8ec
[barrelfish] / kernel / arch / x86 / startup_x86.c
1 /**
2  * \file
3  * \brief x86 kernel bootup code.
4  */
5
6 /*
7  * Copyright (c) 2007, 2008, 2009, 2010, 2013, ETH Zurich.
8  * All rights reserved.
9  *
10  * This file is distributed under the terms in the attached LICENSE file.
11  * If you do not find this file, copies can be found by writing to:
12  * ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
13  */
14
15 #include <kernel.h>
16
17 #include <dispatch.h>
18 #include <elf/elf.h>
19 #include <exec.h>
20 #include <init.h>
21 #include <getopt/getopt.h>
22 #include <kcb.h>
23 #include <kernel_multiboot.h>
24 #include <irq.h>
25 #include <kputchar.h>
26 #include <mdb/mdb_tree.h>
27 #ifdef CONFIG_MICROBENCHMARKS
28 #include <microbenchmarks.h>
29 #endif
30 #include <paging_kernel_arch.h>
31 #include <startup.h>
32 #include <string.h>
33 #include <wakeup.h>
34 #include <barrelfish_kpi/cpu.h>
35 #include <barrelfish_kpi/init.h>
36 #include <barrelfish_kpi/paging_arch.h>
37 #include <barrelfish_kpi/syscalls.h>
38 #include <arch/x86/apic.h>
39 #include <target/x86/barrelfish_kpi/coredata_target.h>
40 #include <arch/x86/startup_x86.h>
41 #include <dev/ia32_dev.h>
42
43 #ifdef __scc__
44 #       include <rck.h>
45 #endif
46
47 /// Optional core ID to use for the BSP core (command-line argument)
48 static int bsp_coreid;
49
50 /// Quick way to find the base address of a cnode capability
51 #define CNODE(cte)     (cte)->cap.u.cnode.cnode
52
53 /// Pointer to bootinfo structure for init
54 static struct bootinfo *bootinfo = (struct bootinfo *)BOOTINFO_BASE;
55
56 /**
57  * Each kernel has a local copy of global and locks. However, during booting and
58  * kernel relocation, these are set to point to global of the pristine kernel,
59  * so that all the kernels can share it.
60  */
61 static  struct global myglobal;
62 struct global *global = &myglobal;
63
64 // Physical memory allocator for spawn_app_init
65 static lpaddr_t app_alloc_phys_start, app_alloc_phys_end;
66 static lpaddr_t app_alloc_phys(size_t size)
67 {
68     uint32_t npages = (size + BASE_PAGE_SIZE - 1) / BASE_PAGE_SIZE;
69
70     lpaddr_t addr = app_alloc_phys_start;
71     app_alloc_phys_start += npages * BASE_PAGE_SIZE;
72
73     if (app_alloc_phys_start >= app_alloc_phys_end) {
74         panic("Out of memory, increase CORE_DATA_PAGES");
75     }
76
77     memset((void*)local_phys_to_mem(addr), 0, npages * BASE_PAGE_SIZE);
78
79     return addr;
80 }
81
82 /**
83  * The address from where bsp_alloc_phys will start allocating memory
84  */
85 static lpaddr_t bsp_init_alloc_addr = 0;
86
87 /**
88  * \brief Linear physical memory allocator.
89  *
90  * This function allocates a linear region of addresses of size 'size' from
91  * physical memory.
92  *
93  * \param size  Number of bytes to allocate.
94  *
95  * \return Base physical address of memory region.
96  */
97 static lpaddr_t bsp_alloc_phys(size_t size)
98 {
99     // round to base page size
100     uint32_t npages = (size + BASE_PAGE_SIZE - 1) / BASE_PAGE_SIZE;
101
102     assert(bsp_init_alloc_addr != 0);
103     lpaddr_t addr = bsp_init_alloc_addr;
104
105     bsp_init_alloc_addr += npages * BASE_PAGE_SIZE;
106
107     memset((void*)local_phys_to_mem(addr), 0, npages * BASE_PAGE_SIZE);
108
109     return addr;
110 }
111
112 /**
113  * \brief Map init user-space memory.
114  *
115  * This function maps pages of the init user-space module. It expects
116  * the virtual base address 'vbase' of a program segment of the init executable,
117  * its size 'size' and its ELF64 access control flags. It maps pages
118  * into physical memory that is allocated on the fly and puts
119  * corresponding frame caps into init's segcn.
120  *
121  * \param vbase Virtual base address of program segment.
122  * \param size  Size of program segment in bytes.
123  * \param flags ELF64 access control flags of program segment.
124  * \param ret   Used to return base region pointer
125  */
126 errval_t startup_alloc_init(void *state, genvaddr_t gvbase, size_t size,
127                             uint32_t flags, void **ret)
128 {
129
130     errval_t err;
131
132     struct spawn_state *spawn_state = state;
133
134     lvaddr_t vbase = (lvaddr_t)gvbase; /* XXX */
135     lvaddr_t offset = BASE_PAGE_OFFSET(vbase);
136
137     /* Page align the parameters */
138     paging_align(&vbase, NULL, &size, BASE_PAGE_SIZE);
139
140     lpaddr_t pbase = 0, paddr = 0;
141     for(lvaddr_t i = vbase; i < vbase + size; i += BASE_PAGE_SIZE) {
142         if (apic_is_bsp()) {
143             paddr = bsp_alloc_phys(BASE_PAGE_SIZE);
144         } else {
145             paddr = app_alloc_phys(BASE_PAGE_SIZE);
146         }
147
148         if(pbase == 0) {
149             pbase = paddr;
150         }
151
152         err = startup_map_init(i, paddr, BASE_PAGE_SIZE, flags);
153         assert(err_is_ok(err));
154     }
155
156     if (apic_is_bsp()) {
157         // Create frame caps for segcn
158         paddr += BASE_PAGE_SIZE;
159
160         debug(SUBSYS_STARTUP,
161               "Allocated physical memory [0x%"PRIxLPADDR", 0x%"PRIxLPADDR"]\n",
162               pbase, paddr - pbase);
163
164         err = create_caps_to_cnode(pbase, paddr - pbase,
165                                    RegionType_RootTask, spawn_state, bootinfo);
166         if (err_is_fail(err)) {
167             return err;
168         }
169     }
170
171     assert(ret != NULL);
172     *ret = (void *)(vbase + offset);
173
174     return SYS_ERR_OK;
175 }
176
177 /// Setup the module cnode, which contains frame caps to all multiboot modules
178 void create_module_caps(struct spawn_state *st)
179 {
180     errval_t err;
181
182     /* Create caps for multiboot modules */
183     struct multiboot_modinfo *module =
184         (struct multiboot_modinfo *)local_phys_to_mem(glbl_core_data->mods_addr);
185
186     // Allocate strings area
187     lpaddr_t mmstrings_phys = bsp_alloc_phys(BASE_PAGE_SIZE);
188     lvaddr_t mmstrings_base = local_phys_to_mem(mmstrings_phys);
189     lvaddr_t mmstrings = mmstrings_base;
190
191     // create cap for strings area in first slot of modulecn
192     assert(st->modulecn_slot == 0);
193     err = caps_create_new(ObjType_Frame, mmstrings_phys, BASE_PAGE_BITS,
194                           BASE_PAGE_BITS, my_core_id,
195                           caps_locate_slot(CNODE(st->modulecn),
196                                            st->modulecn_slot++));
197     assert(err_is_ok(err));
198
199     /* Walk over multiboot modules, creating frame caps */
200     for (int i = 0; i < glbl_core_data->mods_count; i++) {
201         struct multiboot_modinfo *m = &module[i];
202
203         // Set memory regions within bootinfo
204         struct mem_region *region =
205             &bootinfo->regions[bootinfo->regions_length++];
206
207         genpaddr_t remain = MULTIBOOT_MODULE_SIZE(*m);
208         genpaddr_t base_addr = local_phys_to_gen_phys(m->mod_start);
209
210         region->mr_type = RegionType_Module;
211         region->mr_base = base_addr;
212         region->mrmod_slot = st->modulecn_slot;  // first slot containing caps
213         region->mrmod_size = remain;  // size of image _in bytes_
214         region->mrmod_data = mmstrings - mmstrings_base; // offset of string in area
215
216         // round up to page size for caps
217         remain = ROUND_UP(remain, BASE_PAGE_SIZE);
218
219         // Create max-sized caps to multiboot module in module cnode
220         while (remain > 0) {
221             assert((base_addr & BASE_PAGE_MASK) == 0);
222             assert((remain & BASE_PAGE_MASK) == 0);
223
224             // determine size of next chunk
225             uint8_t block_size = bitaddralign(remain, base_addr);
226
227             assert(st->modulecn_slot < (1UL << st->modulecn->cap.u.cnode.bits));
228             // create as DevFrame cap to avoid zeroing memory contents
229             err = caps_create_new(ObjType_DevFrame, base_addr, block_size,
230                                   block_size, my_core_id,
231                                   caps_locate_slot(CNODE(st->modulecn),
232                                                    st->modulecn_slot++));
233             assert(err_is_ok(err));
234
235             // Advance by that chunk
236             base_addr += ((genpaddr_t)1 << block_size);
237             remain -= ((genpaddr_t)1 << block_size);
238         }
239
240         // Copy multiboot module string to mmstrings area
241         strcpy((char *)mmstrings, MBADDR_ASSTRING(m->string));
242         mmstrings += strlen(MBADDR_ASSTRING(m->string)) + 1;
243         assert(mmstrings < mmstrings_base + BASE_PAGE_SIZE);
244     }
245 }
246
247 void cleanup_bios_regions(char *mmap_addr, char **new_mmap_addr,
248                           uint32_t *new_mmap_length)
249 {
250     assert(new_mmap_addr);
251     assert(new_mmap_length);
252 #define PRINT_REGIONS(map, map_length) do {\
253         for(char * printcur = map; printcur < map + map_length;) {\
254             struct multiboot_mmap * printcurmmap = (struct multiboot_mmap * SAFE)TC(printcur);\
255             printf("\t0x%08"PRIx64" - 0x%08"PRIx64" Type: %"PRIu32" Length: 0x%"PRIx64"\n", printcurmmap->base_addr, printcurmmap->base_addr + printcurmmap->length, printcurmmap->type, printcurmmap->length);\
256             printcur += printcurmmap->size + 4;\
257         }\
258     } while (0)
259
260     printf("Raw MMAP from BIOS\n");
261     PRINT_REGIONS(mmap_addr, glbl_core_data->mmap_length);
262
263     // normalize memory regions
264     lpaddr_t clean_base = bsp_alloc_phys(glbl_core_data->mmap_length);
265     char *clean_mmap_addr = (char *)local_phys_to_mem(clean_base);
266     uint32_t clean_mmap_length = glbl_core_data->mmap_length;
267     memcpy(clean_mmap_addr, mmap_addr, glbl_core_data->mmap_length);
268
269     // first of all, sort regions by base address
270     // yes, it's a bubble sort, but the dataset is small and usually in the right order
271     bool swapped;
272     do {
273         swapped = false;
274
275         for(char * cur = clean_mmap_addr; cur < clean_mmap_addr + clean_mmap_length;) {
276             struct multiboot_mmap * curmmap = (struct multiboot_mmap * SAFE)TC(cur);
277             if (cur + curmmap->size + 4 >= clean_mmap_addr + clean_mmap_length)
278                 break; // do not try to move this check into the forloop as entries do not have to be the same length
279
280             struct multiboot_mmap * nextmmap = (struct multiboot_mmap * SAFE)TC(cur + curmmap->size + 4);
281
282             if (nextmmap->base_addr < curmmap->base_addr ||
283                 (nextmmap->base_addr == curmmap->base_addr && nextmmap->length > curmmap->length)) {
284                 // swap
285                 assert(curmmap->size == 20); // FIXME: The multiboot specification does not require this size
286                 assert(nextmmap->size == 20);
287
288                 struct multiboot_mmap tmp;
289                 tmp = *curmmap;
290                 *curmmap = *nextmmap;
291                 *nextmmap = tmp;
292
293                 swapped = true;
294             }
295
296             cur += curmmap->size + 4;
297         }
298     } while(swapped);
299
300     printf("Sorted MMAP\n");
301     PRINT_REGIONS(clean_mmap_addr, clean_mmap_length);
302
303     // now merge consecutive memory regions of the same or lower type
304     for(char * cur = clean_mmap_addr; cur < clean_mmap_addr + clean_mmap_length;) {
305         struct multiboot_mmap * curmmap = (struct multiboot_mmap * SAFE)TC(cur);
306         if (cur + curmmap->size + 4 >= clean_mmap_addr + clean_mmap_length)
307             break; // do not try to move this check into the forloop as entries do not have to be the same length
308
309         struct multiboot_mmap * nextmmap = (struct multiboot_mmap * SAFE)TC(cur + curmmap->size + 4);
310
311         /* On some machines (brie1) the IOAPIC region is only 1kB.
312          * Currently we're not able to map regions that are <4kB so we
313          * make sure that every region (if there is no problematic overlap)
314          * is at least BASE_PAGE_SIZEd (==4kB) here.
315          */
316         if ((curmmap->length < BASE_PAGE_SIZE) && (curmmap->base_addr + BASE_PAGE_SIZE <= nextmmap->base_addr)) {
317             curmmap->length = BASE_PAGE_SIZE;
318         }
319
320 #define DISCARD_NEXT_MMAP do {\
321     uint32_t discardsize = nextmmap->size + 4;\
322     memmove(cur + curmmap->size + 4, cur + curmmap->size + 4 + discardsize, clean_mmap_length - (cur - clean_mmap_addr) - curmmap->size - 4 - discardsize);\
323     clean_mmap_length -= discardsize;\
324     } while (0)
325
326 #define BUBBLE_NEXT_MMAP do {\
327     for (char * bubblecur = cur + curmmap->size + 4; bubblecur < clean_mmap_addr + clean_mmap_length;){\
328         struct multiboot_mmap * bubblecur_mmap = (struct multiboot_mmap * SAFE)TC(bubblecur);\
329         if (bubblecur + bubblecur_mmap->size + 4 >= clean_mmap_addr + clean_mmap_length)\
330             break;\
331         struct multiboot_mmap * bubblenext_mmap = (struct multiboot_mmap * SAFE)TC(bubblecur + bubblecur_mmap->size + 4);\
332         if (bubblenext_mmap->base_addr < bubblecur_mmap->base_addr || (bubblecur_mmap->base_addr == bubblenext_mmap->base_addr && bubblenext_mmap->length > bubblecur_mmap->length)) {\
333             struct multiboot_mmap bubbletmp; bubbletmp = *bubblecur_mmap; *bubblecur_mmap = *bubblenext_mmap; *bubblenext_mmap = bubbletmp;\
334         } else break;\
335     }} while(0)
336
337
338         bool reduced = false;
339         do {
340             reduced = false;
341
342             if (curmmap->base_addr == nextmmap->base_addr) {
343                 // regions start at the same location
344                 if (curmmap->length == nextmmap->length) {
345                     // trivial case. They are the same. Choose higher type and discard next
346                     curmmap->type = max(curmmap->type, nextmmap->type);
347
348                     DISCARD_NEXT_MMAP;
349
350                     reduced = true;
351                     continue;
352                 } else {
353                     // next region is smaller (we sorted that way)
354                     if (nextmmap->type <= curmmap->type) {
355                         // next regions type is the same or smaller. discard
356                         DISCARD_NEXT_MMAP;
357
358                         reduced = true;
359                         continue;
360                     } else {
361                         // next regions type is higher, so it gets priority
362                         // change type of current region and shrink next
363                         uint32_t tmptype = curmmap->type;
364                         uint64_t newlength = curmmap->length - nextmmap->length;
365                         curmmap->type = nextmmap->type;
366                         curmmap->length = nextmmap->length;
367                         nextmmap->type = tmptype;
368                         nextmmap->base_addr += nextmmap->length;
369                         nextmmap->length = newlength;
370
371                         // now we need to bubble next to the right place to restore order
372                         BUBBLE_NEXT_MMAP;
373
374                         reduced = true;
375                         continue;
376                     }
377                 }
378             }
379
380             // regions overlap
381             if (nextmmap->base_addr > curmmap->base_addr && nextmmap->base_addr < curmmap->base_addr + curmmap->length) {
382                 // same type
383                 if (curmmap->type == nextmmap->type) {
384                     // simple. just extend if necessary and discard next
385                     if (nextmmap->base_addr + nextmmap->length > curmmap->base_addr + curmmap->length)
386                         curmmap->length = (nextmmap->base_addr + nextmmap->length) - curmmap->base_addr;
387
388                     DISCARD_NEXT_MMAP;
389
390                     reduced = true;
391                     continue;
392                 } else {
393                     // type is not the same
394                     if (nextmmap->base_addr + nextmmap->length < curmmap->base_addr + curmmap->length) {
395                         // there is a chunk at the end. create a new region
396                         struct multiboot_mmap tmpmmap;
397                         tmpmmap.size = 20;
398                         tmpmmap.base_addr = nextmmap->base_addr + nextmmap->length;
399                         tmpmmap.length = (curmmap->base_addr + curmmap->length) - (nextmmap->base_addr + nextmmap->length);
400                         tmpmmap.type = curmmap->type;
401
402                         // move everything to make room
403                         assert(clean_mmap_length + tmpmmap.length + 4 < BOOTINFO_SIZE);
404                         memmove(cur + curmmap->size + 4 + tmpmmap.size + 4, cur + curmmap->size + 4, clean_mmap_length - ((cur - clean_mmap_addr) + curmmap->size + 4));
405                         clean_mmap_length += tmpmmap.size + 4;
406
407                         // insert new
408                         *nextmmap = tmpmmap;
409
410                         // restore order
411                         BUBBLE_NEXT_MMAP;
412
413                         reduced = true;
414                     }
415
416                     // after the previous step, the next region either ends
417                     // at the same location as the current or is longer
418                     uint64_t overlap = (curmmap->base_addr + curmmap->length) - nextmmap->base_addr;
419
420                     if (curmmap-> type > nextmmap->type) {
421                         // current has priority, shrink next and extend current
422                         nextmmap->length -= overlap;
423                         nextmmap->base_addr += overlap;
424                         curmmap->length += overlap;
425
426                         if (nextmmap->length == 0)
427                             DISCARD_NEXT_MMAP;
428
429                         reduced = true;
430                         continue;
431                     } else {
432                         // next has priority, shrink current and extend next
433                         nextmmap->length += overlap;
434                         nextmmap->base_addr -= overlap;
435                         curmmap->length -= overlap;
436
437                         reduced = true;
438                         continue;
439                     }
440                 }
441             }
442         } while (reduced);
443
444         cur += curmmap->size + 4;
445
446 #undef DISCARD_NEXT_MMAP
447 #undef BUBBLE_NEXT_MMAP
448     }
449
450     printf("Preprocessed MMAP\n");
451     PRINT_REGIONS(clean_mmap_addr, clean_mmap_length);
452
453     // we can only map pages. Therefore page align regions
454     for(char * cur = clean_mmap_addr; cur < clean_mmap_addr + clean_mmap_length;) {
455         struct multiboot_mmap * curmmap = (struct multiboot_mmap * SAFE)TC(cur);
456         if (cur + curmmap->size + 4 >= clean_mmap_addr + clean_mmap_length)
457             break; // do not try to move this check into the forloop as entries do not have to be the same length
458
459         struct multiboot_mmap * nextmmap = (struct multiboot_mmap * SAFE)TC(cur + curmmap->size + 4);
460
461         if (nextmmap->base_addr & BASE_PAGE_MASK) {
462             uint64_t offset = nextmmap->base_addr - ((nextmmap->base_addr >> BASE_PAGE_BITS) << BASE_PAGE_BITS);
463
464             // round in favour of higher type
465             if (curmmap->type > nextmmap->type) {
466                 curmmap->length += BASE_PAGE_SIZE - offset;
467                 nextmmap->base_addr += BASE_PAGE_SIZE - offset;
468                 nextmmap->length -= BASE_PAGE_SIZE - offset;
469             } else {
470                 curmmap->length -= offset;
471                 nextmmap->base_addr -= offset;
472                 nextmmap->length += offset;
473             }
474         }
475
476         cur += curmmap->size + 4;
477     }
478
479     printf("Pagealigned MMAP\n");
480     PRINT_REGIONS(clean_mmap_addr, clean_mmap_length);
481
482 #undef PRINT_REGIONS
483     *new_mmap_addr = clean_mmap_addr;
484     *new_mmap_length = clean_mmap_length;
485     return;
486 }
487
488 // XXX from serial.c
489 extern int serial_portbase;
490
491 static struct cmdarg cmdargs[] = {
492     {"loglevel", ArgType_Int, { .integer = &kernel_loglevel }},
493     {"logmask", ArgType_Int, { .integer = &kernel_log_subsystem_mask }},
494     {"ticks", ArgType_Bool, { .boolean = &kernel_ticks_enabled }},
495     {"timeslice", ArgType_Int, { .integer = &kernel_timeslice }},
496 #ifndef __scc__ // FIXME: why not?
497     {"serial", ArgType_Int, { .integer = &serial_portbase }},
498 #endif
499     {"bsp_coreid", ArgType_Int, { .integer = &bsp_coreid }},
500     {NULL, 0, {NULL}}
501 };
502
503 /**
504  * Name of multiboot module containing program for init domains.
505  */
506 #if defined(__k1om__)
507 #       define BSP_INIT_MODULE_PATH     BF_BINARY_PREFIX "k1om/sbin/init"
508 #elif defined(__x86_64__)
509 #       define BSP_INIT_MODULE_PATH     BF_BINARY_PREFIX "x86_64/sbin/init"
510 #elif defined(__scc__)
511 #       define BSP_INIT_MODULE_PATH     BF_BINARY_PREFIX "scc/sbin/init"
512 #elif defined(__i386__)
513 #       define BSP_INIT_MODULE_PATH     BF_BINARY_PREFIX "x86_32/sbin/init"
514 #else
515 #       error "Unknown x86"
516 #endif
517 #define BSP_INIT_PROG_NAME       "init"
518 #define APP_INIT_PROG_NAME       "monitor"
519
520 /**
521  * \brief Kernel's early startup code, called from arch-specific bootstrap.
522  */
523 void kernel_startup_early(void)
524 {
525     const char *cmdline;
526     assert(glbl_core_data != NULL);
527     cmdline = MBADDR_ASSTRING(glbl_core_data->cmdline);
528     parse_commandline(cmdline, cmdargs);
529 }
530
531 /**
532  * \brief Kernel's main startup code, called from arch-specific bootstrap.
533  *
534  * This function never returns.
535  */
536 extern bool verbose_dispatch;
537 void kernel_startup(void)
538 {
539 #ifdef CONFIG_MICROBENCHMARKS
540     printk(LOG_NOTE, "\nRunning microbenchmarks...\n");
541     microbenchmarks_run_all();
542 #endif
543
544     /* Initialize the core_data */
545     /* Used when bringing up other cores, must be at consistent global address
546      * seen by all cores */
547     struct x86_core_data *core_data
548         = (void *)((lvaddr_t)&_start_kernel - BASE_PAGE_SIZE);
549
550     struct dcb *init_dcb;
551     if (apic_is_bsp()) {
552         if (bsp_coreid != 0) {
553             my_core_id = bsp_coreid;
554         }
555
556         /* Initialize the location to allocate phys memory from */
557         bsp_init_alloc_addr = glbl_core_data->start_free_ram;
558
559         /* allocate initial KCB */
560         kcb_current = (struct kcb *) local_phys_to_mem(bsp_alloc_phys(sizeof(*kcb_current)));
561         memset(kcb_current, 0, sizeof(*kcb_current));
562         assert(kcb_current);
563
564         /* spawn init */
565         init_dcb = spawn_bsp_init(BSP_INIT_MODULE_PATH, bsp_alloc_phys);
566     } else {
567         kcb_current = (struct kcb *)
568             local_phys_to_mem((lpaddr_t) kcb_current);
569
570         start_ap_signal();
571         // if we have a kernel control block, use it
572         if (kcb_current && kcb_current->is_valid) {
573             debug(SUBSYS_STARTUP, "have valid kcb, restoring state\n");
574             print_kcb();
575
576             // restore mdb
577             errval_t err = mdb_init(kcb_current);
578             if (err_is_fail(err)) {
579                 panic("couldn't restore mdb");
580             }
581             // figure out if we need to convert scheduler state
582 #ifdef CONFIG_SCHEDULER_RR
583             if (kcb_current->sched != SCHED_RR) {
584                 printf("converting scheduler state to RR\n");
585                 scheduler_convert();
586             }
587 #elif CONFIG_SCHEDULER_RBED
588             if (kcb_current->sched != SCHED_RBED) {
589                 printf("converting scheduler state to RBED\n");
590                 scheduler_convert();
591             }
592 #else
593 #error must define scheduler
594 #endif
595             // update core id of domains
596             kcb_update_core_id(kcb_current);
597             // set queue pointers
598             scheduler_restore_state();
599             // restore wakeup queue state
600             printk(LOG_DEBUG, "%s:%s:%d: kcb_current->wakeup_queue_head = %p\n",
601                    __FILE__, __FUNCTION__, __LINE__, kcb_current->wakeup_queue_head);
602             wakeup_set_queue_head(kcb_current->wakeup_queue_head);
603
604             printk(LOG_DEBUG, "%s:%s:%d: dcb_current = %p\n",
605                    __FILE__, __FUNCTION__, __LINE__, dcb_current);
606             struct dcb *next = schedule();
607             debug(SUBSYS_STARTUP, "next = %p\n", next);
608             if (next != NULL) {
609                 assert (next->disp);
610                 struct dispatcher_shared_generic *dst =
611                     get_dispatcher_shared_generic(next->disp);
612                 debug(SUBSYS_STARTUP, "scheduling '%s' from restored state\n",
613                       dst->name);
614             }
615             // interrupt state should be fine, as it's used directly from the
616             // kcb.
617             dispatch(next);
618             panic("should not get here!");
619         }
620         my_core_id = core_data->dst_core_id;
621
622         /* Initialize the allocator */
623         app_alloc_phys_start = core_data->memory_base_start;
624         app_alloc_phys_end   = ((lpaddr_t)1 << core_data->memory_bits) +
625                                     app_alloc_phys_start;
626
627         init_dcb = spawn_app_init(core_data, APP_INIT_PROG_NAME, app_alloc_phys);
628     }
629
630     // Should not return
631     //if (apic_is_bsp()) {
632     dispatch(init_dcb);
633     //}
634     panic("Error spawning init!");
635 }
636
637 /*
638  * Configure the IA32_PAT_MSR register such that PA4 is write-combining and
639  * PA5 is write-protect.
640  */
641 void configure_page_attribute_table(void)
642 {
643     ia32_t ia32;
644     ia32_cr_pat_t pat;
645
646     ia32_initialize(&ia32);
647
648     pat = ia32_cr_pat_rd(&ia32);
649
650     pat = ia32_cr_pat_pa4_insert(pat, ia32_wc);
651     pat = ia32_cr_pat_pa5_insert(pat, ia32_wp);
652
653     ia32_cr_pat_wr(&ia32, pat);
654
655     debug(SUBSYS_STARTUP, "Configured IA32_PAT_MSR.\n");
656 }