/** * \file * \brief Architecture-independent bootstrap code. */ /* * Copyright (c) 2007, 2008, 2009, 2010, 2011, 2013, ETH Zurich. * All rights reserved. * * This file is distributed under the terms in the attached LICENSE file. * If you do not find this file, copies can be found by writing to: * ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group. */ #include #include #include #include #include #include #include #include #include #include #include #include struct kcb *kcb_current = NULL; coreid_t my_core_id; /// Quick way to find the base address of a cnode capability #define CNODE(cte) get_address(&(cte)->cap) /** * \brief Create caps in 'cnode' * * This function creates untyped caps to the RAM at physical address 'base_addr' * and size 'size' and adds them to a cnode for the init task. The bootinfo is * updated accordingly. * * \param base_addr The physical base address of the RAM for which caps have to * be created * \param size The size of the physical region * \param type Region type to create * \param st spawn_state structure to update * \param bootinfo bootinfo structure to update */ errval_t create_caps_to_cnode(lpaddr_t base_addr, size_t size, enum region_type type, struct spawn_state *st, struct bootinfo *bootinfo) { struct mem_region *regions = bootinfo->regions; size_t *regions_index = &bootinfo->regions_length; struct capability *cnode; cslot_t *slot; enum objtype cap_type; errval_t err; // determine destination and cap type switch(type) { case RegionType_Empty: cap_type = ObjType_RAM; cnode = &st->supercn->cap; slot = &st->supercn_slot; break; case RegionType_PhyAddr: case RegionType_PlatformData: cap_type = ObjType_PhysAddr; cnode = &st->physaddrcn->cap; slot = &st->physaddrcn_slot; break; case RegionType_RootTask: cap_type = ObjType_Frame; cnode = &st->segcn->cap; slot = &st->segcn_slot; break; default: panic("Cannot handle bootinfo region type!"); } if (*slot >= cnode_get_slots(cnode)) { printk(LOG_WARN, "create_caps_to_cnode: Cannot create more caps " "in CNode\n"); return SYS_ERR_SLOTS_IN_USE; } /* Cannot insert anymore into the mem_region */ if (*regions_index >= MAX_MEM_REGIONS) { printk(LOG_WARN, "create_caps_to_cnode: mem_region out of space\n"); return -1; } /* create the capability */ err = caps_create_new(cap_type, base_addr, size, size, my_core_id, caps_locate_slot(get_address(cnode), (*slot)++)); if (err_is_fail(err)) { return err; } /* record region */ assert(regions != NULL); regions[*regions_index].mr_base = base_addr; regions[*regions_index].mr_type = type; regions[*regions_index].mr_bytes = size; regions[*regions_index].mr_consumed = false; regions[*regions_index].mrmod_size = 0; regions[*regions_index].mrmod_data = 0; (*regions_index)++; return SYS_ERR_OK; } struct dcb *spawn_module(struct spawn_state *st, const char *name, int argc, const char** argv, lpaddr_t bootinfo, lvaddr_t args_base, alloc_phys_func alloc_phys, alloc_phys_aligned_func alloc_phys_aligned, lvaddr_t *retparamaddr) { errval_t err; printf("spawn module: %s\n", name); // check for reuse of static state #ifndef NDEBUG static bool once_only; assert(!once_only); once_only = true; #endif /* Set up root cnode and the caps it contains */ // Has to be valid after leaving this stack frame, because this CTE will // be entered into the MDB! // Don't want this to be part of the data section, as the memory backing // the data section of the kernel can and will disappear when we reboot a // core with a different kernel but want to restore the state struct cte *rootcn = &kcb_current->init_rootcn; mdb_init(kcb_current); kcb_current->is_valid = true; #if defined(CONFIG_SCHEDULER_RR) kcb_current->sched = SCHED_RR; #elif defined(CONFIG_SCHEDULER_RBED) kcb_current->sched = SCHED_RBED; #else #error invalid scheduler #endif /* create root cnode */ err = caps_create_new(ObjType_L1CNode, alloc_phys(OBJSIZE_L2CNODE), OBJSIZE_L2CNODE, OBJSIZE_L2CNODE, my_core_id, rootcn); assert(err_is_ok(err)); // on BSP core: Add BSP KCB to rootcn if (arch_core_is_bsp()) { // cannot use caps_create_new() here, as that would zero out KCB, so // we replicate the cap initialization here. struct capability bspkcb_cap; memset(&bspkcb_cap, 0, sizeof(struct capability)); bspkcb_cap.type = ObjType_KernelControlBlock; bspkcb_cap.rights = CAPRIGHTS_ALLRIGHTS; bspkcb_cap.u.kernelcontrolblock.kcb = kcb_current; // find slot in init rootcn struct cte *bspkcb = caps_locate_slot(CNODE(rootcn), ROOTCN_SLOT_BSPKCB); assert(bspkcb && bspkcb->cap.type == ObjType_Null); memcpy(&bspkcb->cap, &bspkcb_cap, sizeof(struct capability)); } // Task cnode in root cnode st->taskcn = caps_locate_slot(CNODE(rootcn), ROOTCN_SLOT_TASKCN); err = caps_create_new(ObjType_L2CNode, alloc_phys(OBJSIZE_L2CNODE), OBJSIZE_L2CNODE, OBJSIZE_L2CNODE, my_core_id, st->taskcn); assert(err_is_ok(err)); // Page cnode in root cnode st->pagecn = caps_locate_slot(CNODE(rootcn), ROOTCN_SLOT_PAGECN); err = caps_create_new(ObjType_L2CNode, alloc_phys(OBJSIZE_L2CNODE), OBJSIZE_L2CNODE, OBJSIZE_L2CNODE, my_core_id, st->pagecn); assert(err_is_ok(err)); // Base page cnode in root cnode st->basepagecn = caps_locate_slot(CNODE(rootcn), ROOTCN_SLOT_BASE_PAGE_CN); err = caps_create_new(ObjType_L2CNode, alloc_phys(OBJSIZE_L2CNODE), OBJSIZE_L2CNODE, OBJSIZE_L2CNODE, my_core_id, st->basepagecn); assert(err_is_ok(err)); // Early cnode alloc cnode in root cnode st->earlycncn = caps_locate_slot(CNODE(rootcn), ROOTCN_SLOT_EARLY_CN_CN); err = caps_create_new(ObjType_L2CNode, alloc_phys(OBJSIZE_L2CNODE), OBJSIZE_L2CNODE, OBJSIZE_L2CNODE, my_core_id, st->earlycncn); assert(err_is_ok(err)); // Super cnode in root cnode st->supercn = caps_locate_slot(CNODE(rootcn), ROOTCN_SLOT_SUPERCN); err = caps_create_new(ObjType_L2CNode, alloc_phys(OBJSIZE_L2CNODE), OBJSIZE_L2CNODE, OBJSIZE_L2CNODE, my_core_id, st->supercn); assert(err_is_ok(err)); // slot_alloc cnodes in root cnode. assumes SLOT_SLOT_ALLOC0,1,2 are // consecutive slots in root cnode. assert(ROOTCN_SLOT_SLOT_ALLOC0 + 1 == ROOTCN_SLOT_SLOT_ALLOC1); assert(ROOTCN_SLOT_SLOT_ALLOC1 + 1 == ROOTCN_SLOT_SLOT_ALLOC2); assert(ROOTCN_SLOT_SLOT_ALLOC2 + 1 == ROOTCN_SLOT_ROOT_MAPPING); st->slot_alloc_cn0 = caps_locate_slot(CNODE(rootcn), ROOTCN_SLOT_SLOT_ALLOC0); err = caps_create_new(ObjType_L2CNode, alloc_phys(4*OBJSIZE_L2CNODE), 4*OBJSIZE_L2CNODE, OBJSIZE_L2CNODE, my_core_id, st->slot_alloc_cn0); assert(err_is_ok(err)); // Seg cnode in root cnode st->segcn = caps_locate_slot(CNODE(rootcn), ROOTCN_SLOT_SEGCN); err = caps_create_new(ObjType_L2CNode, alloc_phys(OBJSIZE_L2CNODE), OBJSIZE_L2CNODE, OBJSIZE_L2CNODE, my_core_id, st->segcn); assert(err_is_ok(err)); // Physaddr cnode in root cnode st->physaddrcn = caps_locate_slot(CNODE(rootcn), ROOTCN_SLOT_PACN); err = caps_create_new(ObjType_L2CNode, alloc_phys(OBJSIZE_L2CNODE), OBJSIZE_L2CNODE, OBJSIZE_L2CNODE, my_core_id, st->physaddrcn); assert(err_is_ok(err)); if (arch_core_is_bsp()) { // Cnode for Boot loaded modules st->modulecn = caps_locate_slot(CNODE(rootcn), ROOTCN_SLOT_MODULECN); err = caps_create_new(ObjType_L2CNode, alloc_phys(OBJSIZE_L2CNODE), OBJSIZE_L2CNODE, OBJSIZE_L2CNODE, my_core_id, st->modulecn); assert(err_is_ok(err)); } /* Managing caps in task cnode */ // Dcb cap struct cte *init_dcb_cte = caps_locate_slot(CNODE(st->taskcn), TASKCN_SLOT_DISPATCHER); err = caps_create_new(ObjType_Dispatcher, alloc_phys(OBJSIZE_DISPATCHER), OBJSIZE_DISPATCHER, 0, my_core_id, init_dcb_cte); assert(err_is_ok(err)); struct dcb *init_dcb = init_dcb_cte->cap.u.dispatcher.dcb; // Copy root cnode to task cnode err = caps_copy_to_cnode(st->taskcn, TASKCN_SLOT_ROOTCN, rootcn, 0, 0, 0); assert(err_is_ok(err)); // Dispatcher frame in task cnode struct cte *init_dispframe_cte = caps_locate_slot(CNODE(st->taskcn), TASKCN_SLOT_DISPFRAME); err = caps_create_new(ObjType_Frame, alloc_phys_aligned(DISPATCHER_FRAME_SIZE, DISPATCHER_FRAME_SIZE), DISPATCHER_FRAME_SIZE, DISPATCHER_FRAME_SIZE, my_core_id, init_dispframe_cte); assert(err_is_ok(err)); // Copy dispatcher frame to the dcb struct err = caps_copy_to_cte(&init_dcb->disp_cte, init_dispframe_cte, false, 0, 0); assert(err_is_ok(err)); // Argspage in task cnode struct cte *init_args_cte = caps_locate_slot(CNODE(st->taskcn), TASKCN_SLOT_ARGSPAGE); err = caps_create_new(ObjType_Frame, alloc_phys_aligned(ARGS_SIZE, ARGS_SIZE), 1UL << ARGS_FRAME_BITS, 1UL << ARGS_FRAME_BITS, my_core_id, init_args_cte); st->args_page = gen_phys_to_local_phys(init_args_cte->cap.u.frame.base); if (arch_core_is_bsp()) { assert(bootinfo != 0); // Map bootinfo (in task cnode) struct cte *bootinfo_cte = caps_locate_slot(CNODE(st->taskcn), TASKCN_SLOT_BOOTINFO); /* DevFrame to prevent zeroing! */ /* Note: Since this is only done in the bsp, we can safely assume we * own the bootinfo memory */ err = caps_create_new(ObjType_DevFrame, bootinfo, 1UL << BOOTINFO_SIZEBITS, 1UL << BOOTINFO_SIZEBITS, my_core_id, bootinfo_cte); assert(err_is_ok(err)); } // Map kernel Cap in task cnode struct cte *kernelcap_cte = caps_locate_slot(CNODE(st->taskcn), TASKCN_SLOT_KERNELCAP); err = caps_create_new(ObjType_Kernel, 0, 0, 0, my_core_id, kernelcap_cte); assert(err_is_ok(err)); // Create capability for performance monitoring struct cte *perfmoncap_cte = caps_locate_slot(CNODE(st->taskcn), TASKCN_SLOT_PERF_MON); err = caps_create_new(ObjType_PerfMon, 0, 0, 0, my_core_id, perfmoncap_cte); assert(err_is_ok(err)); // Map IRQ table in task cnode err = caps_create_new(ObjType_IRQTable, 0, 0, 0, my_core_id, caps_locate_slot(CNODE(st->taskcn), TASKCN_SLOT_IRQ)); assert(err_is_ok(err)); // Create capability for IPI sending struct cte *ipicap_cte = caps_locate_slot(CNODE(st->taskcn), TASKCN_SLOT_IPI); err = caps_create_new(ObjType_IPI, 0, 0, 0, my_core_id, ipicap_cte); assert(err_is_ok(err)); // Create process manager capability struct cte *procmngcap_cte = caps_locate_slot(CNODE(st->taskcn), TASKCN_SLOT_PROC_MNG); err = caps_create_new(ObjType_ProcessManager, 0, 0, 0, my_core_id, procmngcap_cte); assert(err_is_ok(err)); /* Initialize dispatcher */ dispatcher_handle_t init_handle = local_phys_to_mem(init_dispframe_cte->cap.u.frame.base); struct dispatcher_shared_generic *init_disp = get_dispatcher_shared_generic(init_handle); init_disp->disabled = true; init_disp->curr_core_id = my_core_id; strncpy(init_disp->name, argv[0], DISP_NAME_LEN); /* Set fields in DCB */ // Set cspace err = caps_copy_to_cte(&init_dcb->cspace, rootcn, 0, 0, 0); assert(err_is_ok(err)); // Set disp and add to run queue init_dcb->disp = init_handle; init_dcb->disabled = true; make_runnable(init_dcb); // XXX: hack for 1:1 mapping if (args_base == 0) { args_base = st->args_page; } /* Construct args page */ struct spawn_domain_params *params = (void *)local_phys_to_mem(st->args_page); memset(params, 0, sizeof(*params)); char *buf = (char *)local_phys_to_mem(st->args_page + sizeof(struct spawn_domain_params)); size_t buflen = ARGS_SIZE - sizeof(struct spawn_domain_params); assert(argc < MAX_CMDLINE_ARGS); params->argc = argc; for (int i = 0; i < argc; i++) { size_t arglen = strlen(argv[i]); assert(arglen < buflen); params->argv[i] = (void *)(args_base + mem_to_local_phys((lvaddr_t)buf) - st->args_page); strcpy(buf, argv[i]); buf += arglen + 1; buflen -= arglen + 1; } assert(retparamaddr != NULL); *retparamaddr = args_base; /* Fill up base page CN (pre-allocated 4K pages) */ err = caps_create_new(ObjType_RAM, alloc_phys(L2_CNODE_SLOTS * BASE_PAGE_SIZE), L2_CNODE_SLOTS * BASE_PAGE_SIZE, BASE_PAGE_SIZE, my_core_id, caps_locate_slot(CNODE(st->basepagecn), 0)); assert(err_is_ok(err)); /* Fill up early cnode alloc CN (pre-allocated 16K pages) */ err = caps_create_new(ObjType_RAM, alloc_phys(EARLY_CNODE_ALLOCATED_SLOTS * OBJSIZE_L2CNODE), EARLY_CNODE_ALLOCATED_SLOTS * OBJSIZE_L2CNODE, OBJSIZE_L2CNODE, my_core_id, caps_locate_slot(CNODE(st->earlycncn), 0)); assert(err_is_ok(err)); // Store the application in the boot applications. trace_new_boot_application((char*) name, (uintptr_t) init_dcb); return init_dcb; } // Physical memory allocator for spawn_app_init lpaddr_t app_alloc_phys_start, app_alloc_phys_end; /** * Allocate physical memory during kernel startup for application cores. * Allocations are always rounded up to multiple pages. * * \param size The number of bytes to allocate. * * \return An lpaddr to the newly allocated physical memory. */ lpaddr_t app_alloc_phys(size_t size) { uint32_t npages = (size + BASE_PAGE_SIZE - 1) / BASE_PAGE_SIZE; lpaddr_t addr = app_alloc_phys_start; app_alloc_phys_start += npages * BASE_PAGE_SIZE; if (app_alloc_phys_start >= app_alloc_phys_end) { panic("Out of memory, increase CORE_DATA_PAGES"); } memset((void*)local_phys_to_mem(addr), 0, npages * BASE_PAGE_SIZE); return addr; } lpaddr_t app_alloc_phys_aligned(size_t size, size_t align) { app_alloc_phys_start = ROUND_UP(app_alloc_phys_start, align); return app_alloc_phys(size); } /** * The address from where bsp_alloc_phys will start allocating memory */ lpaddr_t bsp_init_alloc_addr = 0; /** * \brief Linear physical memory allocator. * * This function allocates a linear region of addresses of size 'size' from * physical memory. * * \param size Number of bytes to allocate. * * \return Base physical address of memory region. */ lpaddr_t bsp_alloc_phys(size_t size) { // round to base page size uint32_t npages = (size + BASE_PAGE_SIZE - 1) / BASE_PAGE_SIZE; assert(bsp_init_alloc_addr != 0); lpaddr_t addr = bsp_init_alloc_addr; bsp_init_alloc_addr += npages * BASE_PAGE_SIZE; memset((void*)local_phys_to_mem(addr), 0, npages * BASE_PAGE_SIZE); return addr; } lpaddr_t bsp_alloc_phys_aligned(size_t size, size_t align) { bsp_init_alloc_addr = ROUND_UP(bsp_init_alloc_addr, align); return bsp_alloc_phys(size); }