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