/* * \brief Initialzie page tables * * This includes setting up page tables for the init process. */ static void init_page_tables(void) { // Create page table for init if(hal_cpu_is_bsp()) { init_l1 = (union arm_l1_entry *)local_phys_to_mem(bsp_alloc_phys_aligned(INIT_L1_BYTES, ARM_L1_ALIGN)); memset(init_l1, 0, INIT_L1_BYTES); init_l2 = (union arm_l2_entry *)local_phys_to_mem(bsp_alloc_phys_aligned(INIT_L2_BYTES, ARM_L2_ALIGN)); memset(init_l2, 0, INIT_L2_BYTES); } else { init_l1 = (union arm_l1_entry *)local_phys_to_mem(app_alloc_phys_aligned(INIT_L1_BYTES, ARM_L1_ALIGN)); memset(init_l1, 0, INIT_L1_BYTES); init_l2 = (union arm_l2_entry *)local_phys_to_mem(app_alloc_phys_aligned(INIT_L2_BYTES, ARM_L2_ALIGN)); memset(init_l2, 0, INIT_L2_BYTES); } printf("init_page_tables done: init_l1=%p init_l2=%p\n", init_l1, init_l2); /* Map pagetables into page CN */ int pagecn_pagemap = 0; /* * ARM has: * * L1 has 4096 entries (16KB). * L2 Coarse has 256 entries (256 * 4B = 1KB). * * CPU driver currently fakes having 1024 entries in L1 and * L2 with 1024 entries by treating a page as 4 consecutive * L2 tables and mapping this as a unit in L1. */ caps_create_new(ObjType_VNode_ARM_l1, mem_to_local_phys((lvaddr_t)init_l1), vnode_objbits(ObjType_VNode_ARM_l1), 0, caps_locate_slot(CNODE(spawn_state.pagecn), pagecn_pagemap++) ); //STARTUP_PROGRESS(); // Map L2 into successive slots in pagecn size_t i; for (i = 0; i < INIT_L2_BYTES / BASE_PAGE_SIZE; i++) { size_t objbits_vnode = vnode_objbits(ObjType_VNode_ARM_l2); assert(objbits_vnode == BASE_PAGE_BITS); caps_create_new( ObjType_VNode_ARM_l2, mem_to_local_phys((lvaddr_t)init_l2) + (i << objbits_vnode), objbits_vnode, 0, caps_locate_slot(CNODE(spawn_state.pagecn), pagecn_pagemap++) ); } /* * Initialize init page tables - this just wires the L1 * entries through to the corresponding L2 entries. */ STATIC_ASSERT(0 == (INIT_VBASE % ARM_L1_SECTION_BYTES), ""); for (lvaddr_t vaddr = INIT_VBASE; vaddr < INIT_SPACE_LIMIT; vaddr += ARM_L1_SECTION_BYTES) { uintptr_t section = (vaddr - INIT_VBASE) / ARM_L1_SECTION_BYTES; uintptr_t l2_off = section * ARM_L2_TABLE_BYTES; lpaddr_t paddr = mem_to_local_phys((lvaddr_t)init_l2) + l2_off; paging_map_user_pages_l1((lvaddr_t)init_l1, vaddr, paddr); } printf("Calling paging_context_switch with address = %"PRIxLVADDR"\n", mem_to_local_phys((lvaddr_t) init_l1)); paging_context_switch(mem_to_local_phys((lvaddr_t)init_l1)); }
spawn_init(const char* name, int32_t kernel_id, const uint8_t* initrd_base, size_t initrd_bytes) { assert(0 == kernel_id); // Create page table for init init_l1 = (uintptr_t*)alloc_mem_aligned(INIT_L1_BYTES, ARM_L1_ALIGN); memset(init_l1, 0, INIT_L1_BYTES); init_l2 = (uintptr_t*)alloc_mem_aligned(INIT_L2_BYTES, ARM_L2_ALIGN); memset(init_l2, 0, INIT_L2_BYTES); STARTUP_PROGRESS(); /* Allocate bootinfo */ lpaddr_t bootinfo_phys = alloc_phys(BOOTINFO_SIZE); memset((void *)local_phys_to_mem(bootinfo_phys), 0, BOOTINFO_SIZE); STARTUP_PROGRESS(); /* Construct cmdline args */ char bootinfochar[16]; snprintf(bootinfochar, sizeof(bootinfochar), "%u", INIT_BOOTINFO_VBASE); const char *argv[] = { "init", bootinfochar }; lvaddr_t paramaddr; struct dcb *init_dcb = spawn_module(&spawn_state, name, ARRAY_LENGTH(argv), argv, bootinfo_phys, INIT_ARGS_VBASE, alloc_phys, ¶maddr); STARTUP_PROGRESS(); /* * Create a capability that allows user-level applications to * access device memory. This capability will be passed to Kaluga, * split up into smaller pieces and distributed to among device * drivers. * * For armv5, this is currently a dummy capability. We do not * have support for user-level device drivers in gem5 yet, so we * do not allocate any memory as device memory. Some cap_copy * operations in the bootup code fail if this capability is not * present. */ struct cte *iocap = caps_locate_slot(CNODE(spawn_state.taskcn), TASKCN_SLOT_IO); errval_t err = caps_create_new(ObjType_IO, 0, 0, 0, my_core_id, iocap); assert(err_is_ok(err)); struct dispatcher_shared_generic *disp = get_dispatcher_shared_generic(init_dcb->disp); struct dispatcher_shared_arm *disp_arm = get_dispatcher_shared_arm(init_dcb->disp); assert(NULL != disp); STARTUP_PROGRESS(); /* Initialize dispatcher */ disp->udisp = INIT_DISPATCHER_VBASE; STARTUP_PROGRESS(); init_dcb->vspace = mem_to_local_phys((lvaddr_t)init_l1); STARTUP_PROGRESS(); /* Page table setup */ /* Map pagetables into page CN */ int pagecn_pagemap = 0; /* * ARM has: * * L1 has 4096 entries (16KB). * L2 Coarse has 256 entries (256 * 4B = 1KB). * * CPU driver currently fakes having 1024 entries in L1 and * L2 with 1024 entries by treating a page as 4 consecutive * L2 tables and mapping this as a unit in L1. */ caps_create_new( ObjType_VNode_ARM_l1, mem_to_local_phys((lvaddr_t)init_l1), vnode_objbits(ObjType_VNode_ARM_l1), 0, my_core_id, caps_locate_slot(CNODE(spawn_state.pagecn), pagecn_pagemap++) ); STARTUP_PROGRESS(); // Map L2 into successive slots in pagecn size_t i; for (i = 0; i < INIT_L2_BYTES / BASE_PAGE_SIZE; i++) { size_t objbits_vnode = vnode_objbits(ObjType_VNode_ARM_l2); assert(objbits_vnode == BASE_PAGE_BITS); caps_create_new( ObjType_VNode_ARM_l2, mem_to_local_phys((lvaddr_t)init_l2) + (i << objbits_vnode), objbits_vnode, 0, my_core_id, caps_locate_slot(CNODE(spawn_state.pagecn), pagecn_pagemap++) ); } /* * Initialize init page tables - this just wires the L1 * entries through to the corresponding L2 entries. */ STATIC_ASSERT(0 == (INIT_VBASE % ARM_L1_SECTION_BYTES), ""); for (lvaddr_t vaddr = INIT_VBASE; vaddr < INIT_SPACE_LIMIT; vaddr += ARM_L1_SECTION_BYTES) { uintptr_t section = (vaddr - INIT_VBASE) / ARM_L1_SECTION_BYTES; uintptr_t l2_off = section * ARM_L2_TABLE_BYTES; lpaddr_t paddr = mem_to_local_phys((lvaddr_t)init_l2) + l2_off; paging_map_user_pages_l1((lvaddr_t)init_l1, vaddr, paddr); } paging_make_good((lvaddr_t)init_l1, INIT_L1_BYTES); STARTUP_PROGRESS(); printf("XXX: Debug print to make Bram's code work\n"); paging_context_switch(mem_to_local_phys((lvaddr_t)init_l1)); STARTUP_PROGRESS(); // Map cmdline arguments in VSpace at ARGS_BASE STATIC_ASSERT(0 == (ARGS_SIZE % BASE_PAGE_SIZE), ""); STARTUP_PROGRESS(); spawn_init_map(init_l2, INIT_VBASE, INIT_ARGS_VBASE, spawn_state.args_page, ARGS_SIZE, INIT_PERM_RW); STARTUP_PROGRESS(); // Map bootinfo spawn_init_map(init_l2, INIT_VBASE, INIT_BOOTINFO_VBASE, bootinfo_phys, BOOTINFO_SIZE, INIT_PERM_RW); struct startup_l2_info l2_info = { init_l2, INIT_VBASE }; genvaddr_t init_ep, got_base; load_init_image(&l2_info, initrd_base, initrd_bytes, &init_ep, &got_base); // Set startup arguments (argc, argv) disp_arm->enabled_save_area.named.r0 = paramaddr; disp_arm->enabled_save_area.named.cpsr = ARM_MODE_USR | CPSR_F_MASK; disp_arm->enabled_save_area.named.rtls = INIT_DISPATCHER_VBASE; disp_arm->enabled_save_area.named.r10 = got_base; disp_arm->got_base = got_base; struct bootinfo* bootinfo = (struct bootinfo*)INIT_BOOTINFO_VBASE; bootinfo->regions_length = 0; STARTUP_PROGRESS(); create_modules_from_initrd(bootinfo, initrd_base, initrd_bytes); debug(SUBSYS_STARTUP, "used %"PRIuCSLOT" slots in modulecn\n", spawn_state.modulecn_slot); STARTUP_PROGRESS(); create_phys_caps(&spawn_state.physaddrcn->cap, bootinfo); STARTUP_PROGRESS(); bootinfo->mem_spawn_core = ~0; // Size of kernel if bringing up others // Map dispatcher spawn_init_map(init_l2, INIT_VBASE, INIT_DISPATCHER_VBASE, mem_to_local_phys(init_dcb->disp), DISPATCHER_SIZE, INIT_PERM_RW); STARTUP_PROGRESS(); // NB libbarrelfish initialization sets up the stack. disp_arm->disabled_save_area.named.pc = init_ep; disp_arm->disabled_save_area.named.cpsr = ARM_MODE_USR | CPSR_F_MASK; disp_arm->disabled_save_area.named.rtls = INIT_DISPATCHER_VBASE; disp_arm->disabled_save_area.named.r10 = got_base; #ifdef __XSCALE__ cp15_disable_cache(); #endif printf("Kernel ready.\n"); pit_start(); // On to userland... STARTUP_PROGRESS(); dispatch(init_dcb); panic("Not reached."); }
/* * compile_vaddr returns the lowest address that is addressed by entry 'entry' * in page table 'ptable' */ errval_t compile_vaddr(struct cte *ptable, size_t entry, genvaddr_t *retvaddr) { if (!type_is_vnode(ptable->cap.type)) { return SYS_ERR_VNODE_TYPE; } genvaddr_t vaddr = 0; // shift at least by BASE_PAGE_BITS for first vaddr part size_t shift = BASE_PAGE_BITS; // figure out how much we need to shift (assuming that // compile_vaddr can be used on arbitrary page table types) // A couple of cases have fallthroughs in order to avoid having // multiple calls to vnode_objbits with the same type argument. switch (ptable->cap.type) { case ObjType_VNode_x86_64_pml4: shift += vnode_objbits(ObjType_VNode_x86_64_pdpt); case ObjType_VNode_x86_64_pdpt: shift += vnode_objbits(ObjType_VNode_x86_64_pdir); case ObjType_VNode_x86_64_pdir: shift += vnode_objbits(ObjType_VNode_x86_64_ptable); case ObjType_VNode_x86_64_ptable: break; case ObjType_VNode_x86_32_pdpt: shift += vnode_objbits(ObjType_VNode_x86_32_pdir); case ObjType_VNode_x86_32_pdir: shift += vnode_objbits(ObjType_VNode_x86_32_ptable); case ObjType_VNode_x86_32_ptable: break; case ObjType_VNode_ARM_l2: shift += vnode_objbits(ObjType_VNode_ARM_l1); case ObjType_VNode_ARM_l1: break; case ObjType_VNode_AARCH64_l0: shift += vnode_objbits(ObjType_VNode_AARCH64_l1); case ObjType_VNode_AARCH64_l1: shift += vnode_objbits(ObjType_VNode_AARCH64_l2); case ObjType_VNode_AARCH64_l2: shift += vnode_objbits(ObjType_VNode_AARCH64_l3); case ObjType_VNode_AARCH64_l3: break; default: return SYS_ERR_VNODE_TYPE; } size_t mask = (1ULL<<vnode_objbits(ptable->cap.type))-1; vaddr = ((genvaddr_t)(entry & mask)) << shift; // add next piece of virtual address until we are at root page table struct cte *old = ptable; struct cte *next, *mapping = NULL; errval_t err; while (!is_root_pt(old->cap.type)) { err = find_mapping_for_cap(old, &mapping); if (err_is_fail(err)) { // no mapping found, cannot reconstruct vaddr *retvaddr = 0; return SYS_ERR_VNODE_NOT_INSTALLED; } err = find_next_ptable(mapping, &next); // no next page table if (err == SYS_ERR_VNODE_NOT_INSTALLED || err == SYS_ERR_VNODE_LOOKUP_NEXT) { *retvaddr = 0; return SYS_ERR_VNODE_NOT_INSTALLED; } if (err_is_fail(err)) { return err; } // calculate offset into next level ptable size_t offset = mapping->cap.u.frame_mapping.entry * get_pte_size(); // shift new part of vaddr by old shiftwidth + #entries of old ptable shift += vnode_entry_bits(old->cap.type); mask = (1ULL<<vnode_objbits(next->cap.type))-1; vaddr |= ((offset & mask) << shift); old = next; } *retvaddr = vaddr; return SYS_ERR_OK; }
/** * \brief Cleanup the last cap copy for an object and the object itself */ static errval_t cleanup_last(struct cte *cte, struct cte *ret_ram_cap) { errval_t err; TRACE_CAP_MSG("cleaning up last copy", cte); struct capability *cap = &cte->cap; assert(!has_copies(cte)); if (cte->mdbnode.remote_copies) { printk(LOG_WARN, "cleanup_last but remote_copies is set\n"); } if (ret_ram_cap && ret_ram_cap->cap.type != ObjType_Null) { return SYS_ERR_SLOT_IN_USE; } struct RAM ram = { .bits = 0 }; size_t len = sizeof(struct RAM) / sizeof(uintptr_t) + 1; if (!has_descendants(cte) && !has_ancestors(cte)) { // List all RAM-backed capabilities here // NB: ObjType_PhysAddr and ObjType_DevFrame caps are *not* RAM-backed! switch(cap->type) { case ObjType_RAM: ram.base = cap->u.ram.base; ram.bits = cap->u.ram.bits; break; case ObjType_Frame: ram.base = cap->u.frame.base; ram.bits = cap->u.frame.bits; break; case ObjType_CNode: ram.base = cap->u.cnode.cnode; ram.bits = cap->u.cnode.bits + OBJBITS_CTE; break; case ObjType_Dispatcher: // Convert to genpaddr ram.base = local_phys_to_gen_phys(mem_to_local_phys((lvaddr_t)cap->u.dispatcher.dcb)); ram.bits = OBJBITS_DISPATCHER; break; default: // Handle VNodes here if(type_is_vnode(cap->type)) { ram.base = get_address(cap); ram.bits = vnode_objbits(cap->type); } break; } } err = cleanup_copy(cte); if (err_is_fail(err)) { return err; } if(ram.bits > 0) { // Send back as RAM cap to monitor if (ret_ram_cap) { if (dcb_current != monitor_ep.u.endpoint.listener) { printk(LOG_WARN, "sending fresh ram cap to non-monitor?\n"); } assert(ret_ram_cap->cap.type == ObjType_Null); ret_ram_cap->cap.u.ram = ram; ret_ram_cap->cap.type = ObjType_RAM; err = mdb_insert(ret_ram_cap); TRACE_CAP_MSG("reclaimed", ret_ram_cap); assert(err_is_ok(err)); // note: this is a "success" code! err = SYS_ERR_RAM_CAP_CREATED; } else if (monitor_ep.type && monitor_ep.u.endpoint.listener != 0) { #ifdef TRACE_PMEM_CAPS struct cte ramcte; memset(&ramcte, 0, sizeof(ramcte)); ramcte.cap.u.ram = ram; ramcte.cap.type = ObjType_RAM; TRACE_CAP_MSG("reclaimed", ret_ram_cap); #endif // XXX: This looks pretty ugly. We need an interface. err = lmp_deliver_payload(&monitor_ep, NULL, (uintptr_t *)&ram, len, false); } else { printk(LOG_WARN, "dropping ram cap base %08"PRIxGENPADDR" bits %"PRIu8"\n", ram.base, ram.bits); } if (err_no(err) == SYS_ERR_LMP_BUF_OVERFLOW) { printk(LOG_WARN, "dropped ram cap base %08"PRIxGENPADDR" bits %"PRIu8"\n", ram.base, ram.bits); err = SYS_ERR_OK; } else { assert(err_is_ok(err)); } } return err; } /* * Mark phase of revoke mark & sweep */ static void caps_mark_revoke_copy(struct cte *cte) { errval_t err; err = caps_try_delete(cte); if (err_is_fail(err)) { // this should not happen as there is a copy of the cap panic("error while marking/deleting cap copy for revoke:" " 0x%"PRIuERRV"\n", err); } }