static BOOT_CODE bool_t try_boot_sys( unsigned long multiboot_magic, multiboot_info_t* mbi ) { /* ==== following code corresponds to the "select" in abstract specification ==== */ acpi_rsdt_t* acpi_rsdt; /* physical address of ACPI root */ paddr_t mods_end_paddr; /* physical address where boot modules end */ paddr_t load_paddr; word_t i; p_region_t ui_p_regs; multiboot_module_t *modules = (multiboot_module_t*)(word_t)mbi->mod_list; if (multiboot_magic != MULTIBOOT_MAGIC) { printf("Boot loader not multiboot compliant\n"); return false; } cmdline_parse((const char *)(word_t)mbi->cmdline, &cmdline_opt); if ((mbi->flags & MULTIBOOT_INFO_MEM_FLAG) == 0) { printf("Boot loader did not provide information about physical memory size\n"); return false; } if (!x86_cpuid_initialize()) { printf("Warning: Your x86 CPU has an unsupported vendor, '%s'.\n" "\tYour setup may not be able to competently run seL4 as " "intended.\n" "\tCurrently supported x86 vendors are AMD and Intel.\n", x86_cpuid_get_identity()->vendor_string); } if (!is_compiled_for_microarchitecture()) { printf("Warning: Your kernel was not compiled for the current microarchitecture.\n"); } #if CONFIG_MAX_NUM_NODES > 1 /* copy boot code for APs to lower memory to run in real mode */ if (!copy_boot_code_aps(mbi->mem_lower)) { return false; } /* Initialize any kernel TLS */ mode_init_tls(0); #endif /* initialize the memory. We track two kinds of memory regions. Physical memory * that we will use for the kernel, and physical memory regions that we must * not give to the user. Memory regions that must not be given to the user * include all the physical memory in the kernel window, but also includes any * important or kernel devices. */ boot_state.mem_p_regs.count = 0; init_allocated_p_regions(); if (mbi->flags & MULTIBOOT_INFO_MMAP_FLAG) { if (!parse_mem_map(mbi->mmap_length, mbi->mmap_addr)) { return false; } } else { /* calculate memory the old way */ p_region_t avail; avail.start = HIGHMEM_PADDR; avail.end = ROUND_DOWN(avail.start + (mbi->mem_upper << 10), PAGE_BITS); if (!add_mem_p_regs(avail)) { return false; } } boot_state.ki_p_reg.start = PADDR_LOAD; boot_state.ki_p_reg.end = kpptr_to_paddr(ki_end); /* copy VESA information from multiboot header */ if ((mbi->flags & MULTIBOOT_INFO_GRAPHICS_FLAG) == 0) { boot_state.vbe_info.vbeMode = -1; printf("Multiboot gave us no video information\n"); } else { boot_state.vbe_info.vbeInfoBlock = *(seL4_VBEInfoBlock_t*)(seL4_Word)mbi->vbe_control_info; boot_state.vbe_info.vbeModeInfoBlock = *(seL4_VBEModeInfoBlock_t*)(seL4_Word)mbi->vbe_mode_info; boot_state.vbe_info.vbeMode = mbi->vbe_mode; printf("Got VBE info in multiboot. Current video mode is %d\n", mbi->vbe_mode); boot_state.vbe_info.vbeInterfaceSeg = mbi->vbe_interface_seg; boot_state.vbe_info.vbeInterfaceOff = mbi->vbe_interface_off; boot_state.vbe_info.vbeInterfaceLen = mbi->vbe_interface_len; } printf("Kernel loaded to: start=0x%lx end=0x%lx size=0x%lx entry=0x%lx\n", boot_state.ki_p_reg.start, boot_state.ki_p_reg.end, boot_state.ki_p_reg.end - boot_state.ki_p_reg.start, (paddr_t)_start ); /* remapping legacy IRQs to their correct vectors */ pic_remap_irqs(IRQ_INT_OFFSET); if (config_set(CONFIG_IRQ_IOAPIC)) { /* Disable the PIC so that it does not generate any interrupts. We need to * do this *before* we initialize the apic */ pic_disable(); } /* get ACPI root table */ acpi_rsdt = acpi_init(); if (!acpi_rsdt) { return false; } /* check if kernel configuration matches platform requirments */ if (!acpi_fadt_scan(acpi_rsdt)) { return false; } if (!config_set(CONFIG_IOMMU) || cmdline_opt.disable_iommu) { boot_state.num_drhu = 0; } else { /* query available IOMMUs from ACPI */ acpi_dmar_scan( acpi_rsdt, boot_state.drhu_list, &boot_state.num_drhu, MAX_NUM_DRHU, &boot_state.rmrr_list ); } /* query available CPUs from ACPI */ boot_state.num_cpus = acpi_madt_scan(acpi_rsdt, boot_state.cpus, &boot_state.num_ioapic, boot_state.ioapic_paddr); if (boot_state.num_cpus == 0) { printf("No CPUs detected\n"); return false; } if (config_set(CONFIG_IRQ_IOAPIC)) { if (boot_state.num_ioapic == 0) { printf("No IOAPICs detected\n"); return false; } } else { if (boot_state.num_ioapic > 0) { printf("Detected %d IOAPICs, but configured to use PIC instead\n", boot_state.num_ioapic); } } if (!(mbi->flags & MULTIBOOT_INFO_MODS_FLAG)) { printf("Boot loader did not provide information about boot modules\n"); return false; } printf("Detected %d boot module(s):\n", mbi->mod_count); if (mbi->mod_count < 1) { printf("Expect at least one boot module (containing a userland image)\n"); return false; } mods_end_paddr = 0; for (i = 0; i < mbi->mod_count; i++) { printf( " module #%ld: start=0x%x end=0x%x size=0x%x name='%s'\n", i, modules[i].start, modules[i].end, modules[i].end - modules[i].start, (char *) (long)modules[i].name ); if ((sword_t)(modules[i].end - modules[i].start) <= 0) { printf("Invalid boot module size! Possible cause: boot module file not found by QEMU\n"); return false; } if (mods_end_paddr < modules[i].end) { mods_end_paddr = modules[i].end; } } mods_end_paddr = ROUND_UP(mods_end_paddr, PAGE_BITS); assert(mods_end_paddr > boot_state.ki_p_reg.end); printf("ELF-loading userland images from boot modules:\n"); load_paddr = mods_end_paddr; load_paddr = load_boot_module(modules, load_paddr); if (!load_paddr) { return false; } /* calculate final location of userland images */ ui_p_regs.start = boot_state.ki_p_reg.end; ui_p_regs.end = ui_p_regs.start + load_paddr - mods_end_paddr; printf( "Moving loaded userland images to final location: from=0x%lx to=0x%lx size=0x%lx\n", mods_end_paddr, ui_p_regs.start, ui_p_regs.end - ui_p_regs.start ); memcpy((void*)ui_p_regs.start, (void*)mods_end_paddr, ui_p_regs.end - ui_p_regs.start); /* adjust p_reg and pv_offset to final load address */ boot_state.ui_info.p_reg.start -= mods_end_paddr - ui_p_regs.start; boot_state.ui_info.p_reg.end -= mods_end_paddr - ui_p_regs.start; boot_state.ui_info.pv_offset -= mods_end_paddr - ui_p_regs.start; /* ==== following code corresponds to abstract specification after "select" ==== */ if (!platAddDevices()) { return false; } /* Total number of cores we intend to boot */ ksNumCPUs = boot_state.num_cpus; printf("Starting node #0 with APIC ID %lu\n", boot_state.cpus[0]); if (!try_boot_sys_node(boot_state.cpus[0])) { return false; } if (config_set(CONFIG_IRQ_IOAPIC)) { ioapic_init(1, boot_state.cpus, boot_state.num_ioapic); } /* initialize BKL before booting up APs */ SMP_COND_STATEMENT(clh_lock_init()); SMP_COND_STATEMENT(start_boot_aps()); /* grab BKL before leaving the kernel */ NODE_LOCK_SYS; printf("Booting all finished, dropped to user space\n"); return true; }
static BOOT_CODE bool_t try_init_kernel( paddr_t ui_p_reg_start, paddr_t ui_p_reg_end, sword_t pv_offset, vptr_t v_entry, paddr_t dtb_addr_start, paddr_t dtb_addr_end ) { cap_t root_cnode_cap; cap_t it_ap_cap; cap_t it_pd_cap; cap_t ipcbuf_cap; region_t ui_reg = paddr_to_pptr_reg((p_region_t) { ui_p_reg_start, ui_p_reg_end }); region_t dtb_reg; word_t extra_bi_size = sizeof(seL4_BootInfoHeader) + (dtb_addr_end - dtb_addr_start); region_t extra_bi_region; pptr_t extra_bi_offset = 0; vptr_t extra_bi_frame_vptr; pptr_t bi_frame_pptr; vptr_t bi_frame_vptr; vptr_t ipcbuf_vptr; create_frames_of_region_ret_t create_frames_ret; create_frames_of_region_ret_t extra_bi_ret; /* convert from physical addresses to userland vptrs */ v_region_t ui_v_reg; v_region_t it_v_reg; ui_v_reg.start = ui_p_reg_start - pv_offset; ui_v_reg.end = ui_p_reg_end - pv_offset; ipcbuf_vptr = ui_v_reg.end; bi_frame_vptr = ipcbuf_vptr + BIT(PAGE_BITS); extra_bi_frame_vptr = bi_frame_vptr + BIT(PAGE_BITS); /* If no DTB was provided, skip allocating extra bootinfo */ if (dtb_addr_start == 0) { extra_bi_size = 0; dtb_reg = (region_t) { 0, 0 }; } else { dtb_reg = paddr_to_pptr_reg((p_region_t) { dtb_addr_start, ROUND_UP(dtb_addr_end, PAGE_BITS) }); } /* The region of the initial thread is the user image + ipcbuf and boot info */ it_v_reg.start = ui_v_reg.start; it_v_reg.end = extra_bi_frame_vptr; if (it_v_reg.end > kernelBase) { printf("Userland image virtual end address too high\n"); return false; } /* setup virtual memory for the kernel */ map_kernel_window(); /* initialise the CPU */ if (!init_cpu()) { return false; } /* debug output via serial port is only available from here */ printf("Bootstrapping kernel\n"); /* initialise the platform */ init_plat(); /* make the free memory available to alloc_region() */ arch_init_freemem(ui_reg, dtb_reg); /* create the root cnode */ root_cnode_cap = create_root_cnode(); if (cap_get_capType(root_cnode_cap) == cap_null_cap) { return false; } /* create the cap for managing thread domains */ create_domain_cap(root_cnode_cap); /* initialise the IRQ states and provide the IRQ control cap */ init_irqs(root_cnode_cap); /* create the bootinfo frame */ bi_frame_pptr = allocate_bi_frame(0, CONFIG_MAX_NUM_NODES, ipcbuf_vptr); if (!bi_frame_pptr) { return false; } /* create extra bootinfo region - will return an empty allocation if extra_bi_size = 0 */ extra_bi_region = allocate_extra_bi_region(extra_bi_size); if (extra_bi_region.start == 0) { return false; } /* update initial thread virtual address range for extra bootinfo */ it_v_reg.end += extra_bi_region.end - extra_bi_region.start; if (it_v_reg.end > kernelBase) { printf("Userland extra bootinfo end address too high\n"); return false; } /* put DTB in the bootinfo block, if present. */ seL4_BootInfoHeader header; if (dtb_reg.start) { header.id = SEL4_BOOTINFO_HEADER_FDT; header.len = sizeof(header) + dtb_reg.end - dtb_reg.start; *(seL4_BootInfoHeader *)(extra_bi_region.start + extra_bi_offset) = header; extra_bi_offset += sizeof(header); memcpy((void *)(extra_bi_region.start + extra_bi_offset), (void *)dtb_reg.start, dtb_reg.end - dtb_reg.start); extra_bi_offset += dtb_reg.end - dtb_reg.start; } if ((extra_bi_region.end - extra_bi_region.start) - extra_bi_offset > 0) { /* provde a chunk for any leftover padding in the extended boot info */ header.id = SEL4_BOOTINFO_HEADER_PADDING; header.len = (extra_bi_region.end - extra_bi_region.start) - extra_bi_offset; *(seL4_BootInfoHeader *)(extra_bi_region.start + extra_bi_offset) = header; } if (config_set(CONFIG_ARM_SMMU)) { ndks_boot.bi_frame->ioSpaceCaps = create_iospace_caps(root_cnode_cap); if (ndks_boot.bi_frame->ioSpaceCaps.start == 0 && ndks_boot.bi_frame->ioSpaceCaps.end == 0) { return false; } } else { ndks_boot.bi_frame->ioSpaceCaps = S_REG_EMPTY; } /* Construct an initial address space with enough virtual addresses * to cover the user image + ipc buffer and bootinfo frames */ it_pd_cap = create_it_address_space(root_cnode_cap, it_v_reg); if (cap_get_capType(it_pd_cap) == cap_null_cap) { return false; } /* Create and map bootinfo frame cap */ create_bi_frame_cap( root_cnode_cap, it_pd_cap, bi_frame_pptr, bi_frame_vptr ); /* create and map extra bootinfo region */ if (extra_bi_size > 0) { extra_bi_ret = create_frames_of_region( root_cnode_cap, it_pd_cap, extra_bi_region, true, pptr_to_paddr((void *)extra_bi_region.start) - extra_bi_frame_vptr ); if (!extra_bi_ret.success) { return false; } ndks_boot.bi_frame->extraBIPages = extra_bi_ret.region; } /* create the initial thread's IPC buffer */ ipcbuf_cap = create_ipcbuf_frame(root_cnode_cap, it_pd_cap, ipcbuf_vptr); if (cap_get_capType(ipcbuf_cap) == cap_null_cap) { return false; } /* create all userland image frames */ create_frames_ret = create_frames_of_region( root_cnode_cap, it_pd_cap, ui_reg, true, pv_offset ); if (!create_frames_ret.success) { return false; } ndks_boot.bi_frame->userImageFrames = create_frames_ret.region; /* create/initialise the initial thread's ASID pool */ it_ap_cap = create_it_asid_pool(root_cnode_cap); if (cap_get_capType(it_ap_cap) == cap_null_cap) { return false; } write_it_asid_pool(it_ap_cap, it_pd_cap); /* create the idle thread */ if (!create_idle_thread()) { return false; } /* Before creating the initial thread (which also switches to it) * we clean the cache so that any page table information written * as a result of calling create_frames_of_region will be correctly * read by the hardware page table walker */ cleanInvalidateL1Caches(); /* create the initial thread */ tcb_t *initial = create_initial_thread( root_cnode_cap, it_pd_cap, v_entry, bi_frame_vptr, ipcbuf_vptr, ipcbuf_cap ); if (initial == NULL) { return false; } init_core_state(initial); /* create all of the untypeds. Both devices and kernel window memory */ if (!create_untypeds( root_cnode_cap, (region_t) { kernelBase, (pptr_t)ki_boot_end } /* reusable boot code/data */ )) { return false; } /* no shared-frame caps (ARM has no multikernel support) */ ndks_boot.bi_frame->sharedFrames = S_REG_EMPTY; /* finalise the bootinfo frame */ bi_finalise(); /* make everything written by the kernel visible to userland. Cleaning to PoC is not * strictly neccessary, but performance is not critical here so clean and invalidate * everything to PoC */ cleanInvalidateL1Caches(); invalidateLocalTLB(); if (config_set(CONFIG_ARM_HYPERVISOR_SUPPORT)) { invalidateHypTLB(); } ksNumCPUs = 1; /* initialize BKL before booting up other cores */ SMP_COND_STATEMENT(clh_lock_init()); SMP_COND_STATEMENT(release_secondary_cpus()); /* grab BKL before leaving the kernel */ NODE_LOCK_SYS; printf("Booting all finished, dropped to user space\n"); /* kernel successfully initialized */ return true; }