#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 "keys.hh" #define INIT_CONFIG "init.config" #define INIT_CONFIG_SIZE 12 #define NUM_SLOTS 8 #define NUM_CAPS 32 enum Captype: SYSREQ PROGRAM FILE 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] template // struct List: struct Item: Item *prev, *next _T value _T &operator* (): return value _T *operator-> (): return &value Item *first Item *begin (): return first void erase (Item *i): if i->prev: i->prev->next = i->next else: first = i->next if i->next: i->next->prev = i->prev delete i Item *insert (Item *after = NULL): Item *ret = new Item if after: ret->prev = after->prev ret->next = after if ret->prev: ret->prev->next = ret else: first = ret after->prev = ret else: ret->prev = NULL ret->next = first first = ret if ret->next: ret->next->prev = ret return ret void init (): first = NULL List () : first (NULL): struct Program struct Devbase: char *name unsigned name_len unsigned type Program *client Iris::Cap cap struct Serverdevice : public Devbase: unsigned index Program *server struct Clientdevice: unsigned type, index Devbase *dev static Iris::Memory top_memory static Iris::Directory root static Iris::Elfrun elfrun struct Program: char *name unsigned name_len unsigned size Iris::Caps pages Iris::Memory memory Iris::Thread thread List server_devices List client_devices unsigned num_waiting bool priv void run (): Iris::Cap cap = Iris::my_receiver.create_capability (Iris::Num (PROGRAM, (unsigned)this)) if priv: kdebug ("priv ") kdebug ("running ") for unsigned i = 0; i < name_len; ++i: kdebug_char (name[i]) kdebug ("\n") Iris::Caps caps = elfrun.run_caps (top_memory, pages, cap.copy (), (size + PAGE_SIZE - 1) >> PAGE_BITS) Iris::free_cap (cap) thread = caps.get (__thread_num) memory = caps.get (__memory_num) Iris::free_cap (caps) if priv: thread.make_priv () thread.run () struct File : public Devbase: unsigned size Iris::Caps pages static List programs static List files static Serverdevice *sysreq static bool name_match (char const *name, unsigned name_len, Iris::String n): char nm[16] n.get_chars (0, nm) for unsigned t = 0; t < 16 && t < name_len; ++t: if nm[t] != name[t]: return false return true static Iris::Caps load (char const *name, unsigned name_len, unsigned &size, Iris::Caps target = Iris::alloc_cap ()): kdebug ("loading ") for unsigned i = 0; i < name_len; ++i: kdebug_char (name[i]) kdebug ("\n") root.lock_ro () Iris::Num sz = root.get_size () if sz.h: Iris::panic (sz.h, "too many files") for unsigned i = 0; i < sz.l; ++i: Iris::String n = root.get_name (i) if !name_match (name, name_len, n): Iris::free_cap (n) continue Iris::free_cap (n) Iris::Block file = root.get_file_ro (i) Iris::Num s = file.get_size () if s.h: Iris::panic (s.h, "file is too large to load") size = s.l unsigned pages = (size + PAGE_SIZE - 1) >> PAGE_BITS if pages > 0: Iris::set_recv_arg (target) Iris::my_memory.create_caps (pages) unsigned slot = target.use () for unsigned p = 0; p < pages; ++p: Iris::set_recv_arg (Iris::Cap (slot, p)) file.get_block (p << PAGE_BITS) Iris::free_slot (slot) Iris::free_cap (file) root.unlock_ro () return target Iris::panic (0, "file not found for init") return target static void delspace (char *&line, unsigned &maxlen): while maxlen && (*line == ' ' || *line == '\t'): ++line --maxlen if maxlen && *line == '#': line += maxlen maxlen = 0 static bool match (char *&line, unsigned &maxlen, char const *word): delspace (line, maxlen) char const *w = word char *l = line unsigned len = 0 while *word: if *l++ != *word++: return false ++len line = l maxlen -= len delspace (line, maxlen) //kdebug ("match: ") //kdebug (w) //kdebug ("\n") return true static bool isnamechar (char c): if c >= 'a' && c <= 'z': return true if c >= 'A' && c <= 'Z': return true if c >= '0' && c <= '9': return true if c == '_': return true return false static bool get_name (char *&line, unsigned &len, char *&name, unsigned &name_len): delspace (line, len) if !len: return false name_len = 0 while name_len < len && isnamechar (line[name_len]): ++name_len name = new char[name_len] //kdebug ("name: ") for unsigned i = 0; i < name_len; ++i: name[i] = line[i] //kdebug_char (name[i]) //kdebug_char ('\n') line += name_len len -= name_len delspace (line, len) return true static bool string_match (char const *s1, unsigned l1, char const *s2, unsigned l2): if l1 != l2: return false for unsigned i = 0; i < l1; ++i: if s1[i] != s2[i]: return false return true struct Type: char const *name unsigned len unsigned type static unsigned read_num (char *&line, unsigned &len): delspace (line, len) unsigned num = 0 while len && *line >= '0' && *line <= '9': num *= 10 num += *line - '0' ++line --len delspace (line, len) return num static Type types[] = { { "String", 6, Iris::String::ID }, { "WString", 7, Iris::WString::ID }, { "Block", 5, Iris::Block::ID }, { "WBlock", 6, Iris::WBlock::ID }, { "Boot", 4, Iris::Boot::ID }, { "Device", 6, Iris::Device::ID }, { "Event", 5, Iris::Event::ID }, { "Parent", 6, Iris::Parent::ID }, { "Keyboard", 8, Iris::Keyboard::ID }, { "Buzzer", 6, Iris::Buzzer::ID }, { "Display", 7, Iris::Display::ID }, { "Font", 4, Iris::Font::ID }, { "Setting", 7, Iris::Setting::ID }, { "Directory", 9, Iris::Directory::ID }, { "WDirectory", 10, Iris::WDirectory::ID }, { "Stream", 6, Iris::Stream::ID }, { "UI", 2, Iris::UI::ID }, { "RTC", 3, Iris::RTC::ID }, { NULL, 0, 0 } } static void find_type (char *&line, unsigned &len, unsigned &type, unsigned &index): char *n unsigned l if !get_name (line, len, n, l) || !len: Iris::panic (0, "no name for type") for unsigned t = 0; types[t].len != 0; ++t: if string_match (types[t].name, types[t].len, n, l): type = types[t].type if len && *line == ',': ++line --len index = read_num (line, len) else: index = 0 return Iris::panic (0, "no valid type found") static bool find_cap (char *&line, unsigned &len, Program **server, Devbase *&dev, bool &present): char *n unsigned l if !get_name (line, len, n, l): Iris::panic (0, "no capability name found in init.config") for List ::Item *p = programs.begin (); p; p = p->next: List ::Item *d for d = (*p)->server_devices.begin (); d; d = d->next: if string_match (n, l, (*d)->name, (*d)->name_len): if server: *server = &**p dev = &**d present = false return true if server: return false for List ::Item *f = files.begin (); f; f = f->next: if string_match (n, l, (*f)->name, (*f)->name_len): dev = &**f present = true return true return false static void parse_line (char *&line, unsigned maxlen) static void include (char const *name, unsigned name_len): unsigned size Iris::Caps caps = load (name, name_len, size) unsigned pages = (size + PAGE_SIZE - 1) >> PAGE_BITS char *config = alloc_space (pages) unsigned pages_slot = caps.use () for unsigned p = 0; p < pages; ++p: Iris::Page page (pages_slot, p) Iris::my_memory.map (page, (unsigned)&config[p << PAGE_BITS]) char *ptr = config while ptr - config < size: parse_line (ptr, config + size - ptr) for unsigned p = 0; p < pages; ++p: Iris::my_memory.destroy (Iris::Cap (pages_slot, p)) Iris::my_memory.destroy (caps) Iris::free_cap (caps) Iris::free_slot (pages_slot) static char *get_filename (char *&line, unsigned &maxlen, unsigned &len): char q = *line++ --maxlen len = 0 while maxlen && *line != q: ++line --maxlen ++len if !maxlen: Iris::panic (0, "no closing quote in init.config") return line - len static void do_load (char *&line, unsigned &maxlen, bool priv): Program *p = &**programs.insert () if !get_name (line, maxlen, p->name, p->name_len) || !match (line, maxlen, "=") || !maxlen: Iris::panic (0, "syntax error in init.config (load)") unsigned l char *n = get_filename (line, maxlen, l) p->pages = load (n, l, p->size) p->priv = priv p->num_waiting = 0 ++line --maxlen static void parse_line (char *&line, unsigned maxlen): char *start = line while maxlen && *line != '\n': ++line --maxlen // The line to execute is from start to line. maxlen = line - start if *line == '\n': ++line delspace (start, maxlen) if !maxlen: return if match (start, maxlen, "program"): // program = "" do_load (start, maxlen, false) else if match (start, maxlen, "driver"): // driver = "" do_load (start, maxlen, true) else if match (start, maxlen, "file"): // file = "" File *f = &**files.insert () f->type = Iris::Block::ID if !get_name (start, maxlen, f->name, f->name_len) || !match (start, maxlen, "=") || !maxlen: Iris::panic (0, "syntax error in init.config (file name)") unsigned l char *n = get_filename (start, maxlen, l) f->pages = load (n, l, f->size) f->cap = Iris::my_receiver.create_capability (Iris::Num (FILE, (unsigned)f)) ++line --maxlen else if match (start, maxlen, "receive"): // receive / [, ] = char *n unsigned l if !get_name (start, maxlen, n, l) || !match (start, maxlen, "/") || !maxlen: Iris::panic (0, "syntax error in init.config (receive)") List ::Item *p for p = programs.begin (); p; p = p->next: if string_match ((*p)->name, (*p)->name_len, n, l): break if !p: Iris::panic (0, "program not found for receive") List ::Item *dev = (*p)->server_devices.insert () find_type (start, maxlen, (*dev)->type, (*dev)->index) if !match (start, maxlen, "=") || !get_name (start, maxlen, (*dev)->name, (*dev)->name_len): Iris::panic (1, "syntax error in init.config (receive)") (*dev)->server = &**p (*dev)->client = NULL (*dev)->cap = Iris::Cap () else if match (start, maxlen, "sysreq"): Program *server if sysreq: Iris::panic (0, "double registration of sysreq") bool dummy if !find_cap (start, maxlen, &server, *(Devbase **)&sysreq, dummy): Iris::panic (0, "capability not found for sysreq") if sysreq->type != Iris::Keyboard::ID: kdebug ("capability for sysreq is not a keyboard\n") else if match (start, maxlen, "give"): // give / [, ] = char *n unsigned l if !get_name (start, maxlen, n, l) || !match (start, maxlen, "/") || !maxlen: Iris::panic (0, "syntax error in init.config (give)") List ::Item *p for p = programs.begin (); p; p = p->next: if string_match ((*p)->name, (*p)->name_len, n, l): break if !p: Iris::panic (0, "program not found for give") List ::Item *d = (*p)->client_devices.insert () find_type (start, maxlen, (*d)->type, (*d)->index) if !match (start, maxlen, "="): Iris::panic (1, "syntax error in init.config (give)") bool present if !find_cap (start, maxlen, NULL, (*d)->dev, present): Iris::panic (0, "capability not found for give") if (*d)->dev->type != (*d)->type: kdebug ("capability type mismatch for give\n") if (*d)->dev->client: Iris::panic (0, "capability given out twice") (*d)->dev->client = &**p if !present: ++(*p)->num_waiting //kdebug ("registered give device: ") //kdebug_num ((*d)->type) //kdebug ("\n") else if match (start, maxlen, "include"): unsigned name_len char *name = get_filename (line, maxlen, name_len) include (name, name_len) else: for unsigned i = 0; i < maxlen; ++i: kdebug_char (start[i]) kdebug_char ('\n') Iris::panic (0, "invalid line in init.config") delspace (start, maxlen) if maxlen: kdebug ("Junk: ") for unsigned i = 0; i < maxlen; ++i: kdebug_char (start[i]) kdebug_char ('\n') Iris::panic (0, "junk at end of line in init.config") Iris::Num start (): init_alloc () programs.init () files.init () root = Iris::my_parent.get_capability () elfrun = Iris::my_parent.get_capability () sysreq = NULL top_memory = Iris::get_top_memory () include (INIT_CONFIG, INIT_CONFIG_SIZE) kdebug ("killing boot threads\n") Iris::my_parent.init_done () for List ::Item *p = programs.begin (); p; p = p->next: if !(*p)->num_waiting: (*p)->run () if !sysreq: Iris::panic (0, "sysreq not registered") if sysreq->client: Iris::panic (0, "sysreq set to reserved capability") kdebug ("waiting for events.\n") while true: Iris::wait () switch Iris::recv.protected_data.l: case SYSREQ: if Iris::recv.data[0].l & Iris::Keyboard::RELEASE: Iris::reboot () continue kdebug ("sysreq event: rebooting device at release\n") continue case FILE: File *file = (File *)Iris::recv.protected_data.h switch Iris::recv.data[0].l: case Iris::Block::GET_SIZE: Iris::recv.reply.invoke (file->size) break case Iris::Block::GET_ALIGN_BITS: Iris::recv.reply.invoke (PAGE_BITS) break case Iris::Block::GET_BLOCK: Iris::Cap reply = Iris::get_reply () Iris::Page target = Iris::get_arg () Iris::Page source = file->pages.get (Iris::recv.data[1].l >> PAGE_BITS) source.share (target, Iris::Page::READONLY) reply.invoke () Iris::free_cap (reply) Iris::free_cap (source) Iris::free_cap (target) break default: Iris::panic (Iris::recv.data[0].l, "unknown request for init string") break case PROGRAM: Program *caller = (Program *)Iris::recv.protected_data.h switch Iris::recv.data[0].l: case Iris::Parent::GET_CAPABILITY: unsigned index = Iris::recv.data[0].h unsigned type = Iris::recv.data[1].l if Iris::recv.data[1].h: Iris::panic (Iris::recv.data[1].h, "high device requested") //kdebug ("requested device ") //kdebug_num (type) //kdebug (":") //kdebug_num (index) //kdebug ("\n") List ::Item *d for d = caller->client_devices.begin (); d; d = d->next: //kdebug ("checking ") //kdebug_num ((*d)->type) //kdebug (":") //kdebug_num ((*d)->index) //kdebug ("\n") if (*d)->type == type && (*d)->index == index: break if !d: Iris::debug ("requested %x by %s\n", type, caller->name) Iris::panic (type, "unregistered device requested") Iris::recv.reply.invoke (0, 0, (*d)->dev->cap) //kdebug ("given device ") //kdebug_num (type) //kdebug (":") //kdebug_num (index) //kdebug ("\n") break case Iris::Parent::PROVIDE_CAPABILITY: if Iris::recv.data[1].h != 0: kdebug ("init: too high device provided\n") continue unsigned type = Iris::recv.data[1].l unsigned index = Iris::recv.data[0].h List ::Item *d for d = caller->server_devices.begin (); d; d = d->next: if (*d)->type == type && (*d)->index == index: break if !d: Iris::debug ("caller: %s\n", caller->name) Iris::panic (type, "unregistered device provided") (*d)->cap = Iris::get_arg () Iris::recv.reply.invoke () if (*d)->client: if !--(*d)->client->num_waiting: (*d)->client->run () //kdebug ("provided ") //kdebug_num ((*d)->type) //kdebug (":") //kdebug_num ((*d)->index) //kdebug ("\n") break case Iris::Parent::INIT_DONE: //kdebug ("init done\n") Iris::recv.reply.invoke () if caller == sysreq->server: Iris::Cap cap = Iris::my_receiver.create_capability (SYSREQ) Iris::Keyboard (sysreq->cap).set_cb (cap.copy ()) Iris::free_cap (cap) kdebug ("registered sysreq\n") break case Iris::Parent::EXIT: kdebug ("child ") for unsigned i = 0; i < caller->name_len; ++i: kdebug_char (caller->name[i]) kdebug (" exits with code ") kdebug_num (Iris::recv.data[1].h) kdebug (":") kdebug_num (Iris::recv.data[1].l) kdebug ("\n") top_memory.destroy (caller->memory) break default: // TODO. kdebug ("child request: ") kdebug_num (Iris::recv.data[0].l) kdebug (" from ") for unsigned i = 0; i < caller->name_len; ++i: kdebug_char (caller->name[i]) kdebug ("\n")