Root/
| 1 | #pypp 0 |
| 2 | // Iris: micro-kernel for a capability-based operating system. |
| 3 | // source/bootinit.ccp: Bootstrapping code. |
| 4 | // Copyright 2009-2010 Bas Wijnen <wijnen@debian.org> |
| 5 | // |
| 6 | // This program is free software: you can redistribute it and/or modify |
| 7 | // it under the terms of the GNU General Public License as published by |
| 8 | // the Free Software Foundation, either version 3 of the License, or |
| 9 | // (at your option) any later version. |
| 10 | // |
| 11 | // This program is distributed in the hope that it will be useful, |
| 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 14 | // GNU General Public License for more details. |
| 15 | // |
| 16 | // You should have received a copy of the GNU General Public License |
| 17 | // along with this program. If not, see <http://www.gnu.org/licenses/>. |
| 18 | |
| 19 | #include "devices.hh" |
| 20 | #include "iris.hh" |
| 21 | #include <elf.h> |
| 22 | |
| 23 | #define ELFRUN_NAME "elfrun.elf" |
| 24 | #define INIT_NAME "init.elf" |
| 25 | |
| 26 | // These numbers are only used for elfrun. |
| 27 | #define NUM_SLOTS 8 |
| 28 | #define NUM_CAPS 32 |
| 29 | |
| 30 | static unsigned _free |
| 31 | extern unsigned _end |
| 32 | |
| 33 | void init_alloc (): |
| 34 | _free = ((unsigned)&_end + PAGE_SIZE - 1) & PAGE_MASK |
| 35 | |
| 36 | char *alloc_space (unsigned pages): |
| 37 | unsigned ret = (_free + PAGE_SIZE - 1) & PAGE_MASK |
| 38 | _free = ret + (pages << PAGE_BITS) |
| 39 | return (char *)ret |
| 40 | |
| 41 | void *operator new[] (unsigned size): |
| 42 | //kdebug ("new ") |
| 43 | void *ret = (void *)_free |
| 44 | size = (size + 3) & ~3 |
| 45 | unsigned rest = PAGE_SIZE - (((_free - 1) & ~PAGE_MASK) + 1) |
| 46 | if rest < size: |
| 47 | unsigned pages = ((size - rest) + PAGE_SIZE - 1) >> PAGE_BITS |
| 48 | for unsigned p = 0; p < pages; ++p: |
| 49 | Iris::Page page = Iris::my_memory.create_page () |
| 50 | page.set_flags (Iris::Page::PAYING | Iris::Page::FRAME) |
| 51 | Iris::my_memory.map (page, _free + rest + (p << PAGE_BITS)) |
| 52 | Iris::free_cap (page) |
| 53 | _free += size |
| 54 | //kdebug_num ((unsigned)ret) |
| 55 | //kdebug ("+") |
| 56 | //kdebug_num (size) |
| 57 | //kdebug ("\n") |
| 58 | return ret |
| 59 | |
| 60 | void *operator new (unsigned size): |
| 61 | return new char[size] |
| 62 | |
| 63 | static unsigned *bss_mapping |
| 64 | static Iris::Page bss_page |
| 65 | |
| 66 | // Get the initial block device and filesystem. |
| 67 | static Iris::Directory receive_devices (): |
| 68 | Iris::Block device |
| 69 | bool have_device = false |
| 70 | Iris::Cap reply[2] |
| 71 | bool have_reply[2] |
| 72 | have_reply[0] = false |
| 73 | have_reply[1] = false |
| 74 | unsigned next = 2 |
| 75 | while true: |
| 76 | Iris::wait () |
| 77 | kdebug_num (Iris::recv.protected_data.l, 1) |
| 78 | kdebug (": ") |
| 79 | if Iris::recv.protected_data.l == 0: |
| 80 | kdebug ("sd detect event device request\n") |
| 81 | // SD detection event device request. |
| 82 | // Ignore all; that will result in the driver thinking there is a card. |
| 83 | Iris::recv.reply.invoke () |
| 84 | continue |
| 85 | switch Iris::recv.data[0].l: |
| 86 | case Iris::Parent::PROVIDE_CAPABILITY: |
| 87 | switch Iris::recv.data[1].l: |
| 88 | case Iris::Block::ID: |
| 89 | case Iris::WBlock::ID: |
| 90 | // Ignore other partitions. |
| 91 | Iris::Cap r = Iris::get_reply () |
| 92 | if Iris::recv.data[0].h != 0: |
| 93 | kdebug ("ignoring non-0 partition\n") |
| 94 | else: |
| 95 | if have_device: |
| 96 | Iris::panic (0, "double device provided") |
| 97 | device = Iris::get_arg () |
| 98 | if have_reply[next - 2]: |
| 99 | kdebug ("block provided (used)\n") |
| 100 | reply[next++ - 2].invoke (0, 0, device.copy ()) |
| 101 | Iris::free_cap (device) |
| 102 | else: |
| 103 | kdebug ("block provided (stored)\n") |
| 104 | have_device = true |
| 105 | r.invoke () |
| 106 | Iris::free_cap (r) |
| 107 | break |
| 108 | case Iris::Directory::ID: |
| 109 | kdebug ("directory provided\n") |
| 110 | Iris::Directory ret = Iris::get_arg () |
| 111 | Iris::recv.reply.invoke () |
| 112 | return ret |
| 113 | default: |
| 114 | Iris::panic (Iris::recv.data[1].l, "invalid capability type provided by boot thread") |
| 115 | break |
| 116 | case Iris::Parent::GET_CAPABILITY: |
| 117 | if Iris::recv.data[1].l == Iris::Event::ID: |
| 118 | kdebug ("event requested\n") |
| 119 | // Detection of sd card. |
| 120 | Iris::Cap reply = Iris::get_reply () |
| 121 | Iris::Cap event = Iris::my_receiver.create_capability (0) |
| 122 | reply.invoke (0, 0, event.copy ()) |
| 123 | Iris::free_cap (event) |
| 124 | Iris::free_cap (reply) |
| 125 | break |
| 126 | if Iris::recv.data[1].l != Iris::Block::ID && Iris::recv.data[1].l != Iris::WBlock::ID: |
| 127 | Iris::panic (Iris::recv.data[1].l, "invalid capability type requested by boot thread") |
| 128 | if next == Iris::recv.protected_data.l && have_device: |
| 129 | kdebug ("block requested (sent)\n") |
| 130 | Iris::recv.reply.invoke (0, 0, device.copy ()) |
| 131 | Iris::free_cap (device) |
| 132 | have_device = false |
| 133 | ++next |
| 134 | else: |
| 135 | kdebug ("block requested (not sent)\n") |
| 136 | reply[Iris::recv.protected_data.l - 2] = Iris::get_reply () |
| 137 | have_reply[Iris::recv.protected_data.l - 2] = true |
| 138 | break |
| 139 | case Iris::Parent::INIT_DONE: |
| 140 | kdebug ("init done\n") |
| 141 | // Ignore. |
| 142 | Iris::recv.reply.invoke () |
| 143 | break |
| 144 | case Iris::Parent::EXIT: |
| 145 | Iris::panic (Iris::recv.protected_data.l, "boot thread exits") |
| 146 | default: |
| 147 | Iris::panic (Iris::recv.protected_data.l, "invalid boot request") |
| 148 | |
| 149 | static bool stringcmp (char const *s1, char const *s2, unsigned size): |
| 150 | for unsigned t = 0; t < size; ++t: |
| 151 | if s1[t] != s2[t]: |
| 152 | return false |
| 153 | return true |
| 154 | |
| 155 | static Iris::Block find (Iris::Directory root, char const *name): |
| 156 | unsigned size = 0 |
| 157 | while name[size]: |
| 158 | ++size |
| 159 | Iris::Num num_files = root.get_size () |
| 160 | for Iris::Num i = 0; i.value () < num_files.value (); i = i.value () + 1: |
| 161 | Iris::String n = root.get_name (i) |
| 162 | char current_name[16] |
| 163 | n.get_chars (0, current_name) |
| 164 | Iris::free_cap (n) |
| 165 | if !stringcmp (current_name, name, size): |
| 166 | continue |
| 167 | // Found elfrun. |
| 168 | Iris::Block ret = root.get_file_ro (i) |
| 169 | return ret |
| 170 | Iris::panic (0, "bootfile not found") |
| 171 | |
| 172 | static void run (Iris::Block data, Iris::Memory parent_memory, Iris::Cap parent): |
| 173 | // Get the size. |
| 174 | Iris::Num size = data.get_size () |
| 175 | if size.value () == 0: |
| 176 | Iris::panic (0, "elfrun is empty") |
| 177 | // Allocate a caps with all the pages. |
| 178 | unsigned pages = (size.value () + PAGE_SIZE - 1) >> PAGE_BITS |
| 179 | Iris::Caps pages_caps = Iris::my_memory.create_caps (pages) |
| 180 | unsigned slot = pages_caps.use () |
| 181 | // Map them into the address space as well. |
| 182 | char *mapping = alloc_space (pages) |
| 183 | // Create a memory for the program. |
| 184 | Iris::Memory mem = parent_memory.create_memory () |
| 185 | // Load the file into memory and map it. |
| 186 | for unsigned p = 0; p < pages; ++p: |
| 187 | //kdebug_num (p) |
| 188 | //kdebug ("/") |
| 189 | //kdebug_num (pages) |
| 190 | //kdebug ("\n") |
| 191 | Iris::set_recv_arg (Iris::Cap (slot, p)) |
| 192 | Iris::my_memory.create_page () |
| 193 | Iris::Page (slot, p).set_flags (Iris::Page::PAYING) |
| 194 | data.get_block (p << PAGE_BITS, PAGE_SIZE, 0, Iris::Cap (slot, p)) |
| 195 | Iris::my_memory.map (Iris::Cap (slot, p), (unsigned)&mapping[p << PAGE_BITS]) |
| 196 | Iris::Thread thread = mem.create_thread (NUM_SLOTS) |
| 197 | Elf32_Ehdr *header = (Elf32_Ehdr *)mapping |
| 198 | for unsigned j = 0; j < SELFMAG; ++j: |
| 199 | if header->e_ident[j] != ELFMAG[j]: |
| 200 | Iris::panic (header->e_ident[j], "invalid ELF magic") |
| 201 | return |
| 202 | if header->e_ident[EI_CLASS] != ELFCLASS32: |
| 203 | kdebug ("invalid ELF class:") |
| 204 | kdebug_num (header->e_ident[EI_CLASS]) |
| 205 | kdebug (" != ") |
| 206 | kdebug_num (ELFCLASS32) |
| 207 | kdebug ("\n") |
| 208 | Iris::panic (0) |
| 209 | return |
| 210 | if header->e_ident[EI_DATA] != ELFDATA2LSB: |
| 211 | Iris::panic (header->e_ident[EI_DATA], "invalid ELF data") |
| 212 | if header->e_ident[EI_VERSION] != EV_CURRENT: |
| 213 | Iris::panic (header->e_ident[EI_VERSION], "invalid ELF version") |
| 214 | if header->e_type != ET_EXEC: |
| 215 | Iris::panic (header->e_type, "invalid ELF type") |
| 216 | if header->e_machine != EM_MIPS_RS3_LE && header->e_machine != EM_MIPS: |
| 217 | Iris::panic (header->e_machine, "invalid ELF machine") |
| 218 | thread.set_pc (header->e_entry) |
| 219 | thread.set_sp (0x80000000) |
| 220 | for unsigned section = 0; section < header->e_shnum; ++section: |
| 221 | Elf32_Shdr *shdr = (Elf32_Shdr *)((unsigned)mapping + header->e_shoff + section * header->e_shentsize) |
| 222 | if ~shdr->sh_flags & SHF_ALLOC: |
| 223 | continue |
| 224 | bool readonly = !(shdr->sh_flags & SHF_WRITE) |
| 225 | //bool executable = shdr->sh_flags & SHF_EXEC_INSTR |
| 226 | if shdr->sh_type != SHT_NOBITS: |
| 227 | unsigned file_offset = shdr->sh_offset >> PAGE_BITS |
| 228 | if (file_offset + ((shdr->sh_size + PAGE_SIZE - 1) >> PAGE_BITS)) >= (PAGE_SIZE >> 2): |
| 229 | Iris::panic (shdr->sh_size, "thread too large") |
| 230 | return |
| 231 | for unsigned p = (shdr->sh_addr & PAGE_MASK); p < shdr->sh_addr + shdr->sh_size; p += PAGE_SIZE: |
| 232 | unsigned section_offset = (p - (shdr->sh_addr & PAGE_MASK)) >> PAGE_BITS |
| 233 | unsigned idx = file_offset + section_offset |
| 234 | Iris::Page page = mem.mapping ((void *)p) |
| 235 | if Iris::recv.data[0].l == Iris::NO_ERROR: |
| 236 | // The address already has a mapping; assume that it is correct. |
| 237 | Iris::free_cap (page) |
| 238 | continue |
| 239 | Iris::free_cap (page) |
| 240 | page = mem.create_page () |
| 241 | unsigned f |
| 242 | if readonly: |
| 243 | f = Iris::Page::PAYING | Iris::Page::MAPPED_READONLY |
| 244 | else: |
| 245 | f = Iris::Page::PAYING |
| 246 | page.set_flags (f) |
| 247 | Iris::Page (slot, idx).share (page, 0) |
| 248 | //kdebug ("mapping at ") |
| 249 | //kdebug_num (p) |
| 250 | //if readonly: |
| 251 | // kdebug (" (readonly)") |
| 252 | //kdebug ("\n") |
| 253 | if !mem.map (page, p): |
| 254 | Iris::panic (0, "unable to map page") |
| 255 | return |
| 256 | Iris::free_cap (page) |
| 257 | else: |
| 258 | if readonly: |
| 259 | Iris::panic (0, "unwritable bss section") |
| 260 | return |
| 261 | for unsigned p = (shdr->sh_addr & PAGE_MASK); p < shdr->sh_addr + shdr->sh_size; p += PAGE_SIZE: |
| 262 | Iris::Page page = mem.mapping ((void *)p) |
| 263 | if Iris::recv.data[0].l == Iris::NO_ERROR: |
| 264 | // No error means there is a mapping. |
| 265 | page.share (bss_page, 0) |
| 266 | Iris::free_cap (page) |
| 267 | for unsigned a = p; a < ((p + PAGE_SIZE) & PAGE_MASK); a += 4: |
| 268 | if a >= shdr->sh_addr + shdr->sh_size: |
| 269 | break |
| 270 | if a < shdr->sh_addr: |
| 271 | continue |
| 272 | bss_mapping[(a & ~PAGE_MASK) >> 2] = 0 |
| 273 | else: |
| 274 | Iris::free_cap (page) |
| 275 | page = mem.create_page () |
| 276 | if Iris::recv.data[0].l != Iris::NO_ERROR: |
| 277 | Iris::panic (Iris::recv.data[0].l, "out of memory") |
| 278 | if !page.set_flags (Iris::Page::PAYING | Iris::Page::FRAME): |
| 279 | Iris::panic (0, "out of memory") |
| 280 | if !mem.map (page, p): |
| 281 | Iris::panic (0, "unable to map bss page") |
| 282 | Iris::free_cap (page) |
| 283 | for unsigned p = 0; p < pages; ++p: |
| 284 | Iris::my_memory.destroy (Iris::Page (slot, p)) |
| 285 | Iris::my_memory.destroy (pages_caps) |
| 286 | Iris::free_slot (slot) |
| 287 | Iris::Page stackpage = mem.create_page () |
| 288 | stackpage.set_flags (Iris::Page::PAYING | Iris::Page::FRAME) |
| 289 | if Iris::recv.data[0].l != Iris::NO_ERROR || !mem.map (stackpage, 0x7ffff000): |
| 290 | Iris::panic (Iris::recv.data[0].l, "unable to map initial stack page") |
| 291 | Iris::free_cap (stackpage) |
| 292 | Iris::Caps caps = mem.create_caps (NUM_CAPS) |
| 293 | thread.use (caps, 0) |
| 294 | thread.set_info (Iris::Thread::A0, NUM_SLOTS) |
| 295 | thread.set_info (Iris::Thread::A1, NUM_CAPS) |
| 296 | Iris::Receiver receiver = mem.create_receiver () |
| 297 | receiver.set_owner (thread.copy ()) |
| 298 | Iris::Cap call = receiver.create_call_capability () |
| 299 | caps.set (__caps_num, caps.copy ()) |
| 300 | caps.set (__receiver_num, receiver.copy ()) |
| 301 | caps.set (__thread_num, thread.copy ()) |
| 302 | caps.set (__memory_num, mem.copy ()) |
| 303 | caps.set (__call_num, call.copy ()) |
| 304 | caps.set (__parent_num, parent) |
| 305 | thread.run () |
| 306 | Iris::free_cap (receiver) |
| 307 | Iris::free_cap (thread) |
| 308 | Iris::free_cap (call) |
| 309 | Iris::free_cap (caps) |
| 310 | |
| 311 | Iris::Num start (): |
| 312 | // Wait for the debugging device to be active, in case there is one. |
| 313 | Iris::schedule () |
| 314 | kdebug ("Starting bootinit\n") |
| 315 | init_alloc () |
| 316 | bss_mapping = (unsigned *)alloc_space (1) |
| 317 | bss_page = Iris::my_memory.create_page () |
| 318 | Iris::my_memory.map (bss_page, (unsigned)bss_mapping) |
| 319 | |
| 320 | Iris::Memory top_memory = Iris::get_top_memory () |
| 321 | Iris::Directory root = receive_devices () |
| 322 | root.lock_ro () |
| 323 | Iris::Block run_block = find (root, ELFRUN_NAME) |
| 324 | Iris::Cap parent_cap = Iris::my_receiver.create_capability (0) |
| 325 | run (run_block, top_memory, parent_cap) |
| 326 | Iris::wait () |
| 327 | if Iris::recv.data[0].l != Iris::Parent::PROVIDE_CAPABILITY || Iris::recv.data[1].l != Iris::Elfrun::ID: |
| 328 | Iris::panic (0, "elfrun doesn't provide correct capability") |
| 329 | Iris::Cap reply = Iris::get_reply () |
| 330 | Iris::Elfrun elfrun = Iris::get_arg () |
| 331 | Iris::my_caps.set (parent_cap.idx (), Iris::Cap (CAP_NONE)) |
| 332 | Iris::free_cap (parent_cap) |
| 333 | reply.invoke () |
| 334 | Iris::free_cap (reply) |
| 335 | |
| 336 | parent_cap = Iris::my_receiver.create_capability (0) |
| 337 | Iris::Block init_block = find (root, INIT_NAME) |
| 338 | Iris::Caps init_caps = elfrun.run_block (top_memory.copy (), init_block.copy (), parent_cap.copy (), 8, 63) |
| 339 | |
| 340 | Iris::Thread init = init_caps.get (__thread_num) |
| 341 | init.make_priv () |
| 342 | init.run () |
| 343 | Iris::free_cap (init) |
| 344 | Iris::free_cap (init_caps) |
| 345 | |
| 346 | bool have_root = false |
| 347 | bool have_elfrun = false |
| 348 | while true: |
| 349 | Iris::wait () |
| 350 | switch Iris::recv.data[0].l: |
| 351 | case Iris::Parent::GET_CAPABILITY: |
| 352 | switch Iris::recv.data[1].l: |
| 353 | case Iris::Directory::ID: |
| 354 | if have_root: |
| 355 | Iris::panic (0, "Init requests root directory twice") |
| 356 | Iris::recv.reply.invoke (0, 0, root.copy ()) |
| 357 | have_root = true |
| 358 | break |
| 359 | case Iris::Elfrun::ID: |
| 360 | if have_elfrun: |
| 361 | Iris::panic (0, "Init requests elfrun twice") |
| 362 | Iris::recv.reply.invoke (0, 0, elfrun.copy ()) |
| 363 | have_elfrun = true |
| 364 | break |
| 365 | default: |
| 366 | Iris::panic (0, "Invalid device requested by init") |
| 367 | break |
| 368 | case Iris::Parent::INIT_DONE: |
| 369 | if Iris::recv.data[1].value () != 0: |
| 370 | Iris::recv.reply.invoke () |
| 371 | break |
| 372 | // Special response: kill boot threads. |
| 373 | Iris::Cap reply = Iris::get_reply () |
| 374 | root.unlock_ro () |
| 375 | reply.invoke () |
| 376 | Iris::free_cap (reply) |
| 377 | top_memory.destroy (Iris::my_memory) |
| 378 | Iris::panic (0, "bootinit should be destroyed") |
| 379 | default: |
| 380 | Iris::panic (Iris::recv.data[0].l, "invalid operation from init") |
| 381 |
Branches:
master
