/* * \brief Platform interface implementation * \author Norman Feske * \date 2009-10-02 */ /* * Copyright (C) 2009-2013 Genode Labs GmbH * * This file is part of the Genode OS framework, which is distributed * under the terms of the GNU General Public License version 2. */ /* Genode includes */ #include #include #include /* core includes */ #include #include #include /* Codezero includes */ #include using namespace Genode; enum { verbose_boot_info = true }; /* * Memory-layout information provided by the linker script */ /* virtual address range consumed by core's program image */ extern unsigned _prog_img_beg, _prog_img_end; /* physical address range occupied by core */ extern addr_t _vma_start, _lma_start; /************************** ** Boot-module handling ** **************************/ /** * Scan ROM module image for boot modules * * By convention, the boot modules start at the page after core's BSS segment. */ int Platform::_init_rom_fs() { /** * Format of module meta-data as found in the ROM module image */ struct Module { long name; /* physical address of null-terminated string */ long base; /* physical address of module data */ long size; /* size of module data in bytes */ }; /* find base address of ROM module image */ addr_t phys_base = round_page((addr_t)&_prog_img_end); /* map the first page of the image containing the module meta data */ class Out_of_virtual_memory_during_rom_fs_init { }; void *virt_base = 0; if (!_core_mem_alloc.virt_alloc()->alloc(get_page_size(), &virt_base)) throw Out_of_virtual_memory_during_rom_fs_init(); if (!map_local(phys_base, (addr_t)virt_base, 1)) { PERR("map_local failed"); return -1; } /* remove page containing module infos from physical memory allocator */ _core_mem_alloc.phys_alloc()->remove_range(phys_base, get_page_size()); /* validate the presence of a ROM image by checking the magic cookie */ const char cookie[4] = {'G', 'R', 'O', 'M'}; for (size_t i = 0; i < sizeof(cookie); i++) if (cookie[i] != ((char *)virt_base)[i]) { PERR("could not detect ROM modules"); return -2; } printf("detected ROM module image at 0x%p\n", (void *)phys_base); /* detect overly large meta data, we only support 4K */ addr_t end_of_header = ((long *)virt_base)[1]; size_t header_size = end_of_header - (long)phys_base; if (header_size > get_page_size()) { PERR("ROM fs module header exceeds %d bytes", get_page_size()); return -3; } /* start of module list */ Module *module = (Module *)((addr_t)virt_base + 2*sizeof(long)); /* * Interate over module list and populate core's ROM file system with * 'Rom_module' objects. */ for (; module->name; module++) { /* convert physical address of module name to core-local address */ char *name = (char *)(module->name - phys_base + (addr_t)virt_base); printf("ROM module \"%s\" at physical address 0x%p, size=%zd\n", name, (void *)module->base, (size_t)module->size); Rom_module *rom_module = new (core_mem_alloc()) Rom_module(module->base, module->size, name); _rom_fs.insert(rom_module); /* remove module from physical memory allocator */ _core_mem_alloc.phys_alloc()->remove_range(module->base, round_page(module->size)); } return 0; } /**************************************** ** Support for core memory management ** ****************************************/ bool Core_mem_allocator::Mapped_mem_allocator::_map_local(addr_t virt_addr, addr_t phys_addr, unsigned size) { return map_local(phys_addr, virt_addr, size / get_page_size()); } bool Core_mem_allocator::Mapped_mem_allocator::_unmap_local(addr_t virt_addr, unsigned size) { return unmap_local(virt_addr, size / get_page_size()); } /************************ ** Platform interface ** ************************/ Platform::Platform() : _io_mem_alloc(core_mem_alloc()), _io_port_alloc(core_mem_alloc()), _irq_alloc(core_mem_alloc()), _vm_base(0), _vm_size(0) { using namespace Codezero; /* init core UTCB */ static char main_utcb[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); static struct exregs_data exregs; exregs_set_utcb(&exregs, (unsigned long)&main_utcb[0]); l4_exchange_registers(&exregs, thread_myself()); /* error handling is futile at this point */ /* read number of capabilities */ int num_caps; int ret; if ((ret = l4_capability_control(CAP_CONTROL_NCAPS, 0, &num_caps)) < 0) { PERR("l4_capability_control(CAP_CONTROL_NCAPS) returned %d", ret); class Could_not_obtain_num_of_capabilities { }; throw Could_not_obtain_num_of_capabilities(); } struct capability cap_array[num_caps]; if (verbose_boot_info) printf("allocated cap array[%d] of size %d on stack\n", num_caps, sizeof(cap_array)); /* read all capabilities */ if ((ret = l4_capability_control(CAP_CONTROL_READ, 0, cap_array)) < 0) { PERR("l4_capability_control(CAP_CONTROL_READ) returned %d", ret); class Read_caps_failed { }; throw Read_caps_failed(); } /* initialize core allocators */ bool phys_mem_defined = false; addr_t dev_mem_base = 0; for (int i = 0; i < num_caps; i++) { struct capability *cap = &cap_array[i]; addr_t base = cap->start << get_page_size_log2(), size = cap->size << get_page_size_log2(); if (verbose_boot_info) printf("cap type=%x, rtype=%x, base=%lx, size=%lx\n", cap_type(cap), cap_rtype(cap), base, size); switch (cap_type(cap)) { case CAP_TYPE_MAP_VIRTMEM: /* * Use first non-UTCB virtual address range as default * virtual memory range usable for all processes. */ if (_vm_size == 0) { /* exclude page at virtual address 0 */ if (base == 0 && size >= get_page_size()) { base += get_page_size(); size -= get_page_size(); } _vm_base = base; _vm_size = size; /* add range as free range to core's virtual address allocator */ _core_mem_alloc.virt_alloc()->add_range(base, size); break; } PWRN("ignoring additional virtual address range [%lx,%lx)", base, base + size); break; case CAP_TYPE_MAP_PHYSMEM: /* * We interpret the first physical memory resource that is bigger * than typical device resources as RAM. */ enum { RAM_SIZE_MIN = 16*1024*1024 }; if (!phys_mem_defined && size > RAM_SIZE_MIN) { _core_mem_alloc.phys_alloc()->add_range(base, size); phys_mem_defined = true; dev_mem_base = base + size; } break; case CAP_TYPE_IPC: case CAP_TYPE_UMUTEX: case CAP_TYPE_IRQCTRL: case CAP_TYPE_QUANTITY: break; } } addr_t core_virt_beg = trunc_page((addr_t)&_prog_img_beg), core_virt_end = round_page((addr_t)&_prog_img_end); size_t core_size = core_virt_end - core_virt_beg; printf("core image:\n"); printf(" virtual address range [%08lx,%08lx) size=0x%zx\n", core_virt_beg, core_virt_end, core_size); printf(" physically located at 0x%08lx\n", _lma_start); /* remove core image from core's virtual address allocator */ _core_mem_alloc.virt_alloc()->remove_range(core_virt_beg, core_size); /* preserve context area in core's virtual address space */ _core_mem_alloc.virt_alloc()->raw()->remove_range(Native_config::context_area_virtual_base(), Native_config::context_area_virtual_size()); /* remove used core memory from physical memory allocator */ _core_mem_alloc.phys_alloc()->remove_range(_lma_start, core_size); /* remove magically mapped UART from core virtual memory */ _core_mem_alloc.virt_alloc()->remove_range(USERSPACE_CONSOLE_VBASE, get_page_size()); /* add boot modules to ROM fs */ if (_init_rom_fs() < 0) { PERR("initialization of romfs failed - halt."); while(1); } /* initialize interrupt allocator */ _irq_alloc.add_range(0, 255); /* regard physical addresses higher than memory area as MMIO */ _io_mem_alloc.add_range(dev_mem_base, 0x80000000 - dev_mem_base); /* * Print statistics about allocator initialization */ printf("VM area at [%08lx,%08lx)\n", _vm_base, _vm_base + _vm_size); if (verbose_boot_info) { printf(":phys_alloc: "); _core_mem_alloc.phys_alloc()->raw()->dump_addr_tree(); printf(":virt_alloc: "); _core_mem_alloc.virt_alloc()->raw()->dump_addr_tree(); printf(":io_mem_alloc: "); _io_mem_alloc.raw()->dump_addr_tree(); } } void Platform::wait_for_exit() { sleep_forever(); } void Core_parent::exit(int exit_value) { }