#pypp 0 // Iris: micro-kernel for a capability-based operating system. // source/bootinit.ccp: Bootstrapping code. // Copyright 2009-2010 Bas Wijnen // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #include "devices.hh" #include "iris.hh" #include #define ELFRUN_NAME "elfrun.elf" #define INIT_NAME "init.elf" // These numbers are only used for elfrun. #define NUM_SLOTS 8 #define NUM_CAPS 32 static unsigned _free extern unsigned _end void init_alloc (): _free = ((unsigned)&_end + PAGE_SIZE - 1) & PAGE_MASK char *alloc_space (unsigned pages): unsigned ret = (_free + PAGE_SIZE - 1) & PAGE_MASK _free = ret + (pages << PAGE_BITS) return (char *)ret void *operator new[] (unsigned size): //kdebug ("new ") void *ret = (void *)_free size = (size + 3) & ~3 unsigned rest = PAGE_SIZE - (((_free - 1) & ~PAGE_MASK) + 1) if rest < size: unsigned pages = ((size - rest) + PAGE_SIZE - 1) >> PAGE_BITS for unsigned p = 0; p < pages; ++p: Iris::Page page = Iris::my_memory.create_page () page.set_flags (Iris::Page::PAYING | Iris::Page::FRAME) Iris::my_memory.map (page, _free + rest + (p << PAGE_BITS)) Iris::free_cap (page) _free += size //kdebug_num ((unsigned)ret) //kdebug ("+") //kdebug_num (size) //kdebug ("\n") return ret void *operator new (unsigned size): return new char[size] static unsigned *bss_mapping static Iris::Page bss_page // Get the initial block device and filesystem. static Iris::Directory receive_devices (): Iris::Block device bool have_device = false Iris::Cap reply[2] bool have_reply[2] have_reply[0] = false have_reply[1] = false unsigned next = 2 while true: Iris::wait () kdebug_num (Iris::recv.protected_data.l, 1) kdebug (": ") if Iris::recv.protected_data.l == 0: kdebug ("sd detect event device request\n") // SD detection event device request. // Ignore all; that will result in the driver thinking there is a card. Iris::recv.reply.invoke () continue switch Iris::recv.data[0].l: case Iris::Parent::PROVIDE_CAPABILITY: switch Iris::recv.data[1].l: case Iris::Block::ID: case Iris::WBlock::ID: // Ignore other partitions. Iris::Cap r = Iris::get_reply () if Iris::recv.data[0].h != 0: kdebug ("ignoring non-0 partition\n") else: if have_device: Iris::panic (0, "double device provided") device = Iris::get_arg () if have_reply[next - 2]: kdebug ("block provided (used)\n") reply[next++ - 2].invoke (0, 0, device.copy ()) Iris::free_cap (device) else: kdebug ("block provided (stored)\n") have_device = true r.invoke () Iris::free_cap (r) break case Iris::Directory::ID: kdebug ("directory provided\n") Iris::Directory ret = Iris::get_arg () Iris::recv.reply.invoke () return ret default: Iris::panic (Iris::recv.data[1].l, "invalid capability type provided by boot thread") break case Iris::Parent::GET_CAPABILITY: if Iris::recv.data[1].l == Iris::Event::ID: kdebug ("event requested\n") // Detection of sd card. Iris::Cap reply = Iris::get_reply () Iris::Cap event = Iris::my_receiver.create_capability (0) reply.invoke (0, 0, event.copy ()) Iris::free_cap (event) Iris::free_cap (reply) break if Iris::recv.data[1].l != Iris::Block::ID && Iris::recv.data[1].l != Iris::WBlock::ID: Iris::panic (Iris::recv.data[1].l, "invalid capability type requested by boot thread") if next == Iris::recv.protected_data.l && have_device: kdebug ("block requested (sent)\n") Iris::recv.reply.invoke (0, 0, device.copy ()) Iris::free_cap (device) have_device = false ++next else: kdebug ("block requested (not sent)\n") reply[Iris::recv.protected_data.l - 2] = Iris::get_reply () have_reply[Iris::recv.protected_data.l - 2] = true break case Iris::Parent::INIT_DONE: kdebug ("init done\n") // Ignore. Iris::recv.reply.invoke () break case Iris::Parent::EXIT: Iris::panic (Iris::recv.protected_data.l, "boot thread exits") default: Iris::panic (Iris::recv.protected_data.l, "invalid boot request") static bool stringcmp (char const *s1, char const *s2, unsigned size): for unsigned t = 0; t < size; ++t: if s1[t] != s2[t]: return false return true static Iris::Block find (Iris::Directory root, char const *name): unsigned size = 0 while name[size]: ++size Iris::Num num_files = root.get_size () for Iris::Num i = 0; i.value () < num_files.value (); i = i.value () + 1: Iris::String n = root.get_name (i) char current_name[16] n.get_chars (0, current_name) Iris::free_cap (n) if !stringcmp (current_name, name, size): continue // Found elfrun. Iris::Block ret = root.get_file_ro (i) return ret Iris::panic (0, "bootfile not found") static void run (Iris::Block data, Iris::Memory parent_memory, Iris::Cap parent): // Get the size. Iris::Num size = data.get_size () if size.value () == 0: Iris::panic (0, "elfrun is empty") // Allocate a caps with all the pages. unsigned pages = (size.value () + PAGE_SIZE - 1) >> PAGE_BITS Iris::Caps pages_caps = Iris::my_memory.create_caps (pages) unsigned slot = pages_caps.use () // Map them into the address space as well. char *mapping = alloc_space (pages) // Create a memory for the program. Iris::Memory mem = parent_memory.create_memory () // Load the file into memory and map it. for unsigned p = 0; p < pages; ++p: //kdebug_num (p) //kdebug ("/") //kdebug_num (pages) //kdebug ("\n") Iris::set_recv_arg (Iris::Cap (slot, p)) Iris::my_memory.create_page () Iris::Page (slot, p).set_flags (Iris::Page::PAYING) data.get_block (p << PAGE_BITS, PAGE_SIZE, 0, Iris::Cap (slot, p)) Iris::my_memory.map (Iris::Cap (slot, p), (unsigned)&mapping[p << PAGE_BITS]) Iris::Thread thread = mem.create_thread (NUM_SLOTS) Elf32_Ehdr *header = (Elf32_Ehdr *)mapping for unsigned j = 0; j < SELFMAG; ++j: if header->e_ident[j] != ELFMAG[j]: Iris::panic (header->e_ident[j], "invalid ELF magic") return if header->e_ident[EI_CLASS] != ELFCLASS32: kdebug ("invalid ELF class:") kdebug_num (header->e_ident[EI_CLASS]) kdebug (" != ") kdebug_num (ELFCLASS32) kdebug ("\n") Iris::panic (0) return if header->e_ident[EI_DATA] != ELFDATA2LSB: Iris::panic (header->e_ident[EI_DATA], "invalid ELF data") if header->e_ident[EI_VERSION] != EV_CURRENT: Iris::panic (header->e_ident[EI_VERSION], "invalid ELF version") if header->e_type != ET_EXEC: Iris::panic (header->e_type, "invalid ELF type") if header->e_machine != EM_MIPS_RS3_LE && header->e_machine != EM_MIPS: Iris::panic (header->e_machine, "invalid ELF machine") thread.set_pc (header->e_entry) thread.set_sp (0x80000000) for unsigned section = 0; section < header->e_shnum; ++section: Elf32_Shdr *shdr = (Elf32_Shdr *)((unsigned)mapping + header->e_shoff + section * header->e_shentsize) if ~shdr->sh_flags & SHF_ALLOC: continue bool readonly = !(shdr->sh_flags & SHF_WRITE) //bool executable = shdr->sh_flags & SHF_EXEC_INSTR if shdr->sh_type != SHT_NOBITS: unsigned file_offset = shdr->sh_offset >> PAGE_BITS if (file_offset + ((shdr->sh_size + PAGE_SIZE - 1) >> PAGE_BITS)) >= (PAGE_SIZE >> 2): Iris::panic (shdr->sh_size, "thread too large") return for unsigned p = (shdr->sh_addr & PAGE_MASK); p < shdr->sh_addr + shdr->sh_size; p += PAGE_SIZE: unsigned section_offset = (p - (shdr->sh_addr & PAGE_MASK)) >> PAGE_BITS unsigned idx = file_offset + section_offset Iris::Page page = mem.mapping ((void *)p) if Iris::recv.data[0].l == Iris::NO_ERROR: // The address already has a mapping; assume that it is correct. Iris::free_cap (page) continue Iris::free_cap (page) page = mem.create_page () unsigned f if readonly: f = Iris::Page::PAYING | Iris::Page::MAPPED_READONLY else: f = Iris::Page::PAYING page.set_flags (f) Iris::Page (slot, idx).share (page, 0) //kdebug ("mapping at ") //kdebug_num (p) //if readonly: // kdebug (" (readonly)") //kdebug ("\n") if !mem.map (page, p): Iris::panic (0, "unable to map page") return Iris::free_cap (page) else: if readonly: Iris::panic (0, "unwritable bss section") return for unsigned p = (shdr->sh_addr & PAGE_MASK); p < shdr->sh_addr + shdr->sh_size; p += PAGE_SIZE: Iris::Page page = mem.mapping ((void *)p) if Iris::recv.data[0].l == Iris::NO_ERROR: // No error means there is a mapping. page.share (bss_page, 0) Iris::free_cap (page) for unsigned a = p; a < ((p + PAGE_SIZE) & PAGE_MASK); a += 4: if a >= shdr->sh_addr + shdr->sh_size: break if a < shdr->sh_addr: continue bss_mapping[(a & ~PAGE_MASK) >> 2] = 0 else: Iris::free_cap (page) page = mem.create_page () if Iris::recv.data[0].l != Iris::NO_ERROR: Iris::panic (Iris::recv.data[0].l, "out of memory") if !page.set_flags (Iris::Page::PAYING | Iris::Page::FRAME): Iris::panic (0, "out of memory") if !mem.map (page, p): Iris::panic (0, "unable to map bss page") Iris::free_cap (page) for unsigned p = 0; p < pages; ++p: Iris::my_memory.destroy (Iris::Page (slot, p)) Iris::my_memory.destroy (pages_caps) Iris::free_slot (slot) Iris::Page stackpage = mem.create_page () stackpage.set_flags (Iris::Page::PAYING | Iris::Page::FRAME) if Iris::recv.data[0].l != Iris::NO_ERROR || !mem.map (stackpage, 0x7ffff000): Iris::panic (Iris::recv.data[0].l, "unable to map initial stack page") Iris::free_cap (stackpage) Iris::Caps caps = mem.create_caps (NUM_CAPS) thread.use (caps, 0) thread.set_info (Iris::Thread::A0, NUM_SLOTS) thread.set_info (Iris::Thread::A1, NUM_CAPS) Iris::Receiver receiver = mem.create_receiver () receiver.set_owner (thread.copy ()) Iris::Cap call = receiver.create_call_capability () caps.set (__caps_num, caps.copy ()) caps.set (__receiver_num, receiver.copy ()) caps.set (__thread_num, thread.copy ()) caps.set (__memory_num, mem.copy ()) caps.set (__call_num, call.copy ()) caps.set (__parent_num, parent) thread.run () Iris::free_cap (receiver) Iris::free_cap (thread) Iris::free_cap (call) Iris::free_cap (caps) Iris::Num start (): // Wait for the debugging device to be active, in case there is one. Iris::schedule () kdebug ("Starting bootinit\n") init_alloc () bss_mapping = (unsigned *)alloc_space (1) bss_page = Iris::my_memory.create_page () Iris::my_memory.map (bss_page, (unsigned)bss_mapping) Iris::Memory top_memory = Iris::get_top_memory () Iris::Directory root = receive_devices () root.lock_ro () kdebug_line () Iris::Block run_block = find (root, ELFRUN_NAME) kdebug_line () Iris::Cap parent_cap = Iris::my_receiver.create_capability (0) kdebug_line () run (run_block, top_memory, parent_cap) kdebug_line () Iris::wait () kdebug_line () if Iris::recv.data[0].l != Iris::Parent::PROVIDE_CAPABILITY || Iris::recv.data[1].l != Iris::Elfrun::ID: Iris::panic (0, "elfrun doesn't provide correct capability") Iris::Cap reply = Iris::get_reply () Iris::Elfrun elfrun = Iris::get_arg () Iris::my_caps.set (parent_cap.idx (), Iris::Cap (CAP_NONE)) Iris::free_cap (parent_cap) reply.invoke () Iris::free_cap (reply) parent_cap = Iris::my_receiver.create_capability (0) Iris::Block init_block = find (root, INIT_NAME) Iris::Caps init_caps = elfrun.run_block (top_memory.copy (), init_block.copy (), parent_cap.copy (), 8, 63) Iris::Thread init = init_caps.get (__thread_num) init.make_priv () init.run () Iris::free_cap (init) Iris::free_cap (init_caps) bool have_root = false bool have_elfrun = false while true: Iris::wait () switch Iris::recv.data[0].l: case Iris::Parent::GET_CAPABILITY: switch Iris::recv.data[1].l: case Iris::Directory::ID: if have_root: Iris::panic (0, "Init requests root directory twice") Iris::recv.reply.invoke (0, 0, root.copy ()) have_root = true break case Iris::Elfrun::ID: if have_elfrun: Iris::panic (0, "Init requests elfrun twice") Iris::recv.reply.invoke (0, 0, elfrun.copy ()) have_elfrun = true break default: Iris::panic (0, "Invalid device requested by init") break case Iris::Parent::INIT_DONE: if Iris::recv.data[1].value () != 0: Iris::recv.reply.invoke () break // Special response: kill boot threads. Iris::Cap reply = Iris::get_reply () root.unlock_ro () reply.invoke () Iris::free_cap (reply) top_memory.destroy (Iris::my_memory) Iris::panic (0, "bootinit should be destroyed") default: Iris::panic (Iris::recv.data[0].l, "invalid operation from init")