From 0c1dfe719b82044439f29af323fd0de558547ec3 Mon Sep 17 00:00:00 2001 From: Bas Wijnen Date: Thu, 14 Jan 2010 18:14:37 +0100 Subject: [PATCH] towards a usb file system --- .gitignore | 7 - Makefile | 4 + boot-programs/crt0.ccp | 4 +- boot-programs/init.ccp | 400 ++++++++++++++------ boot-programs/udc.ccp | 158 +++++--- bootstrap/init.ccp | 257 ------------- devices.hhp | 50 ++- invoke.ccp | 51 +-- iris.hhp | 10 +- mips/nanonote/Makefile.arch | 8 +- mips/nanonote/server/usb-server.ccp | 85 ++++- mips/nanonote/threadlist.S | 20 - {boot-programs => source}/buzzer.ccp | 0 {boot-programs => source}/charset | 0 source/crt0.ccp | 143 +++++++ {boot-programs => source}/gpio.ccp | 0 {boot-programs => source}/gpio.txt | 0 {boot-programs => source}/lcd.ccp | 0 {boot-programs => source}/metronome.ccp | 0 {boot-programs => source}/nanonote-gpio.ccp | 0 20 files changed, 690 insertions(+), 507 deletions(-) delete mode 100644 bootstrap/init.ccp rename {boot-programs => source}/buzzer.ccp (100%) rename {boot-programs => source}/charset (100%) create mode 100644 source/crt0.ccp rename {boot-programs => source}/gpio.ccp (100%) rename {boot-programs => source}/gpio.txt (100%) rename {boot-programs => source}/lcd.ccp (100%) rename {boot-programs => source}/metronome.ccp (100%) rename {boot-programs => source}/nanonote-gpio.ccp (100%) diff --git a/.gitignore b/.gitignore index 30160cd..5bf04dd 100644 --- a/.gitignore +++ b/.gitignore @@ -6,13 +6,6 @@ uimage *.elf *.cc *.hh -gpio -lcd -init -udc -buzzer -metronome -nanonote-gpio boot-programs/charset.data mips/nanonote/sdram-setup.raw nanonote-boot diff --git a/Makefile b/Makefile index a193bfc..a45cea3 100644 --- a/Makefile +++ b/Makefile @@ -46,6 +46,10 @@ PYPP = /usr/bin/pypp $(LD) $(LDFLAGS) $(filter %.o,$^) -o $@ #$(OBJCOPY) -S $(OBJCOPYFLAGS) $@ +fs/%.elf: source/crt0.o source/%.o + $(LD) $(LDFLAGS) $(filter %.o,$^) -o $@ + #$(OBJCOPY) -S $(OBJCOPYFLAGS) $@ + clean: rm -f *.o boot-programs/*.o $(BUILT_SOURCES) $(ARCH_CLEAN_FILES) diff --git a/boot-programs/crt0.ccp b/boot-programs/crt0.ccp index cd5a0d9..f0e7703 100644 --- a/boot-programs/crt0.ccp +++ b/boot-programs/crt0.ccp @@ -97,7 +97,7 @@ namespace Kernel: return Cap (0, ret - __cap_admin) extern "C": - void __main (unsigned slots, unsigned caps, list *slot_admin, list *cap_admin): + void run__main (unsigned slots, unsigned caps, list *slot_admin, list *cap_admin): __slots = slots __caps = caps __slot_admin = slot_admin @@ -137,7 +137,7 @@ __asm__ volatile ("\t.globl __start\n" "\tmove $a2, $sp\n" "\tsubu $sp, $sp, $v1\n" "\tmove $a3, $sp\n" - "\tla $t9, __main\n" + "\tla $t9, run__main\n" "\tjr $t9\n" "\tnop\n" "\t.set reorder") diff --git a/boot-programs/init.ccp b/boot-programs/init.ccp index 574907b..1c536cd 100644 --- a/boot-programs/init.ccp +++ b/boot-programs/init.ccp @@ -1,6 +1,6 @@ #pypp 0 // Iris: micro-kernel for a capability-based operating system. -// boot-programs/init.ccp: System boot manager. +// bootstrap/init.ccp: Bootstrapping code. // Copyright 2009 Bas Wijnen // // This program is free software: you can redistribute it and/or modify @@ -18,136 +18,288 @@ #include "devices.hh" #include "iris.hh" +#include -static Keyboard sysreq -static Device kbd_dev, buz_dev, backlight_dev, rootfs_dev +#define NUM_SLOTS 4 +#define NUM_CAPS 16 + +static unsigned _free +extern unsigned _end + +void init_alloc (): + _free = _end + +char *alloc_space (unsigned pages): + unsigned ret = (_free + PAGE_SIZE - 1) & PAGE_MASK + _free = ret + pages * PAGE_SIZE + return (char *)ret + +void *operator new[] (unsigned size): + void *ret = (void *)_free + size = (size + 3) & ~3 + unsigned rest = PAGE_SIZE - (size & ~PAGE_MASK) + if rest <= size: + _free += size + return ret + unsigned pages = ((size - rest) + PAGE_SIZE - 1) & PAGE_MASK + char *space = alloc_space (pages) + 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, (unsigned)&space[p << PAGE_BITS]) + Kernel::free_cap (page) + _free += size + return ret + +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 -// Event types. -enum type: - SYSREQ - KBDDEV - BUZDEV - BACKLIGHTDEV - ROOTFSDEV - -static void user_reply (Kernel::Cap target, unsigned dev): - switch dev: - case Keyboard::ID: - // keyboard user - Kernel::Cap kbd = kbd_dev.create_user (Kernel::Cap ()) - kbd_dev.use (kbd) - target.invoke (0, 0, kbd.copy ()) - Kernel::free_cap (kbd) - break - case Buzzer::ID: - // buzzer user - Kernel::Cap buzzer = buz_dev.create_user (Kernel::Cap ()) - buz_dev.use (buzzer) - target.invoke (0, 0, buzzer.copy ()) - Kernel::free_cap (buzzer) - break - default: - kdebug ("invalid id requested:") - kdebug_num (dev) - kdebug_char ('\n') - break - -static void setup (): - unsigned state = 0 - Kernel::Caps caps = Kernel::my_memory.create_caps (32) - slot = caps.use () - Kernel::Cap user - unsigned device - while true: +// Get the initial block device and filesystem. +static Directory receive_devices (): + String data + Filesystem fs + bool have_data = false, have_fs = false + for unsigned i = 0; i < 2; ++i: + Device dev Kernel::wait () - Kernel::Cap reply = Kernel::get_reply () - Kernel::Cap arg = Kernel::get_arg () - switch Kernel::recv.data[0].l: - case Parent::PROVIDE_DEVICE: - switch Kernel::recv.data[1].l: - case Keyboard::ID: - switch Kernel::recv.data[0].h: - case 0: - caps.set (KBDDEV, arg.copy ()) - kbd_dev = Kernel::Cap (slot, KBDDEV) - break - case 1: - caps.set (SYSREQ, arg.copy ()) - sysreq = Kernel::Cap (slot, SYSREQ) - break - default: - kdebug ("unexpected keyboard\n") - break - reply.invoke () - Kernel::free_cap (reply) - break - case Buzzer::ID: - caps.set (BUZDEV, arg.copy ()) - buz_dev = Kernel::Cap (slot, BUZDEV) - reply.invoke () - Kernel::free_cap (reply) - break - case Setting::ID: - caps.set (BACKLIGHTDEV, arg.copy ()) - backlight_dev = Kernel::Cap (slot, BACKLIGHTDEV) - reply.invoke () - Kernel::free_cap (reply) - break - case Directory::ID: - caps.set (ROOTFSDEV, arg.copy ()) - rootfs_dev = Kernel::Cap (slot, ROOTFSDEV) - reply.invoke () - Kernel::free_cap (reply) - break - default: - kdebug ("unexpected device: ") - kdebug_num (Kernel::recv.data[1].l) - kdebug_char ('\n') - break + 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) + dev = Kernel::get_arg () + have_data = true + Kernel::recv.reply.invoke () + data = dev.create_user (Kernel::my_memory) + dev.use (data) + Kernel::free_cap (dev) break - case Parent::GET_DEVICE: - user = reply - device = Kernel::recv.data[1].l + case Filesystem::ID: + if have_fs: + kdebug ("duplicate filesystem.\n") + Kernel::panic (0) + dev = Kernel::get_arg () + have_fs = true + fs = dev.create_user (Kernel::my_memory) + dev.use (fs) + Kernel::free_cap (dev) + Kernel::recv.reply.invoke () break default: - kdebug ("unknown setup request for init\n") - reply.invoke () - Kernel::free_cap (reply) - Kernel::free_cap (arg) - continue - Kernel::free_cap (arg) - if ++state == 6: - break - // sysreq - Kernel::Cap cb = Kernel::my_receiver.create_capability (SYSREQ) - sysreq.set_cb (cb.copy ()) - Kernel::free_cap (cb) - // First user reply. - user_reply (user, device) - Kernel::free_cap (user) + kdebug ("unexpected device: ") + kdebug_num (Kernel::recv.data[1].l) + kdebug_char ('\n') + Kernel::panic (0) + // Initialize the root file system. + Directory root = fs.use_device_ro (data.copy ()) + Kernel::free_cap (data) + Kernel::free_cap (fs) + 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.\n") + Kernel::panic (0) + num_files = fullsize.l + files = new file[num_files] + Kernel::Caps caps = Kernel::my_memory.create_caps (num_files * 2) + unsigned slot = Kernel::alloc_slot () + caps.use (slot) + for unsigned i = 0; i < num_files; ++i: + Kernel::set_recv_arg (Kernel::Cap (slot, i * 2)) + String n = root.get_name (i) + n.get_chars (0, files[i].name) + Kernel::set_recv_arg (Kernel::Cap (slot, i * 2 + 1)) + 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.\n") + Kernel::panic (0) + files[i].size = fullsize.l + if max_pages < (fullsize.l + PAGE_SIZE - 1) & PAGE_MASK: + max_pages = (fullsize.l + PAGE_SIZE - 1) & PAGE_MASK + +// 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): + Kernel::Memory mem = top_memory.create_memory () + unsigned num_pages = (f->size + PAGE_SIZE - 1) & PAGE_MASK + for unsigned p = 0; p < num_pages; ++p: + Kernel::set_recv_arg (Kernel::Cap (slot, p)) + mem.create_page () + 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\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 + page = mem.create_page () + Kernel::Page (slot, idx).share (page, 0) + if !mem.map (page, p, readonly): + 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 no mapping. + 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) + else: + 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])[(a & ~PAGE_MASK) >> 2] = 0 + for unsigned p = 0; p <= num_pages; ++p: + mem.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 (++current_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 (mem) + Kernel::free_cap (call) + Kernel::free_cap (parent) Kernel::Num start (): + // Wait for the debugging device to be active, in case there is one. Kernel::schedule () - setup () - // claim backlight - Setting backlight = backlight_dev.create_user (Kernel::Cap ()) - backlight_dev.use (backlight) - bool backlight_state = true - while true: - Kernel::wait () - switch Kernel::recv.protected_data.value (): - case SYSREQ: - unsigned code = Kernel::recv.data[0].l - if !(code & Keyboard::RELEASE): - backlight_state = !backlight_state - backlight.set (backlight_state ? ~0 : 0) - break - default: - if Kernel::recv.data[0].l != Parent::GET_DEVICE: - kdebug ("invalid call from user\n") - break - Kernel::Cap reply = Kernel::get_reply () - user_reply (reply, Kernel::recv.data[1].l) - Kernel::free_cap (reply) - break + 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) + for unsigned i = 0; i < num_files; ++i: + run (&files[index[i]], files[index[i]].name[0] == '#') + root.unlock_ro () + Kernel::free_slot (slot) + Kernel::my_memory.destroy (caps) + return 0 diff --git a/boot-programs/udc.ccp b/boot-programs/udc.ccp index 797ab52..95d9daa 100644 --- a/boot-programs/udc.ccp +++ b/boot-programs/udc.ccp @@ -150,17 +150,19 @@ class Udc: char configuration unsigned size char const *ptr + unsigned cmd_code, cmd_arg bool rebooting - bool vendor (Setup *s) + bool vendor (Setup *s, unsigned cmd) bool get_descriptor (unsigned type, unsigned idx, unsigned len) - bool handle_setup (Setup *s) + bool handle_setup (Setup *s, unsigned cmd) char log_buffer[1000] unsigned log_buffer_size unsigned log_buffer_start public: void init () void log (unsigned c) - void interrupt () + void interrupt (unsigned cmd) + void send (unsigned code, unsigned arg) Udc::Device Udc::device_descriptor = { sizeof (Device), Device::Type, 0x200, 0, 0, 0, max_packet_size0, 0xfffe, 0x0002, 0x100, 1, 2, 0, 1 } Udc::my_config Udc::config_descriptor = { @@ -198,6 +200,8 @@ void Udc::init (): s_product = (String <16>){ sizeof (String <16>), String <16>::Type, { 'I', 'r', 'i', 's', ' ', 'o', 'n', ' ', 'N', 'a', 'n', 'o', 'N', 'o', 't', 'e' } } log_buffer_size = 0 log_buffer_start = 0 + cmd_code = ~0 + cmd_arg = 0 // Disconnect from the bus and don't try to get high-speed. UDC_POWER &= ~(UDC_POWER_SOFTCONN | UDC_POWER_HSENAB) @@ -221,21 +225,35 @@ void Udc::init (): // Connect to the host. UDC_POWER |= UDC_POWER_SOFTCONN -bool Udc::vendor (Setup *s): +bool Udc::vendor (Setup *s, unsigned cmd): if !(s->request_type & 0x80): + switch s->request: + case Directory::GET_SIZE: + //TODO + case Directory::GET_NAME: + //TODO + default: + kdebug ("invalid vendor request\n") + Kernel::panic (0) return true if s->request == 10: - static char b[2] - ptr = b - size = s->length < 2 ? s->length : 2 - b[0] = '#' - if log_buffer_start == log_buffer_size: - size = 1 + static unsigned b[2] + ptr = (char *)b + size = s->length < 8 ? s->length : 8 + if cmd_code != ~0: + b[0] = cmd_code + b[1] = cmd_arg + cmd_code = ~0 else: - b[1] = log_buffer[log_buffer_start++] - if log_buffer_start == log_buffer_size: - log_buffer_start = 0 - log_buffer_size = 0 + if log_buffer_start == log_buffer_size: + b[0] = ~1 + b[1] = 0 + else: + b[0] = ~0 + b[1] = (log_buffer[log_buffer_start++] & 0xff) * 0x01010101 + if log_buffer_start == log_buffer_size: + log_buffer_start = 0 + log_buffer_size = 0 else: static char const *name = "Reboot" ptr = name @@ -244,6 +262,13 @@ bool Udc::vendor (Setup *s): state = TX return true +void Udc::send (unsigned code, unsigned arg): + if code != ~0: + kdebug ("new code sent while old one wasn't finished.\n") + Kernel::panic (0) + cmd_code = code + cmd_arg = arg + bool Udc::get_descriptor (unsigned type, unsigned idx, unsigned len): switch type: case Configuration::Type: @@ -265,7 +290,7 @@ bool Udc::get_descriptor (unsigned type, unsigned idx, unsigned len): //size = (len < sizeof (device_qualifier_descriptor) ? len : sizeof (device_qualifier_descriptor)) //break return false - // The 6 is an arbitrary number, except that String <6> in instantiated already. + // The 6 is an arbitrary number, except that String <6> is instantiated already. case String <6>::Type: switch idx: case 0: @@ -288,7 +313,7 @@ bool Udc::get_descriptor (unsigned type, unsigned idx, unsigned len): state = TX return true -bool Udc::handle_setup (Setup *s): +bool Udc::handle_setup (Setup *s, unsigned cmd): switch s->request_type: case STANDARD_TO_DEVICE: switch s->request: @@ -331,12 +356,12 @@ bool Udc::handle_setup (Setup *s): break case VENDOR_TO_DEVICE: case VENDOR_FROM_DEVICE: - return vendor (s) + return vendor (s, cmd) default: return false return true -void Udc::interrupt (): +void Udc::interrupt (unsigned cmd): unsigned i = UDC_INTRUSB if i & UDC_INTR_RESET: state = IDLE @@ -361,7 +386,7 @@ void Udc::interrupt (): packet.d[0] = UDC_FIFO (0) packet.d[1] = UDC_FIFO (0) UDC_CSR0 = csr | UDC_CSR0_DATAEND | UDC_CSR0_SVDOUTPKTRDY - if !handle_setup (&packet.s): + if !handle_setup (&packet.s, cmd): csr |= UDC_CSR0_SENDSTALL break if size == 0: @@ -395,6 +420,8 @@ void Udc::log (unsigned c): enum pdata: LOG = 32 FS + DATA + DIRECTORY Kernel::Num start (): map_udc () @@ -408,52 +435,28 @@ Kernel::Num start (): Kernel::register_interrupt (IRQ_UDC) Device fs_dev = Kernel::my_receiver.create_capability (FS) Kernel::my_parent.provide_device (fs_dev) - Kernel::Num current_user = 0 + unsigned data_current_user = 0, fs_current_user = 0 unsigned next_user + unsigned state = 0 while true: Kernel::wait () + Kernel::Cap reply = Kernel::get_reply () switch Kernel::recv.protected_data.h: case 0: switch Kernel::recv.protected_data.l: case IRQ_UDC: - udc.interrupt () + udc.interrupt (state) Kernel::register_interrupt (IRQ_UDC) break case LOG: udc.log (Kernel::recv.data[0].l) break case FS: - switch Kernel::recv.data[0].l: - case Device::CREATE_USER: - Kernel::Cap reply = Kernel::get_reply () - Kernel::Cap c = Kernel::my_receiver.create_capability (Kernel::Num (next_user++, FS)) - reply.invoke (0, 0, c.copy ()) - Kernel::free_cap (c) - Kernel::free_cap (reply) - break - case Device::DESTROY_USER: - Kernel::Cap reply = Kernel::get_reply () - reply.invoke () - Kernel::free_cap (reply) - break - case Device::USE: - Kernel::Cap reply = Kernel::get_reply () - Kernel::Cap arg = Kernel::get_arg () - current_user = Kernel::my_receiver.get_protected (arg) - reply.invoke () - Kernel::free_cap (reply) - Kernel::free_cap (arg) - break - case Device::UNUSE: - Kernel::Cap reply = Kernel::get_reply () - Kernel::Cap arg = Kernel::get_arg () - if current_user.value () == Kernel::my_receiver.get_protected (arg).value (): - current_user = 0 - reply.invoke () - Kernel::free_cap (reply) - Kernel::free_cap (arg) - break - break + Device::host (FS, fs_current_user, reply) + continue + case DATA: + Device::host (DATA, data_current_user, reply) + continue default: udc.log ('~') char digit[] = "0123456789abcdef" @@ -461,18 +464,55 @@ Kernel::Num start (): udc.log (digit[(Kernel::recv.protected_data.l >> (4 * (7 - i))) & 0xf]) udc.log ('\n') break - case FS: - if current_user.value () != Kernel::recv.protected_data.value (): + case DATA: + if data_current_user != Kernel::recv.protected_data.l: break + switch Kernel::recv.data[0].l: + case String::GET_SIZE: + case String::GET_CHARS: + reply.invoke (0) + Kernel::free_cap (reply) + continue + case String::GET_PAGE: + default: + reply.invoke (Kernel::ERR_INVALID_OPERATION) + Kernel::free_cap (reply) + continue + break + case FS: + if fs_current_user != Kernel::recv.protected_data.l: + break + switch Kernel::recv.data[0].l: + case Filesystem::USE_DEVICE: + case Filesystem::USE_DEVICE_RO: + Directory dir = Kernel::my_receiver.create_capability (Kernel::Num (0, DIRECTORY)) + reply.invoke (0, 0, dir.copy ()) + Kernel::free_cap (dir) + Kernel::free_cap (reply) + continue + default: + reply.invoke (Kernel::ERR_INVALID_OPERATION) + Kernel::free_cap (reply) + continue + case DIRECTORY: switch Kernel::recv.data[0].l: case Directory::GET_SIZE: case Directory::GET_NAME: - case Directory::GET_FILE_RO: case Directory::LOCK_RO: case Directory::UNLOCK_RO: - Kernel::recv.reply.invoke () - break + state = Kernel::recv.data[0].l + if Kernel::recv.data[1].h != 0: + kdebug ("index out of supported range\n") + Kernel::panic (0) + udc.send (Kernel::recv.data[0].l, Kernel::recv.data[1].l) + Kernel::free_cap (reply) + continue + case Directory::GET_FILE_RO: + //TODO case Directory::GET_FILE_INFO: default: - Kernel::recv.reply.invoke (~0) - break + reply.invoke (Kernel::ERR_INVALID_OPERATION) + Kernel::free_cap (reply) + continue + reply.invoke () + Kernel::free_cap (reply) diff --git a/bootstrap/init.ccp b/bootstrap/init.ccp deleted file mode 100644 index a70fb3c..0000000 --- a/bootstrap/init.ccp +++ /dev/null @@ -1,257 +0,0 @@ -#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" - -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 - -// Get the initial block device and filesystem. -static Directory receive_devices (): - String dev - Filesystem fs - 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 dev: - kdebug ("duplicate device.\n") - Kernel::panic (0) - dev = Kernel::get_arg () - Kernel::recv.reply.invoke () - break - case Filesystem::ID: - if fs: - kdebug ("duplicate filesystem.\n") - Kernel::panic (0) - fs = Kernel::get_arg () - Kernel::recv.reply.invoke () - break - default: - kdebug ("unexpected device: ") - kdebug_num (Kernel::recv.data[1].l) - kdebug_char ('\n') - Kernel::panic (0) - // Initialize the root file system. - Directory root = fs.use_device (dev.copy ()) - Kernel::free_cap (dev) - Kernel::free_cap (fs) - 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.\n") - Kernel::panic (0) - num_files = fullsize.l - files = new file[num_files] - Kernel::Caps caps = Kernel::my_memory.create_caps (num_files * 2) - unsigned slot = Kernel::alloc_slot () - caps.use (slot) - for unsigned i = 0; i < num_files; ++i: - Kernel::set_recv_arg (Kernel::Cap (slot, i * 2)) - String n = root.get_name (i) - n.get_chars (0, files[i].name) - Kernel::set_recv_arg (Kernel::Cap (slot, i * 2 + 1)) - 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.\n") - Kernel::panic (0) - files[i].size = fullsize.l - if max_pages < (fullsize.l + PAGE_SIZE - 1) & PAGE_MASK: - max_pages = (fullsize.l + PAGE_SIZE - 1) & PAGE_MASK - -// Sort the list of files. -static bool is_less (file *f1, file *f2): - return - -// 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): - Kernel::Memory mem = top_memory.create_memory () - unsigned num_pages = (f->size + PAGE_SIZE - 1) & PAGE_MASK - for unsigned p = 0; p < num_pages; ++p: - Kernel::set_recv_arg (Kernel::Cap (slot, p)) - mem.create_page () - 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\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 *)(thread_start[i] + 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 (p) - if page: - // The address already has a mapping; assume that it is correct. - Kernel::free_cap (page) - continue - page = mem.create_page () - Kernel::Cap (slot, idx).share (page, 0) - if !mem.map (page, p, readonly): - 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: - kPage *page = mem->get_mapping (p) - if !page: - page = mem.create_page () - if !page: - 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) - else: - 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[idx << PAGE_BITS])[(a & ~PAGE_MASK) >> 2] = 0 - for unsigned p = 0; p <= num_pages; ++p: - mem.destroy (Kernel::Cap (slot, p)) - Kernel::Page stackpage = mem.create_page () - stackpage.set_flags (Kernel::Page::PAYING | Kernel::Page::FRAME, Kernel::Page::PAYING | Kernel::Page::FRAME) - if !stackpage || !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 (++current_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 (mem) - Kernel::free_cap (call) - Kernel::free_cap (parent) - -Kernel::Num start (): - // Wait for the debugging device to be active, in case there is one. - Kernel::schedule () - 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) - for unsigned i = 0; i < num_files; ++i: - run (files[i], files[i].name[0] == '#') - root.unlock_ro () - Kernel::free_slot (slot) - Kernel::my_memory.destroy (caps) - return 0 diff --git a/devices.hhp b/devices.hhp index 73950e3..6412b80 100644 --- a/devices.hhp +++ b/devices.hhp @@ -98,6 +98,40 @@ struct Device : public Kernel::Cap: // Make user active. It makes the previous active user inactive. void use (Kernel::Cap user): ocall (user, CAP_MASTER_DIRECT | USE) + // Convenience function for threads implementing a device. + static void host (unsigned id, unsigned ¤t_user, Kernel::Cap &reply): + static unsigned last_user + switch Kernel::recv.data[0].l: + case Device::CREATE_USER: + // Increment last_user; skip 0. + // FIXME: if this really wraps, it is possible that two users share their id. + if !++last_user: + ++last_user + Kernel::Cap c = Kernel::my_receiver.create_capability (Kernel::Num (last_user, id)) + reply.invoke (0, 0, c.copy ()) + Kernel::free_cap (c) + Kernel::free_cap (reply) + break + case Device::DESTROY_USER: + reply.invoke () + Kernel::free_cap (reply) + break + case Device::USE: + Kernel::Cap arg = Kernel::get_arg () + current_user = Kernel::my_receiver.get_protected (arg).l + reply.invoke () + Kernel::free_cap (reply) + Kernel::free_cap (arg) + break + case Device::UNUSE: + Kernel::Cap arg = Kernel::get_arg () + Kernel::Num p = Kernel::my_receiver.get_protected (arg) + if p.h == id && current_user == p.l: + current_user = 0 + reply.invoke () + Kernel::free_cap (reply) + Kernel::free_cap (arg) + break // Interface for talking to the parent process. struct Parent : public Kernel::Cap: @@ -270,11 +304,25 @@ struct WDirectory : public Directory: void unlock (): call (CAP_MASTER_DIRECT | UNLOCK) +// A filesystem turns a String into a Directory. +struct Filesystem : public Device: + Filesystem (Kernel::Cap c = Kernel::Cap ()) : Device (c): + enum request: + USE_DEVICE = WDirectory::ID + USE_DEVICE_RO + ID + WDirectory use_device (WString dev): + ocall (dev, CAP_MASTER_DIRECT | USE_DEVICE) + return Kernel::get_arg () + Directory use_device_ro (String dev): + ocall (dev, CAP_MASTER_DIRECT | USE_DEVICE_RO) + return Kernel::get_arg () + // Stream interface. struct Stream : public Kernel::Cap: Stream (Kernel::Cap c = Kernel::Cap ()) : Kernel::Cap (c): enum request: - READ = WDirectory::ID + READ = Filesystem::ID WRITE ID // Try to read size bytes. Returns the number of bytes successfully read. diff --git a/invoke.ccp b/invoke.ccp index 40098b9..1cf22c5 100644 --- a/invoke.ccp +++ b/invoke.ccp @@ -194,7 +194,7 @@ static void receiver_invoke (unsigned cmd, unsigned target, Kernel::Num protecte switch cmd: case Kernel::Receiver::SET_OWNER & REQUEST_MASK: if !c->arg.valid (): - reply_num (~0) + reply_num (Kernel::ERR_INVALID_ARGUMENT) return unsigned cap = (unsigned)c->arg->target if cap != (CAPTYPE_THREAD | CAP_MASTER) && cap != (CAPTYPE_THREAD | Kernel::Thread::SET_OWNER): @@ -211,7 +211,7 @@ static void receiver_invoke (unsigned cmd, unsigned target, Kernel::Num protecte case Kernel::Receiver::GET_PROTECTED & REQUEST_MASK: if !c->arg.valid () || c->arg->target != receiver: dpanic (0, "wrong argument for get_protected") - reply_num (~0) + reply_num (Kernel::ERR_INVALID_ARGUMENT) return reply_num (c->arg->protected_data) return @@ -303,12 +303,12 @@ static void memory_invoke (unsigned cmd, unsigned target, Kernel::Num protected_ return default: dpanic (0, "invalid create type") - reply_num (~0) + reply_num (Kernel::ERR_INVALID_ARGUMENT) return break case Kernel::Memory::DESTROY & REQUEST_MASK: if !c->arg.valid () || (unsigned)c->arg->target & ~KERNEL_MASK || !c->arg->target || ((kObject *)c->arg->protected_data.l)->address_space != mem: - reply_num (~0) + reply_num (Kernel::ERR_INVALID_ARGUMENT) return switch (unsigned)c->arg->target & CAPTYPE_MASK: case CAPTYPE_RECEIVER: @@ -337,12 +337,12 @@ static void memory_invoke (unsigned cmd, unsigned target, Kernel::Num protected_ // FIXME: this should work for fake pages as well. if !c->arg.valid () || (unsigned)c->arg->target & ~KERNEL_MASK || ((unsigned)c->arg->target & CAPTYPE_MASK) != CAPTYPE_PAGE: dpanic (0x22993341, "Trying to map non-page") - reply_num (~0) + reply_num (Kernel::ERR_INVALID_ARGUMENT) return kPage *page = (kPage *)c->arg->protected_data.l if page->address_space != mem: dpanic (0x52993341, "Trying to map foreign page") - reply_num (~0) + reply_num (Kernel::ERR_INVALID_ARGUMENT) return bool readonly = c->data[1].l & (unsigned)c->arg->target & Kernel::Page::READONLY mem->map (page, c->data[1].l & PAGE_MASK, readonly) @@ -350,6 +350,9 @@ static void memory_invoke (unsigned cmd, unsigned target, Kernel::Num protected_ case Kernel::Memory::MAPPING & REQUEST_MASK: bool readonly kPage *page = mem->get_mapping (c->data[1].l, &readonly) + if !page: + reply_num (Kernel::ERR_UNMAPPED_READ) + return unsigned t = CAPTYPE_PAGE | CAP_MASTER if readonly: t |= Kernel::Page::READONLY @@ -421,12 +424,12 @@ static void thread_invoke (unsigned cmd, unsigned target, Kernel::Num protected_ if c->data[1].l >= thread->slots || !c->arg.valid (): dbg_send (5, 3) dpanic (c->data[1].l, "no argument given for USE_SLOT") - reply_num (~0) + reply_num (Kernel::ERR_INVALID_ARGUMENT) return // FIXME: This doesn't allow using a fake caps. if (unsigned)c->arg->target != (CAPTYPE_CAPS | CAP_MASTER) && (unsigned)c->arg->target != (CAPTYPE_CAPS | Kernel::Caps::USE): dpanic (0, "argument for USE_SLOT is not a caps") - reply_num (~0) + reply_num (Kernel::ERR_INVALID_ARGUMENT) return unsigned slot = c->data[1].l kCaps *new_caps = (kCaps *)c->arg->protected_data.l @@ -465,14 +468,14 @@ static void thread_invoke (unsigned cmd, unsigned target, Kernel::Num protected_ return case Kernel::Thread::PRIV_MAKE_PRIV & REQUEST_MASK: if !c->arg.valid () || ((unsigned)c->arg->target) & ~REQUEST_MASK != CAPTYPE_THREAD: - reply_num (~0) + reply_num (Kernel::ERR_INVALID_ARGUMENT) return ((kThread *)c->arg->protected_data.l)->flags |= Kernel::Thread::PRIV break case Kernel::Thread::PRIV_ALLOC_RANGE & REQUEST_MASK: if !c->arg.valid () || ((unsigned)c->arg->target) & ~REQUEST_MASK != CAPTYPE_MEMORY: panic (0x54365435, "non-memory argument to alloc_range") - reply_num (~0) + reply_num (Kernel::ERR_INVALID_ARGUMENT) return kMemory *mem = (kMemory *)c->arg->protected_data.l if !mem->use (c->data[1].l): @@ -490,11 +493,11 @@ static void thread_invoke (unsigned cmd, unsigned target, Kernel::Num protected_ case Kernel::Thread::PRIV_ALLOC_PHYSICAL & REQUEST_MASK: if !c->arg.valid (): panic (0x71342134, "no argument provided for alloc physical") - reply_num (~0) + reply_num (Kernel::ERR_INVALID_ARGUMENT) return if ((unsigned)c->arg->target & ~REQUEST_MASK) != CAPTYPE_PAGE: panic (0x21342134, "no page provided for alloc physical") - reply_num (~0) + reply_num (Kernel::ERR_INVALID_ARGUMENT) return kPage *page = (kPage *)c->arg->protected_data.l page->forget () @@ -519,7 +522,7 @@ static void thread_invoke (unsigned cmd, unsigned target, Kernel::Num protected_ case Kernel::Thread::PRIV_PHYSICAL_ADDRESS & REQUEST_MASK: if !c->arg.valid () || ((unsigned)c->arg->target) & ~REQUEST_MASK != CAPTYPE_PAGE: dpanic (0x99049380, "invalid page for physical address") - reply_num (~0) + reply_num (Kernel::ERR_INVALID_ARGUMENT) return kPage *page = (kPage *)c->arg->protected_data.l reply_num (page->frame & ~0xc0000000) @@ -571,11 +574,11 @@ static void page_invoke (unsigned cmd, unsigned target, Kernel::Num protected_da case Kernel::Page::SHARE & REQUEST_MASK: if !c->arg.valid (): // Cannot share without a target page. - reply_num (~0) + reply_num (Kernel::ERR_INVALID_ARGUMENT) return if ((unsigned)c->arg->target & ~REQUEST_MASK) != CAPTYPE_PAGE: // FIXME: This makes it impossible to use a fake kPage capability. - reply_num (~0) + reply_num (Kernel::ERR_INVALID_ARGUMENT) return kPage *t = (kPage *)c->arg->protected_data.l t->forget () @@ -645,7 +648,7 @@ static void page_invoke (unsigned cmd, unsigned target, Kernel::Num protected_da break case Kernel::Page::SET_FLAGS & REQUEST_MASK: if cmd & Kernel::Page::READONLY: - reply_num (~0) + reply_num (Kernel::ERR_WRITE_DENIED) return // Always refuse to set reserved flags. c->data[1].h &= ~(Kernel::Page::PHYSICAL | Kernel::Page::UNCACHED) @@ -785,12 +788,12 @@ static void list_invoke (unsigned cmd, unsigned target, Kernel::Num protected_da else: if ((unsigned)c->arg->target & ~REQUEST_MASK) != CAPTYPE_LISTITEM: dpanic (0, "invalid request for list: arg is no listitem") - reply_num (~0) + reply_num (Kernel::ERR_INVALID_ARGUMENT) return item = (kListitem *)c->arg->protected_data.l if item->list != list: dpanic (0, "item list is not equal to called object") - reply_num (~0) + reply_num (Kernel::ERR_INVALID_ARGUMENT) return switch cmd: case Kernel::List::GET_NEXT & REQUEST_MASK: @@ -799,7 +802,7 @@ static void list_invoke (unsigned cmd, unsigned target, Kernel::Num protected_da else: if ((unsigned)c->arg->target & REQUEST_MASK) != CAP_MASTER && ((unsigned)c->arg->target & REQUEST_MASK) != Kernel::Listitem::LIST: dpanic (0, "trying to get next listitem with insufficient rights") - reply_num (~0) + reply_num (Kernel::ERR_INVALID_ARGUMENT) return item = item->next_item if !item: @@ -810,32 +813,32 @@ static void list_invoke (unsigned cmd, unsigned target, Kernel::Num protected_da case Kernel::List::ADD_ITEM & REQUEST_MASK: if !item: dpanic (0, "invalid request: no listitem for List::ADD_ITEM") - reply_num (~0) + reply_num (Kernel::ERR_INVALID_ARGUMENT) return if ((unsigned)c->arg->target & REQUEST_MASK) != CAP_MASTER && ((unsigned)c->arg->target & REQUEST_MASK) != Kernel::Listitem::ADD: dpanic (0, "trying to add listitem with insufficient rights") - reply_num (~0) + reply_num (Kernel::ERR_INVALID_ARGUMENT) return ((kListitem *)c->arg->protected_data.l)->add (list) break case Kernel::List::GET_INFO & REQUEST_MASK: if !item: dpanic (0, "no item for List::GET_INFO") - reply_num (~0, ~0, ~0) + reply_num (Kernel::ERR_INVALID_ARGUMENT, ~0, ~0) return reply_num (item->info) return case Kernel::List::SET_INFO & REQUEST_MASK: if !item: dpanic (0, "no item for List::SET_INFO") - reply_num (~0) + reply_num (Kernel::ERR_INVALID_ARGUMENT) return item->info = c->data[1] break case Kernel::List::GET_CAP & REQUEST_MASK: if !item: dpanic (0, "no item for List::GET_CAP") - reply_num (~0) + reply_num (Kernel::ERR_INVALID_ARGUMENT) return kCapability *cap = item->target.cap (0) reply_cap ((unsigned)cap->target, cap->protected_data, ((unsigned)cap->target & ~KERNEL_MASK) == 0 ? &((kObject *)cap->target)->refs : &cap->target->capabilities) diff --git a/iris.hhp b/iris.hhp index 272aad1..fced34a 100644 --- a/iris.hhp +++ b/iris.hhp @@ -86,6 +86,7 @@ namespace Kernel: ERR_OUT_OF_MEMORY // The following are not raised, but returned. ERR_INVALID_OPERATION + ERR_INVALID_ARGUMENT NUM_EXCEPTION_CODES #ifndef NDEBUG @@ -378,7 +379,7 @@ namespace Kernel: return get_info (SP) unsigned get_flags (): return get_info (FLAGS) - void run (bool run): + void run (bool run = true): set_flags (run ? RUNNING : 0, RUNNING) void wait (bool wait): set_flags (wait ? WAITING : 0, WAITING) @@ -454,8 +455,9 @@ namespace Kernel: ocall (target, CAP_MASTER_DIRECT | SHARE, flags) unsigned get_flags (): return call (CAP_MASTER_DIRECT | GET_FLAGS).l - void set_flags (unsigned new_flags, unsigned mask): + bool set_flags (unsigned new_flags, unsigned mask): call (CAP_MASTER_DIRECT | SET_FLAGS, Num (new_flags, mask)) + return recv.data[0].l == NO_ERROR unsigned physical_address (): return my_thread.ocall (*this, CAP_MASTER_DIRECT | Thread::PRIV_PHYSICAL_ADDRESS).l void alloc_physical (unsigned address, bool cachable, bool freeable): @@ -536,10 +538,10 @@ namespace Kernel: void destroy (Cap target): ocall (target, CAP_MASTER_DIRECT | DESTROY) // TODO: LIST - void map (Cap page, unsigned address, bool readonly = false): + bool map (Cap page, unsigned address, bool readonly = false): if readonly: address |= Page::READONLY - ocall (page, CAP_MASTER_DIRECT | MAP, address) + return ocall (page, CAP_MASTER_DIRECT | MAP, address).l != ~0 Page mapping (void *address): icall (CAP_MASTER_DIRECT | MAPPING, Num ((unsigned)address)) return get_arg () diff --git a/mips/nanonote/Makefile.arch b/mips/nanonote/Makefile.arch index 4b62551..ee27a7d 100644 --- a/mips/nanonote/Makefile.arch +++ b/mips/nanonote/Makefile.arch @@ -17,7 +17,7 @@ load = 0x80000000 -ARCH_CXXFLAGS = -DNUM_THREADS=6 +ARCH_CXXFLAGS = -DNUM_THREADS=2 ARCH_CPPFLAGS = -I. -Imips -Imips/nanonote -Wa,-mips32 -DNANONOTE -DUSE_SERIAL CROSS = mipsel-linux-gnu- OBJDUMP = $(CROSS)objdump @@ -28,7 +28,10 @@ LDFLAGS = --omagic -Ttext $(load) arch_iris_sources = mips/interrupts.cc mips/arch.cc boot_sources = mips/init.cc mips/nanonote/board.cc arch_headers = mips/arch.hh mips/nanonote/jz4740.hh mips/nanonote/board.hh -boot_threads = init udc nanonote-gpio buzzer metronome lcd +boot_threads = init udc +programs = nanonote-gpio buzzer metronome lcd + +all: test $(addsuffix .elf,$(addprefix fs/,$(programs))) test: iris.raw mips/nanonote/server/usb-server mips/nanonote/sdram-setup.raw echo "reboot 0xa$(shell /bin/sh -c '$(OBJDUMP) -t iris.elf | grep __start$$ | cut -b2-8')" | nc localhost 5050 @@ -53,6 +56,7 @@ mips/nanonote/sdram-setup.elf: LDFLAGS = --omagic -T mips/nanonote/sdram-setup.l mips/nanonote/threadlist.o: $(addsuffix .elf,$(boot_threads)) mips/boot.o: TARGET_FLAGS = -DMEMORY_SIZE="32 << 20" mips/init.o: TARGET_FLAGS = -I/usr/include +boot-programs/init.o: TARGET_FLAGS = -I/usr/include $(addsuffix .elf,$(boot_threads)): TARGET_FLAGS = -I. $(addsuffix .elf,$(boot_threads)): LDFLAGS = -EL $(addprefix boot-programs/,$(addsuffix .cc,$(boot_threads))): devices.hh keys.hh diff --git a/mips/nanonote/server/usb-server.ccp b/mips/nanonote/server/usb-server.ccp index 7b0ef35..5f7aedd 100644 --- a/mips/nanonote/server/usb-server.ccp +++ b/mips/nanonote/server/usb-server.ccp @@ -22,9 +22,11 @@ #include #include #include +#include #include #include #include +#include "devices.hh" struct client @@ -69,18 +71,87 @@ struct data: void data::poll (): while true: - char buffer[2] - int s = usb_control_msg (handle, USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, POLL, 0, 0, buffer, 8, timeout) - if s < 1 || s > 2 || buffer[0] != '#': + unsigned buffer[2] + int s = usb_control_msg (handle, USB_ENDPOINT_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, POLL, 0, 0, (char *)buffer, 8, timeout) + if s != 8: std::cerr << "unable to send poll message to device: " << usb_strerror () << std::endl usb_release_interface (handle, 0) usb_close (handle) handle = NULL return - if s != 1: - std::cout << buffer[1] << std::flush - else: - break + switch buffer[0] & 0xffff: + case ~0 & 0xffff: + // Log character. + std::cout << (char)buffer[1] << std::flush + continue + case ~1 & 0xffff: + // No event. + break + case Directory::GET_SIZE: + unsigned long long size = dir.size () + if usb_control_msg (handle, USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, Directory::GET_SIZE, 0, 0, (char const *)&size, 8, timeout) != 8: + std::cerr << "unable to send size to device: " << usb_strerror () << std::endl + usb_release_interface (handle, 0) + usb_close (handle) + handle = NULL + return + continue + case Directory::GET_NAME: + if buffer[1] >= dir.size (): + std::cerr << "invalid file name requested" << std::endl; + usb_release_interface (handle, 0) + usb_close (handle) + handle = NULL + return + if usb_control_msg (handle, USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, Directory::GET_NAME, 0, 0, dir[buffer[1]].name, 16, timeout) != 16: + std::cerr << "unable to send name to device: " << usb_strerror () << std::endl + usb_release_interface (handle, 0) + usb_close (handle) + handle = NULL + return + continue + case Directory::LOCK_RO: + if !lock++: + dir.load (files) + continue + case Directory::UNLOCK_RO: + if !lock: + std::cerr << "unlocking without lock" << std::endl + usb_release_interface (handle, 0) + usb_close (handle) + handle = NULL + return + --lock + continue + case Directory::GET_FILE_RO: + unsigned f = buffer[0] >> 16 + if f >= dir.size (): + std::cerr << "reading invalid file" << std::endl + usb_release_interface (handle, 0) + usb_close (handle) + handle = NULL + return + std::ifstream file (dir[f]) + file.seek (buffer[1] << 12) + char page[1 << 12] + memset (page, 0, 1 << 12) + file.read (page, 1 << 12) + for unsigned i = 0; i < (1 << 12); i += 64: + if usb_bulk_write (handle, 1 | USB_ENDPOINT_OUT, p, 64) != 64: + std::cerr << "unable to send file to device: " << usb_strerror () << std::endl + usb_release_interface (handle, 0) + usb_close (handle) + handle = NULL + return + continue + default: + std::cerr << "invalid request" << std::endl + usb_release_interface (handle, 0) + usb_close (handle) + handle = NULL + return + // If the code reaches this point, break out of the loop. The loop continues if a continue statement is reached. + break (shevek::absolute_time () + shevek::relative_time (1, 0)).schedule (sigc::mem_fun (*this, &data::poll)) struct client : public shevek::server ::connection: diff --git a/mips/nanonote/threadlist.S b/mips/nanonote/threadlist.S index 5127a30..45ab1fa 100644 --- a/mips/nanonote/threadlist.S +++ b/mips/nanonote/threadlist.S @@ -29,22 +29,6 @@ thread1: .balign 0x1000 thread2: - .incbin "nanonote-gpio.elf" - - .balign 0x1000 -thread3: - .incbin "buzzer.elf" - - .balign 0x1000 -thread4: - .incbin "metronome.elf" - - .balign 0x1000 -thread5: - .incbin "lcd.elf" - - .balign 0x1000 -thread6: // Everything from here may be freed after kernel initialization. init_start: @@ -53,7 +37,3 @@ thread_start: .word thread0 .word thread1 .word thread2 - .word thread3 - .word thread4 - .word thread5 - .word thread6 diff --git a/boot-programs/buzzer.ccp b/source/buzzer.ccp similarity index 100% rename from boot-programs/buzzer.ccp rename to source/buzzer.ccp diff --git a/boot-programs/charset b/source/charset similarity index 100% rename from boot-programs/charset rename to source/charset diff --git a/source/crt0.ccp b/source/crt0.ccp new file mode 100644 index 0000000..f0e7703 --- /dev/null +++ b/source/crt0.ccp @@ -0,0 +1,143 @@ +#pypp 0 +// Iris: micro-kernel for a capability-based operating system. +// boot-programs/init.S: Startup code for initial Threads. +// 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 "iris.hh" +#include "devices.hh" + +// For some unknown reason, gcc needs this to be defined. +unsigned __gxx_personality_v0 + +struct list: + list *prev, *next + +static unsigned __slots, __caps +static list *__slot_admin, *__cap_admin +static list *__first_free_slot, *__first_free_cap + +namespace Kernel: + Receiver my_receiver + Thread my_thread + Memory my_memory + Cap my_call + Parent my_parent + __recv_data_t recv + + void print_caps (): + // Assume __caps to be 16. + bool used[16] + for unsigned i = 0; i < 16; ++i: + used[i] = true + unsigned num = 0 + for list *i = __first_free_cap; i; i = i->next: + used[i - __cap_admin] = false + ++num + kdebug_num (num, 1) + kdebug (":") + for unsigned i = 0; i < 16; ++i: + kdebug_char (used[i] ? '#' : '.') + kdebug_char ('\n') + + void free_slot (unsigned slot): + //kdebug ("free slot\n") + __slot_admin[slot].prev = NULL + __slot_admin[slot].next = __first_free_slot + if __slot_admin[slot].next: + __slot_admin[slot].next->prev = &__slot_admin[slot] + __first_free_slot = &__slot_admin[slot] + + void free_cap (Cap cap): + //kdebug ("free cap\n") + if cap.slot () != 0: + kdebug ("trying to free capability from non-0 slot\n") + return + list *l = &__cap_admin[cap.idx ()] + l->prev = NULL + l->next = __first_free_cap + if l->next: + l->next->prev = l + __first_free_cap = l + + unsigned alloc_slot (): + //kdebug ("alloc slot\n") + if !__first_free_slot: + // Out of slots... Probably best to raise an exception. For now, just return NO_SLOT. + kdebug ("out of slots!\n") + return ~0 + list *ret = __first_free_slot + __first_free_slot = ret->next + if ret->next: + ret->next->prev = NULL + return ret - __slot_admin + + Cap alloc_cap (): + //kdebug ("alloc cap\n") + if !__first_free_cap: + // Out of caps... Probably best to raise an exception. For now, just return CAP_NONE + kdebug ("out of capabilities!\n") + return Cap (0, CAP_NONE) + list *ret = __first_free_cap + __first_free_cap = ret->next + if ret->next: + ret->next->prev = NULL + return Cap (0, ret - __cap_admin) + +extern "C": + void run__main (unsigned slots, unsigned caps, list *slot_admin, list *cap_admin): + __slots = slots + __caps = caps + __slot_admin = slot_admin + __cap_admin = cap_admin + __first_free_slot = NULL + for unsigned i = 2; i < __slots; ++i: + Kernel::free_slot (i) + __first_free_cap = NULL + for unsigned i = 7; i < __caps; ++i: + Kernel::free_cap (Kernel::Cap (0, i)) + Kernel::my_receiver = Kernel::Cap (0, __receiver_num) + Kernel::my_thread = Kernel::Cap (0, __thread_num) + Kernel::my_memory = Kernel::Cap (0, __memory_num) + Kernel::my_call = Kernel::Cap (0, __call_num) + Kernel::my_parent = Kernel::Cap (0, __parent_num) + Kernel::recv.reply = Kernel::alloc_cap () + Kernel::recv.arg = Kernel::alloc_cap () + Kernel::Num ret = start () + Kernel::my_parent.invoke (~0, ret) + Kernel::my_memory.destroy (Kernel::my_thread) + // The program no longer exists. If it somehow does, generate an address fault. + while true: + *(volatile unsigned *)~0 + +__asm__ volatile ("\t.globl __start\n" + "\t.set noreorder\n" + "__start:\n" + "\tbal 1f\n" + "__hack_label:\n" + "\tnop\n" + "\t.word _gp\n" + "1:\n" + "\tlw $gp, 0($ra)\n" + "\tsll $v0, $a0, 3\n" + "\tsll $v1, $a1, 3\n" + "\tsubu $sp, $sp, $v0\n" + "\tmove $a2, $sp\n" + "\tsubu $sp, $sp, $v1\n" + "\tmove $a3, $sp\n" + "\tla $t9, run__main\n" + "\tjr $t9\n" + "\tnop\n" + "\t.set reorder") diff --git a/boot-programs/gpio.ccp b/source/gpio.ccp similarity index 100% rename from boot-programs/gpio.ccp rename to source/gpio.ccp diff --git a/boot-programs/gpio.txt b/source/gpio.txt similarity index 100% rename from boot-programs/gpio.txt rename to source/gpio.txt diff --git a/boot-programs/lcd.ccp b/source/lcd.ccp similarity index 100% rename from boot-programs/lcd.ccp rename to source/lcd.ccp diff --git a/boot-programs/metronome.ccp b/source/metronome.ccp similarity index 100% rename from boot-programs/metronome.ccp rename to source/metronome.ccp diff --git a/boot-programs/nanonote-gpio.ccp b/source/nanonote-gpio.ccp similarity index 100% rename from boot-programs/nanonote-gpio.ccp rename to source/nanonote-gpio.ccp