/// Setup the module cnode, which contains frame caps to all multiboot modules void create_module_caps(struct spawn_state *st) { errval_t err; /* Create caps for multiboot modules */ struct multiboot_modinfo *module = (struct multiboot_modinfo *)local_phys_to_mem(glbl_core_data->mods_addr); // Allocate strings area lpaddr_t mmstrings_phys = bsp_alloc_phys(BASE_PAGE_SIZE); lvaddr_t mmstrings_base = local_phys_to_mem(mmstrings_phys); lvaddr_t mmstrings = mmstrings_base; // create cap for strings area in first slot of modulecn assert(st->modulecn_slot == 0); err = caps_create_new(ObjType_Frame, mmstrings_phys, BASE_PAGE_BITS, BASE_PAGE_BITS, caps_locate_slot(CNODE(st->modulecn), st->modulecn_slot++)); assert(err_is_ok(err)); /* Walk over multiboot modules, creating frame caps */ for (int i = 0; i < glbl_core_data->mods_count; i++) { struct multiboot_modinfo *m = &module[i]; // Set memory regions within bootinfo struct mem_region *region = &bootinfo->regions[bootinfo->regions_length++]; genpaddr_t remain = MULTIBOOT_MODULE_SIZE(*m); genpaddr_t base_addr = local_phys_to_gen_phys(m->mod_start); region->mr_type = RegionType_Module; region->mr_base = base_addr; region->mrmod_slot = st->modulecn_slot; // first slot containing caps region->mrmod_size = remain; // size of image _in bytes_ region->mrmod_data = mmstrings - mmstrings_base; // offset of string in area // round up to page size for caps remain = ROUND_UP(remain, BASE_PAGE_SIZE); // Create max-sized caps to multiboot module in module cnode while (remain > 0) { assert((base_addr & BASE_PAGE_MASK) == 0); assert((remain & BASE_PAGE_MASK) == 0); // determine size of next chunk uint8_t block_size = bitaddralign(remain, base_addr); assert(st->modulecn_slot < (1UL << st->modulecn->cap.u.cnode.bits)); // create as DevFrame cap to avoid zeroing memory contents err = caps_create_new(ObjType_DevFrame, base_addr, block_size, block_size, caps_locate_slot(CNODE(st->modulecn), st->modulecn_slot++)); assert(err_is_ok(err)); // Advance by that chunk base_addr += ((genpaddr_t)1 << block_size); remain -= ((genpaddr_t)1 << block_size); } // Copy multiboot module string to mmstrings area strcpy((char *)mmstrings, MBADDR_ASSTRING(m->string)); mmstrings += strlen(MBADDR_ASSTRING(m->string)) + 1; assert(mmstrings < mmstrings_base + BASE_PAGE_SIZE); } }
/// Create physical address range or RAM caps to unused physical memory static void create_phys_caps(lpaddr_t init_alloc_addr) { errval_t err; /* Walk multiboot MMAP structure, and create appropriate caps for memory */ char *mmap_addr = MBADDR_ASSTRING(glbl_core_data->mmap_addr); genpaddr_t last_end_addr = 0; for(char *m = mmap_addr; m < mmap_addr + glbl_core_data->mmap_length;) { struct multiboot_mmap *mmap = (struct multiboot_mmap * SAFE)TC(m); debug(SUBSYS_STARTUP, "MMAP %llx--%llx Type %"PRIu32"\n", mmap->base_addr, mmap->base_addr + mmap->length, mmap->type); if (last_end_addr >= init_alloc_addr && mmap->base_addr > last_end_addr) { /* we have a gap between regions. add this as a physaddr range */ debug(SUBSYS_STARTUP, "physical address range %llx--%llx\n", last_end_addr, mmap->base_addr); err = create_caps_to_cnode(last_end_addr, mmap->base_addr - last_end_addr, RegionType_PhyAddr, &spawn_state, bootinfo); assert(err_is_ok(err)); } if (mmap->type == MULTIBOOT_MEM_TYPE_RAM) { genpaddr_t base_addr = mmap->base_addr; genpaddr_t end_addr = base_addr + mmap->length; // only map RAM which is greater than init_alloc_addr if (end_addr > local_phys_to_gen_phys(init_alloc_addr)) { if (base_addr < local_phys_to_gen_phys(init_alloc_addr)) { base_addr = local_phys_to_gen_phys(init_alloc_addr); } debug(SUBSYS_STARTUP, "RAM %llx--%llx\n", base_addr, end_addr); assert(end_addr >= base_addr); err = create_caps_to_cnode(base_addr, end_addr - base_addr, RegionType_Empty, &spawn_state, bootinfo); assert(err_is_ok(err)); } } else if (mmap->base_addr > local_phys_to_gen_phys(init_alloc_addr)) { /* XXX: The multiboot spec just says that mapping types other than * RAM are "reserved", but GRUB always maps the ACPI tables as type * 3, and things like the IOAPIC tend to show up as type 2 or 4, * so we map all these regions as platform data */ debug(SUBSYS_STARTUP, "platform %llx--%llx\n", mmap->base_addr, mmap->base_addr + mmap->length); assert(mmap->base_addr > local_phys_to_gen_phys(init_alloc_addr)); err = create_caps_to_cnode(mmap->base_addr, mmap->length, RegionType_PlatformData, &spawn_state, bootinfo); assert(err_is_ok(err)); } last_end_addr = mmap->base_addr + mmap->length; m += mmap->size + 4; } // Assert that we have some physical address space assert(last_end_addr != 0); if (last_end_addr < PADDR_SPACE_SIZE) { /* * FIXME: adding the full range results in too many caps to add * to the cnode (and we can't handle such big caps in user-space * yet anyway) so instead we limit it to something much smaller */ genpaddr_t size = PADDR_SPACE_SIZE - last_end_addr; const genpaddr_t phys_region_limit = 1ULL << 32; // PCI implementation limit if (last_end_addr > phys_region_limit) { size = 0; // end of RAM is already too high! } else if (last_end_addr + size > phys_region_limit) { size = phys_region_limit - last_end_addr; } debug(SUBSYS_STARTUP, "end physical address range %llx--%llx\n", last_end_addr, last_end_addr + size); err = create_caps_to_cnode(last_end_addr, size, RegionType_PhyAddr, &spawn_state, bootinfo); assert(err_is_ok(err)); } }
/// Create physical address range or RAM caps to unused physical memory static void create_phys_caps(lpaddr_t init_alloc_addr) { errval_t err; #ifndef __scc__ // map first meg of RAM, which contains lots of crazy BIOS tables err = create_caps_to_cnode(0, X86_32_START_KERNEL_PHYS, RegionType_PlatformData, &spawn_state, bootinfo); assert(err_is_ok(err)); #endif /* Walk multiboot MMAP structure, and create appropriate caps for memory */ char *mmap_addr = MBADDR_ASSTRING(glbl_core_data->mmap_addr); genpaddr_t last_end_addr = 0; char *clean_mmap_addr; uint32_t clean_mmap_length; cleanup_bios_regions(mmap_addr, &clean_mmap_addr, &clean_mmap_length); for(char *m = mmap_addr; m < mmap_addr + glbl_core_data->mmap_length;) { struct multiboot_mmap *mmap = (struct multiboot_mmap * SAFE)TC(m); debug(SUBSYS_STARTUP, "MMAP %llx--%llx Type %"PRIu32"\n", mmap->base_addr, mmap->base_addr + mmap->length, mmap->type); #if 0 // XXX: Remove intersecting regions bool skip = false; for(int i = 0; i < bootinfo->regions_length; i++) { struct mem_region *r = &bootinfo->regions[i]; // Remove intersecting regions (earlier additions take precedence) if((r->base + (1 << r->bits) >= mmap->base_addr && r->base + (1 << r->bits) <= mmap->base_addr + mmap->length) || (r->base >= mmap->base_addr && r->base <= mmap->base_addr + mmap->length)) { skip = true; break; } } if(skip) { continue; } #endif if (last_end_addr >= init_alloc_addr && mmap->base_addr > last_end_addr) { /* we have a gap between regions. add this as a physaddr range */ debug(SUBSYS_STARTUP, "physical address range %llx--%llx\n", last_end_addr, mmap->base_addr); err = create_caps_to_cnode(last_end_addr, mmap->base_addr - last_end_addr, RegionType_PhyAddr, &spawn_state, bootinfo); assert(err_is_ok(err)); } if (mmap->type == MULTIBOOT_MEM_TYPE_RAM) { genpaddr_t base_addr = mmap->base_addr; genpaddr_t end_addr = base_addr + mmap->length; // only map RAM which is greater than init_alloc_addr if (end_addr > local_phys_to_gen_phys(init_alloc_addr)) { if (base_addr < local_phys_to_gen_phys(init_alloc_addr)) { base_addr = local_phys_to_gen_phys(init_alloc_addr); } #ifndef CONFIG_PAE if(base_addr >= X86_32_PADDR_SPACE_SIZE) { printk(LOG_NOTE, "skipping RAM [%llx--%llx] out of " "mappable space\n", base_addr, end_addr); last_end_addr = mmap->base_addr + mmap->length; m += mmap->size + 4; continue; } if(end_addr > X86_32_PADDR_SPACE_SIZE) { printk(LOG_NOTE, "shortening RAM [%llx--%llx] to mappable " "space [0--%llx]\n", base_addr, end_addr, X86_32_PADDR_SPACE_LIMIT); end_addr = X86_32_PADDR_SPACE_SIZE; } #endif #ifndef __scc__ // XXX: Do not create ram caps for memory the kernel cannot // address to prevent kernel objects from being created there if(base_addr >= PADDR_SPACE_LIMIT) { last_end_addr = mmap->base_addr + mmap->length; m += mmap->size + 4; continue; } if (end_addr > PADDR_SPACE_LIMIT) { end_addr = PADDR_SPACE_LIMIT; } #endif debug(SUBSYS_STARTUP, "RAM %llx--%llx\n", base_addr, end_addr); assert(end_addr >= base_addr); err = create_caps_to_cnode(base_addr, end_addr - base_addr, RegionType_Empty, &spawn_state, bootinfo); assert(err_is_ok(err)); } } else if (mmap->base_addr > local_phys_to_gen_phys(init_alloc_addr)) { /* XXX: The multiboot spec just says that mapping types other than * RAM are "reserved", but GRUB always maps the ACPI tables as type * 3, and things like the IOAPIC tend to show up as type 2 or 4, * so we map all these regions as platform data */ debug(SUBSYS_STARTUP, "platform %llx--%llx\n", mmap->base_addr, mmap->base_addr + mmap->length); assert(mmap->base_addr > local_phys_to_gen_phys(init_alloc_addr)); err = create_caps_to_cnode(mmap->base_addr, mmap->length, RegionType_PlatformData, &spawn_state, bootinfo); assert(err_is_ok(err)); } last_end_addr = mmap->base_addr + mmap->length; m += mmap->size + 4; } // Assert that we have some physical address space assert(last_end_addr != 0); if (last_end_addr < X86_32_PADDR_SPACE_SIZE) { /* * FIXME: adding the full range results in too many caps to add * to the cnode (and we can't handle such big caps in user-space * yet anyway) so instead we limit it to something much smaller */ genpaddr_t size = X86_32_PADDR_SPACE_SIZE - last_end_addr; const genpaddr_t phys_region_limit = 1ULL << 32; // PCI implementation limit if (last_end_addr > phys_region_limit) { size = 0; // end of RAM is already too high! } else if (last_end_addr + size > phys_region_limit) { size = phys_region_limit - last_end_addr; } debug(SUBSYS_STARTUP, "end physical address range %llx--%llx\n", last_end_addr, last_end_addr + size); err = create_caps_to_cnode(last_end_addr, size, RegionType_PhyAddr, &spawn_state, bootinfo); assert(err_is_ok(err)); } }