#pypp 0 // Iris: micro-kernel for a capability-based operating system. // bootstrap/init.ccp: Bootstrapping code. // Copyright 2009 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 NUM_SLOTS 8 #define NUM_CAPS 32 enum Cap_codes: SYSREQ = 1 << 16 THREAD 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: Kernel::Page page = Kernel::my_memory.create_page () page.set_flags (Kernel::Page::PAYING | Kernel::Page::FRAME, Kernel::Page::PAYING | Kernel::Page::FRAME) Kernel::my_memory.map (page, _free + rest + (p << PAGE_BITS)) Kernel::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] struct file: unsigned size // Only the first 16 characters of the name are used, because that's much easier. // This means that file names must be different in the first 16 characters if sort order matters. char name[16] String string static unsigned num_files static file *files static unsigned *index static Kernel::Memory top_memory static unsigned slot static unsigned max_pages static char *mapping static unsigned current_thread static Kernel::Caps terminals static unsigned terminal_slot static Kernel::Caps memories static unsigned memory_slot // Get the initial block device and filesystem. static Directory receive_devices (): String data Filesystem fs bool have_data = false, have_fs = false Device fs_dev, data_dev for unsigned i = 0; i < 2; ++i: Kernel::wait () if Kernel::recv.data[0].l != Parent::PROVIDE_DEVICE: kdebug ("Invalid bootstrap request.\n") Kernel::panic (0) switch Kernel::recv.data[1].l: case String::ID: if have_data: kdebug ("duplicate device.\n") Kernel::panic (0) data_dev = Kernel::get_arg () Kernel::recv.reply.invoke () have_data = true break case Filesystem::ID: if have_fs: kdebug ("duplicate filesystem.\n") Kernel::panic (0) fs_dev = Kernel::get_arg () Kernel::recv.reply.invoke () have_fs = true break default: kdebug ("unexpected device: ") kdebug_num (Kernel::recv.data[1].l) kdebug_char ('\n') Kernel::panic (0) // Initialize the root file system. data = data_dev.create_user (Kernel::my_memory) data_dev.use (data) fs = fs_dev.create_user (Kernel::my_memory) fs_dev.use (fs) Directory root = fs.use_device_ro (data.copy ()) Kernel::free_cap (data) Kernel::free_cap (fs) Kernel::free_cap (data_dev) Kernel::free_cap (fs_dev) return root // Make a list of all files. static void list_files (Directory root): Kernel::Num fullsize = root.get_size () if fullsize.h != 0: kdebug ("Too many files in bootstrap directory:") kdebug_num (fullsize.h) kdebug (":") kdebug_num (fullsize.l) kdebug ("\n") Kernel::panic (0) num_files = fullsize.l files = new file[num_files] Kernel::Caps caps = Kernel::my_memory.create_caps (num_files) unsigned slot = Kernel::alloc_slot () caps.use (slot) Kernel::free_cap (caps) for unsigned i = 0; i < num_files; ++i: String n = root.get_name (i) n.get_chars (0, files[i].name) Kernel::free_cap (n) Kernel::set_recv_arg (Kernel::Cap (slot, i)) files[i].string = root.get_file_ro (i) Kernel::Num fullsize = files[i].string.get_size () if fullsize.h != 0: kdebug ("initial file size too large: ") kdebug_num (fullsize.h) kdebug (":") kdebug_num (fullsize.l) kdebug (".\n") Kernel::panic (0) files[i].size = fullsize.l if max_pages < (fullsize.l + PAGE_SIZE - 1) >> PAGE_BITS: max_pages = (fullsize.l + PAGE_SIZE - 1) >> PAGE_BITS // Sort the list of files. static bool is_less (file *f1, file *f2): for unsigned i = 0; i < 16; ++i: if f1->name[i] < f2->name[i]: return true if f1->name[i] > f2->name[i]: return false return false // Bubble sort. static void sort (): index = new unsigned[num_files] index[0] = 0 // Invariant: index[0...f-1] is sorted. for unsigned f = 1; f < num_files; ++f: // Bubble up until top. Test for less-than, because it wraps to maxunsigned. unsigned i // Invariant: index[0...f] \ index[i+1] is sorted and index[i+1...f] is sorted. for i = f - 1; i < f; --i: if is_less (&files[index[i]], &files[f]): break index[i + 1] = index[i] index[i + 1] = f static void run (file *f, bool priv, int id): Kernel::Memory mem = Kernel::Cap (memory_slot, id) Kernel::set_recv_arg (mem) top_memory.create_memory () unsigned num_pages = (f->size + PAGE_SIZE - 1) >> PAGE_BITS for unsigned p = 0; p < num_pages; ++p: //kdebug_num (p) //kdebug ("/") //kdebug_num (num_pages) //kdebug ("\n") Kernel::set_recv_arg (Kernel::Cap (slot, p)) Kernel::my_memory.create_page () Kernel::Page (slot, p).set_flags (Kernel::Page::PAYING, Kernel::Page::PAYING) f->string.get_page (p << PAGE_BITS, Kernel::Cap (slot, p)) Kernel::my_memory.map (Kernel::Cap (slot, p), (unsigned)&mapping[p << PAGE_BITS]) Kernel::Thread thread = mem.create_thread (NUM_SLOTS) if priv: thread.make_priv () Elf32_Ehdr *header = (Elf32_Ehdr *)mapping for unsigned j = 0; j < SELFMAG; ++j: if header->e_ident[j] != ELFMAG[j]: kdebug ("invalid ELF magic\n") Kernel::panic (0) 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") Kernel::panic (0) return if header->e_ident[EI_DATA] != ELFDATA2LSB: kdebug ("invalid ELF data\n") Kernel::panic (0) return if header->e_ident[EI_VERSION] != EV_CURRENT: kdebug ("invalid ELF version\n") Kernel::panic (0) return if header->e_type != ET_EXEC: kdebug ("invalid ELF type\n") Kernel::panic (0) return if header->e_machine != EM_MIPS_RS3_LE && header->e_machine != EM_MIPS: kdebug ("invalid ELF machine\n") Kernel::panic (0) return 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): kdebug ("thread too large\n") Kernel::panic (0) 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 Kernel::Page page = mem.mapping ((void *)p) if Kernel::recv.data[0].l == Kernel::NO_ERROR: // The address already has a mapping; assume that it is correct. Kernel::free_cap (page) continue Kernel::free_cap (page) page = mem.create_page () unsigned f if readonly: f = Kernel::Page::PAYING | Kernel::Page::MAPPED_READONLY else: f = Kernel::Page::PAYING page.set_flags (f, f) Kernel::Page (slot, idx).share (page, 0) //kdebug ("mapping at ") //kdebug_num (p) //if readonly: // kdebug (" (readonly)") //kdebug ("\n") if !mem.map (page, p): kdebug ("unable to map page\n") Kernel::panic (0) return Kernel::free_cap (page) else: if readonly: kdebug ("unwritable bss section\n") Kernel::panic (0) return for unsigned p = (shdr->sh_addr & PAGE_MASK); p < shdr->sh_addr + shdr->sh_size; p += PAGE_SIZE: Kernel::Page page = mem.mapping ((void *)p) if Kernel::recv.data[0].l == Kernel::NO_ERROR: // No error means there is a mapping. Kernel::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 ((unsigned *)&mapping[p - shdr->sh_addr])[(a & ~PAGE_MASK) >> 2] = 0 else: Kernel::free_cap (page) page = mem.create_page () if Kernel::recv.data[0].l != Kernel::NO_ERROR: kdebug ("out of memory\n") Kernel::panic (0) return if !page.set_flags (Kernel::Page::PAYING | Kernel::Page::FRAME, Kernel::Page::PAYING | Kernel::Page::FRAME): kdebug ("out of memory\n") Kernel::panic (0) return if !mem.map (page, p): kdebug ("unable to map bss page\n") Kernel::panic (0) return Kernel::free_cap (page) for unsigned p = 0; p < num_pages; ++p: Kernel::my_memory.destroy (Kernel::Page (slot, p)) Kernel::Page stackpage = mem.create_page () stackpage.set_flags (Kernel::Page::PAYING | Kernel::Page::FRAME, Kernel::Page::PAYING | Kernel::Page::FRAME) if Kernel::recv.data[0].l != Kernel::NO_ERROR || !mem.map (stackpage, 0x7ffff000): kdebug ("unable to map initial stack page\n") Kernel::panic (0) return Kernel::free_cap (stackpage) Kernel::Caps caps = mem.create_caps (NUM_CAPS) thread.use (caps, 0) thread.set_info (Kernel::Thread::A0, NUM_SLOTS) thread.set_info (Kernel::Thread::A1, NUM_CAPS) Kernel::Receiver receiver = mem.create_receiver () receiver.set_owner (thread.copy ()) Kernel::Cap call = receiver.create_call_capability () Kernel::Cap parent = Kernel::my_receiver.create_capability (Kernel::Num (current_thread++, THREAD)) 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.copy ()) thread.run () Kernel::free_cap (receiver) Kernel::free_cap (thread) Kernel::free_cap (call) Kernel::free_cap (parent) Kernel::free_cap (caps) static void kdebug_name (char const *t, file *f): kdebug (t) for unsigned j = 0; j < 16; ++j: if f->name[j] != 0: kdebug_char (f->name[j]) kdebug ("...") static Device sysreq_dev struct Dev: static Dev *devs static unsigned num_devs Dev *next unsigned code, idx, id Device dev static Dev *find (unsigned c, unsigned i): for Dev *d = devs; d; d = d->next: if d->code == c && d->idx == i: return d return NULL static void add (unsigned c, unsigned i, Kernel::Cap cap): if c == Keyboard::ID && i == 1: sysreq_dev = cap return while find (c, i): ++i Dev *d = new Dev () d->next = devs d->code = c d->idx = i d->dev = cap devs = d d->id = num_devs++ static void kdebug_list (): for Dev *d = devs; d; d = d->next: kdebug (">") kdebug_num (d->code) kdebug (":") kdebug_num (d->idx) Dev *Dev::devs unsigned Dev::num_devs static void handle_init (unsigned id): while true: Kernel::wait () switch Kernel::recv.data[0].l: case Parent::PROVIDE_DEVICE: kdebug ("adding dev ") kdebug_num (Kernel::recv.data[1].l) kdebug (":") kdebug_num (Kernel::recv.data[0].h) kdebug (" as ") kdebug_num (Dev::num_devs) kdebug ("\n") Kernel::Cap reply = Kernel::get_reply () Dev::add (Kernel::recv.data[1].l, Kernel::recv.data[0].h, Kernel::get_arg ()) reply.invoke () Kernel::free_cap (reply) break case Parent::INIT_DONE: memories.set (id + num_files, Kernel::get_reply ()) return default: kdebug ("unknown init request\n") Kernel::panic (Kernel::recv.data[0].l) static void get_device (): Kernel::Cap reply = Kernel::get_reply () unsigned id = Kernel::recv.protected_data.l Dev *d = Dev::find (Kernel::recv.data[1].l, Kernel::recv.data[0].h) if d: kdebug ("giving dev ") kdebug_num (Kernel::recv.data[1].l) kdebug (":") kdebug_num (Kernel::recv.data[0].h) kdebug (" = ") kdebug_num (d->id) kdebug (" to ") kdebug_num (id) kdebug ("\n") Kernel::Cap cap = Kernel::Caps (terminal_slot, id).get (d->id) d->dev.use (cap) reply.invoke (0, 0, cap.copy ()) Kernel::free_cap (cap) else: kdebug ("device not found: ") kdebug_num (Kernel::recv.data[1].l) kdebug (":") kdebug_num (Kernel::recv.data[0].h) Dev::kdebug_list () kdebug ("\n") reply.invoke (~0, ~0) Kernel::panic (0) Kernel::free_cap (reply) Kernel::Num start (): // Wait for the debugging device to be active, in case there is one. Kernel::schedule () Dev::devs = NULL Dev::num_devs = 0 init_alloc () top_memory = Kernel::get_top_memory () Directory root = receive_devices () root.lock_ro () list_files (root) sort () Kernel::Caps caps = Kernel::my_memory.create_caps (max_pages) slot = caps.use () mapping = alloc_space (max_pages) terminals = Kernel::my_memory.create_caps (num_files) terminal_slot = terminals.use () // Two times, because it holds the memory and the init_done reply for each task. memories = Kernel::my_memory.create_caps (num_files * 2) memory_slot = memories.use () for unsigned i = 0; i < num_files; ++i: kdebug_name ("loading ", &files[index[i]]) run (&files[index[i]], files[index[i]].name[0] == '#', i) kdebug ("running\n") handle_init (i) // Notify all programs that they may start. for unsigned i = 0; i < num_files; ++i: Kernel::Cap (memory_slot, i + num_files).invoke () // create terminals. for unsigned i = 0; i < num_files; ++i: Kernel::Caps term (terminal_slot, i) Kernel::Memory mem (memory_slot, i) Kernel::set_recv_arg (term) mem.create_caps (Dev::num_devs) unsigned termslot = term.use () for Dev *d = Dev::devs; d; d = d->next: Kernel::set_recv_arg (Kernel::Cap (termslot, d->id)) // The 0x15000 is for the Display; the others don't care about it. d->dev.create_user (mem, 0, 0x15000) Kernel::free_slot (termslot) // set up system request. Keyboard sysreq = sysreq_dev.create_user (Kernel::my_memory) sysreq_dev.use (sysreq) Kernel::free_cap (sysreq_dev) Kernel::Cap cap = Kernel::my_receiver.create_capability (Kernel::Num (0, SYSREQ)) sysreq.set_cb (cap.copy ()) Kernel::free_cap (sysreq) Kernel::free_cap (cap) kdebug ("init done\n") root.unlock_ro () Kernel::free_slot (slot) Kernel::my_memory.destroy (caps) while true: Kernel::wait () switch Kernel::recv.protected_data.h: case SYSREQ: // System request. if Kernel::recv.data[0].l & Keyboard::RELEASE: continue kdebug ("system request...\n") continue case THREAD: // Request for something from a child thread. switch Kernel::recv.data[0].l: case Parent::INIT_DONE: Kernel::panic (Kernel::recv.protected_data.l, "double init_done") case Parent::PROVIDE_DEVICE: Kernel::panic (Kernel::recv.protected_data.l, "too late now for provide") case Parent::GET_DEVICE: get_device () break case Parent::EXIT: Kernel::panic (Kernel::recv.protected_data.l, "exit is not supported") default: Kernel::panic (Kernel::recv.data[0].l, "invalid operation from child") break default: Kernel::panic (Kernel::recv.protected_data.h, "unknown source of request")