mirror of
git://projects.qi-hardware.com/iris.git
synced 2024-11-05 13:26:16 +02:00
515 lines
15 KiB
Plaintext
515 lines
15 KiB
Plaintext
#pypp 0
|
|
// Iris: micro-kernel for a capability-based operating system.
|
|
// iris.hhp: header file for userspace programs.
|
|
// Copyright 2009 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/>.
|
|
|
|
#ifndef __IRIS_HHP
|
|
#define __IRIS_HHP
|
|
|
|
// Without the standard library, we don't have this definition.
|
|
// I preferred ((void*)0), but C++ has too strict type-checking to
|
|
// make that work.
|
|
#ifndef NULL
|
|
#define NULL 0
|
|
#endif
|
|
|
|
// Number of clock interrupts per second.
|
|
#define HZ 100
|
|
|
|
#define PAGE_BITS (12)
|
|
#define PAGE_SIZE (1 << PAGE_BITS)
|
|
#define PAGE_MASK (~(PAGE_SIZE - 1))
|
|
|
|
enum Exception_code:
|
|
NO_ERROR
|
|
ERR_WRITE_DENIED
|
|
ERR_UNMAPPED_READ
|
|
ERR_UNMAPPED_WRITE
|
|
ERR_INVALID_ADDRESS_READ
|
|
ERR_INVALID_ADDRESS_WRITE
|
|
ERR_RESERVED_INSTRUCTION
|
|
ERR_COPROCESSOR_UNUSABLE
|
|
ERR_OVERFLOW
|
|
ERR_TRAP
|
|
ERR_WATCHPOINT
|
|
ERR_BREAKPOINT
|
|
ERR_NO_PAGE_DIRECTORY
|
|
ERR_NO_PAGE_TABLE
|
|
ERR_OUT_OF_MEMORY
|
|
// The following are not raised, but returned.
|
|
ERR_INVALID_OPERATION
|
|
NUM_EXCEPTION_CODES
|
|
|
|
#ifndef NDEBUG
|
|
static const char *exception_name[NUM_EXCEPTION_CODES] = {
|
|
"no error",
|
|
"write denied",
|
|
"unmapped read",
|
|
"unmapped write",
|
|
"invalid address read",
|
|
"invalid address write",
|
|
"reserved instruction",
|
|
"coprocessor unusable",
|
|
"overflow",
|
|
"trap",
|
|
"watchpoint",
|
|
"breakpoint",
|
|
"no page directory",
|
|
"no page table",
|
|
"out of memory",
|
|
"invalid operation"
|
|
}
|
|
#endif
|
|
|
|
#define KERNEL_MASK 0xfff
|
|
#define CAPTYPE_MASK 0xe00
|
|
#define REQUEST_MASK (KERNEL_MASK & ~CAPTYPE_MASK)
|
|
#define CAPTYPE_INVALID 0x000
|
|
#define CAPTYPE_RECEIVER 0x200
|
|
#define CAPTYPE_MEMORY 0x400
|
|
#define CAPTYPE_THREAD 0x600
|
|
#define CAPTYPE_PAGE 0x800
|
|
#define CAPTYPE_CAPS 0xa00
|
|
//#define CAPTYPE_??? 0xc00
|
|
//#define CAPTYPE_??? 0xe00
|
|
|
|
// All kernel capabilities have a master capability, which can create others.
|
|
#define CAP_MASTER 0
|
|
|
|
// Create, invoke and forget, with masked data set to 0.
|
|
#define CAP_MASTER_DIRECT 0
|
|
// Master capabilities can create others.
|
|
#define CAP_MASTER_CREATE (1 << 31)
|
|
|
|
struct Num:
|
|
unsigned l, h
|
|
Num (unsigned long long n = 0) : l (n), h (n >> 32):
|
|
Num (unsigned ll, unsigned hh) : l (ll), h (hh):
|
|
unsigned long long value () const:
|
|
return ((unsigned long long)h << 32) | l
|
|
unsigned &low ():
|
|
return l
|
|
unsigned &high ():
|
|
return h
|
|
unsigned const &low () const:
|
|
return l
|
|
unsigned const &high () const:
|
|
return h
|
|
|
|
// The start function has this prototype (there is no main function).
|
|
Num start ()
|
|
|
|
struct Cap
|
|
struct Caps
|
|
struct Receiver
|
|
struct Thread
|
|
struct Page
|
|
struct Memory
|
|
|
|
#define __receiver_num 0
|
|
#define __thread_num 1
|
|
#define __memory_num 2
|
|
#define __caps_num 3
|
|
#define __call_num 4
|
|
#define __parent_num 5
|
|
#define __tmp_num 6
|
|
|
|
// If this flag is set in a capability, it is copied instead of mapped.
|
|
// If it is set in the target capability, the Thread waits after the request.
|
|
#define CAP_COPY ((unsigned)0x80000000)
|
|
// This constant signifies that no capability is passed.
|
|
#define CAP_NONE (~CAP_COPY)
|
|
|
|
extern Receiver __my_receiver
|
|
extern Thread __my_thread
|
|
extern Memory __my_memory
|
|
extern Cap __my_call
|
|
extern Cap __my_parent
|
|
extern Caps __my_caps
|
|
extern Caps __tmp_caps
|
|
|
|
unsigned alloc_slot ()
|
|
unsigned alloc_cap ()
|
|
void free_slot (unsigned slot)
|
|
void free_cap (Cap cap)
|
|
|
|
#define __tmp_slot 1
|
|
|
|
struct Cap:
|
|
unsigned code
|
|
inline Cap copy () const
|
|
inline Cap ()
|
|
explicit inline Cap (unsigned c)
|
|
inline Cap (unsigned slot, unsigned idx)
|
|
inline unsigned slot () const
|
|
inline unsigned idx () const
|
|
struct IMessage
|
|
struct OMessage
|
|
inline void invoke (IMessage const *i, OMessage *o)
|
|
inline void call (IMessage *i, OMessage *o)
|
|
inline void invoke (Cap c, Num d0 = 0, Num d1 = 0, Caps caps = __tmp_caps)
|
|
inline void invoke (Num d0 = 0, Num d1 = 0)
|
|
inline Num call (Cap c, Num d0 = 0, Num d1 = 0, Caps caps = __tmp_caps, unsigned slot = ~0)
|
|
inline Num call (Num d0 = 0, Num d1 = 0, Caps caps = __tmp_caps, unsigned slot = ~0)
|
|
inline void clone (Caps caps, unsigned idx)
|
|
|
|
struct Caps : public Cap:
|
|
Caps (unsigned slot, unsigned idx) : Cap (slot, idx):
|
|
Caps (Cap c = Cap ()) : Cap (c):
|
|
|
|
struct Cap::IMessage:
|
|
Num data[2]
|
|
Caps caps
|
|
unsigned slot
|
|
unsigned num, first
|
|
Cap *set
|
|
struct Cap::OMessage:
|
|
Num data[2]
|
|
Num cap_protected
|
|
Num recv_protected
|
|
Cap Cap::copy () const:
|
|
return Cap (code | CAP_COPY)
|
|
Cap::Cap () : code (CAP_NONE):
|
|
Cap::Cap (unsigned c) : code (c):
|
|
Cap::Cap (unsigned slot, unsigned idx) : code (idx | (slot << 16)):
|
|
unsigned Cap::slot () const:
|
|
return code >> 16
|
|
unsigned Cap::idx () const:
|
|
return code & 0xffff
|
|
void Cap::invoke (IMessage const *i, OMessage *o):
|
|
switch i->num:
|
|
default:
|
|
__asm__ volatile ("lw $t9, %0" :: "m"(i->set[9].code) : "t9")
|
|
case 9:
|
|
__asm__ volatile ("lw $t8, %0" :: "m"(i->set[8].code) : "t8")
|
|
case 8:
|
|
__asm__ volatile ("lw $t7, %0" :: "m"(i->set[7].code) : "t7")
|
|
case 7:
|
|
__asm__ volatile ("lw $t6, %0" :: "m"(i->set[6].code) : "t6")
|
|
case 6:
|
|
__asm__ volatile ("lw $t5, %0" :: "m"(i->set[5].code) : "t5")
|
|
case 5:
|
|
__asm__ volatile ("lw $t4, %0" :: "m"(i->set[4].code) : "t4")
|
|
case 4:
|
|
__asm__ volatile ("lw $t3, %0" :: "m"(i->set[3].code) : "t3")
|
|
case 3:
|
|
__asm__ volatile ("lw $t2, %0" :: "m"(i->set[2].code) : "t2")
|
|
case 2:
|
|
__asm__ volatile ("lw $t1, %0" :: "m"(i->set[1].code) : "t1")
|
|
case 1:
|
|
__asm__ volatile ("lw $t0, %0" :: "m"(i->set[0].code) : "t0")
|
|
case 0:
|
|
break
|
|
__asm__ volatile ("lw $v0, %2\n"
|
|
"\tlw $a0, 0($v0)\n"
|
|
"\tlw $a1, 4($v0)\n"
|
|
"\tlw $a2, 8($v0)\n"
|
|
"\tlw $a3, 12($v0)\n"
|
|
"\tlw $s0, 16($v0)\n"
|
|
"\tlw $s1, 20($v0)\n"
|
|
"\tlw $s2, 24($v0)\n"
|
|
"\tlw $s3, 28($v0)\n"
|
|
"\tlw $v0, %1\n"
|
|
"\tsyscall\n"
|
|
"\tlw $v0, %0\n"
|
|
"\tsw $a0, 0($v0)\n"
|
|
"\tsw $a1, 4($v0)\n"
|
|
"\tsw $a2, 8($v0)\n"
|
|
"\tsw $a3, 12($v0)\n"
|
|
"\tsw $s0, 16($v0)\n"
|
|
"\tsw $s1, 20($v0)\n"
|
|
"\tsw $s2, 24($v0)\n"
|
|
"\tsw $s3, 28($v0)\n"
|
|
: "=m"(o)
|
|
: "m"(code), "m"(i)
|
|
: "memory", "v0", "s0", "s1", "s2", "s3", "a0", "a1", "a2", "a3")
|
|
void Cap::call (IMessage *i, OMessage *o):
|
|
i->set[0] = *this
|
|
__my_call.copy ().invoke (i, o)
|
|
void Cap::invoke (Cap c, Num d0, Num d1, Caps caps):
|
|
IMessage i
|
|
OMessage o
|
|
i.slot = ~0
|
|
i.caps = caps
|
|
Cap cs[2]
|
|
cs[0] = c
|
|
i.set = cs
|
|
i.num = 2
|
|
i.first = 0
|
|
i.data[0] = d0
|
|
i.data[1] = d1
|
|
invoke (&i, &o)
|
|
void Cap::invoke (Num d0, Num d1):
|
|
IMessage i
|
|
OMessage o
|
|
i.caps = Cap ()
|
|
i.slot = ~0
|
|
i.num = 0
|
|
i.data[0] = d0
|
|
i.data[1] = d1
|
|
invoke (&i, &o)
|
|
Num Cap::call (Cap c, Num d0, Num d1, Caps caps, unsigned slot):
|
|
IMessage i
|
|
OMessage o
|
|
Cap cs[2]
|
|
cs[1] = c
|
|
i.set = cs
|
|
i.caps = caps
|
|
i.slot = slot
|
|
i.num = 2
|
|
i.first = 0
|
|
i.data[0] = d0
|
|
i.data[1] = d1
|
|
invoke (&i, &o)
|
|
return o.data[0]
|
|
Num Cap::call (Num d0, Num d1, Caps caps, unsigned slot):
|
|
IMessage i
|
|
OMessage o
|
|
Cap cs[2]
|
|
i.set = cs
|
|
i.caps = caps
|
|
i.slot = slot
|
|
i.num = 2
|
|
i.first = 0
|
|
i.data[0] = d0
|
|
i.data[1] = d1
|
|
invoke (&i, &o)
|
|
return o.data[0]
|
|
void Cap::clone (Caps caps, unsigned idx):
|
|
IMessage i
|
|
OMessage o
|
|
Cap c = this->copy ()
|
|
i.set = &c
|
|
i.caps = caps
|
|
i.first = idx
|
|
i.num = 1
|
|
i.slot = ~0
|
|
i.data[0] = 0
|
|
i.data[1] = 0
|
|
invoke (&i, &o)
|
|
|
|
struct Receiver : public Cap:
|
|
Receiver (unsigned slot, unsigned idx) : Cap (slot, idx):
|
|
Receiver (Cap c = Cap ()) : Cap (c):
|
|
enum request:
|
|
// Operations
|
|
SET_OWNER = 1
|
|
CREATE_CAPABILITY
|
|
CREATE_CALL_CAPABILITY
|
|
CREATE_ASYNC_CALL_CAPABILITY
|
|
GET_REPLY_PROTECTED_DATA
|
|
SET_REPLY_PROTECTED_DATA
|
|
GET_ALARM
|
|
SET_ALARM
|
|
ADD_ALARM
|
|
// Reply capability. This can only be created by invoking a CALL or CALL_ASYNC capability.
|
|
REPLY
|
|
// A call capability. This can only be created by invoking CREATE_CALL_CAPABILITY.
|
|
CALL
|
|
// A call capability, waiting for only this reply is disabled. This can only be created by invoking CREATE_CALL_ASYNC_CAPABILITY.
|
|
CALL_ASYNC
|
|
void set_owner (Cap owner):
|
|
invoke (owner, CAP_MASTER_DIRECT | Receiver::SET_OWNER)
|
|
Cap create_capability (unsigned protected_data, Cap ret = Cap (0, alloc_cap ())):
|
|
call (ret, CAP_MASTER_DIRECT | CREATE_CAPABILITY, protected_data)
|
|
return ret
|
|
Num get_reply_protected_data ():
|
|
return call (CAP_MASTER_DIRECT | GET_REPLY_PROTECTED_DATA)
|
|
void set_reply_protected_data (Num data):
|
|
invoke (CAP_MASTER_DIRECT | SET_REPLY_PROTECTED_DATA, data)
|
|
unsigned get_alarm ():
|
|
return call (CAP_MASTER_DIRECT | GET_ALARM).l
|
|
unsigned add_alarm (unsigned data):
|
|
return call (CAP_MASTER_DIRECT | ADD_ALARM, data).l
|
|
void set_alarm (unsigned data):
|
|
invoke (CAP_MASTER_DIRECT | SET_ALARM, data)
|
|
static inline void sleep (unsigned value, OMessage *ret, unsigned slot = __tmp_slot)
|
|
Cap create_call_capability (Cap ret = Cap (0, alloc_cap ())):
|
|
call (ret, CAP_MASTER_DIRECT | CREATE_CALL_CAPABILITY)
|
|
return ret
|
|
Cap create_async_call_capability (Cap ret = Cap (0, alloc_cap ())):
|
|
call (ret, CAP_MASTER_DIRECT | CREATE_ASYNC_CALL_CAPABILITY)
|
|
return ret
|
|
|
|
struct Thread : public Cap:
|
|
Thread (unsigned slot, unsigned idx) : Cap (slot, idx):
|
|
Thread (Cap c = Cap ()) : Cap (c):
|
|
enum request:
|
|
// Info details are arch-specific.
|
|
GET_INFO = 1
|
|
SET_INFO
|
|
SCHEDULE
|
|
PRIV_ALLOC_RANGE
|
|
PRIV_PHYSICAL_ADDRESS
|
|
PRIV_ALLOC_PHYSICAL
|
|
PRIV_MAKE_PRIV
|
|
PRIV_GET_TOP_MEMORY
|
|
PRIV_REGISTER_INTERRUPT
|
|
// This is not an operation, but having this capability allows using the thread in Receiver::set_owner.
|
|
SET_OWNER
|
|
// These get/set_info are not arch-specific.
|
|
enum info_type:
|
|
PC = ~0
|
|
SP = ~1
|
|
FLAGS = ~2
|
|
enum flags:
|
|
PRIV = 1 << 31
|
|
WAITING = 1 << 30
|
|
RUNNING = 1 << 29
|
|
USER_FLAGS = ~(PRIV | WAITING | RUNNING)
|
|
void make_priv ():
|
|
__my_thread.invoke (*this, CAP_MASTER_DIRECT | PRIV_MAKE_PRIV)
|
|
unsigned get_info (unsigned info):
|
|
return call (Num (CAP_MASTER_DIRECT | GET_INFO, info)).l
|
|
void set_info (unsigned info, unsigned value, unsigned mask = ~0):
|
|
invoke (Num (CAP_MASTER_DIRECT | SET_INFO, info), Num (value, mask))
|
|
void set_pc (unsigned pc):
|
|
set_info (PC, pc)
|
|
void set_sp (unsigned sp):
|
|
set_info (SP, sp)
|
|
void set_flags (unsigned value, unsigned mask):
|
|
set_info (FLAGS, value, mask)
|
|
unsigned get_pc ():
|
|
return get_info (PC)
|
|
unsigned get_sp ():
|
|
return get_info (SP)
|
|
unsigned get_flags ():
|
|
return get_info (FLAGS)
|
|
void run (bool run):
|
|
set_flags (run ? RUNNING : 0, RUNNING)
|
|
void wait (bool wait):
|
|
set_flags (wait ? WAITING : 0, WAITING)
|
|
|
|
struct Page : public Cap:
|
|
Page (unsigned slot, unsigned idx) : Cap (slot, idx):
|
|
Page (Cap c = Cap ()) : Cap (c):
|
|
enum request:
|
|
SHARE = 1
|
|
GET_FLAGS
|
|
SET_FLAGS
|
|
// Not an operation; a capability with this bit cannot write to the page.
|
|
READONLY = 8
|
|
enum share_detail:
|
|
// Operation details for PAGE_SHARE
|
|
// Forget the source page during the operation. This makes it a move.
|
|
FORGET
|
|
// Make the target independent of the source (make a copy if needed).
|
|
COPY
|
|
// Make the target unwritable.
|
|
//READONLY: use the value from request.
|
|
enum flag_values:
|
|
// This is a read-only flag, which is set if the Page is shared.
|
|
SHARED = 1
|
|
// When paying, the memory's use is incremented. If a frame is held, it cannot be lost. Frames are lost when the last payer forgets them.
|
|
PAYING = 2
|
|
// Set if this page has a frame associated with it. This flag is automatically reset if the frame is lost because of payment problems.
|
|
FRAME = 4
|
|
// A readonly page cannot be written to. This flag can not be reset while the frame is shared. The flag is already defined in request.
|
|
//READONLY = 8
|
|
// This is a read-only flag, saying if this is physical memory, which mustn't be freed.
|
|
PHYSICAL = 0x10
|
|
// This is a read-only flag, saying if this is uncachable memory.
|
|
UNCACHED = 0x20
|
|
void share (Cap target, unsigned flags):
|
|
invoke (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):
|
|
invoke (CAP_MASTER_DIRECT | SET_FLAGS, Num (new_flags, mask))
|
|
unsigned physical_address ():
|
|
return __my_thread.call (*this, CAP_MASTER_DIRECT | Thread::PRIV_PHYSICAL_ADDRESS).l
|
|
void alloc_physical (unsigned address, bool cachable, bool freeable):
|
|
__my_thread.invoke (*this, CAP_MASTER_DIRECT | Thread::PRIV_ALLOC_PHYSICAL, (address & PAGE_MASK) | (cachable ? 1 : 0) | (freeable ? 2 : 0))
|
|
|
|
struct Memory : public Cap:
|
|
Memory (unsigned slot, unsigned idx) : Cap (slot, idx):
|
|
Memory (Cap c = Cap ()) : Cap (c):
|
|
enum request:
|
|
CREATE = 1
|
|
DESTROY
|
|
LIST
|
|
MAP
|
|
MAPPING
|
|
GET_LIMIT
|
|
SET_LIMIT
|
|
Page create_page (Page ret = Cap (0, alloc_cap ())):
|
|
call (ret, Num (CAP_MASTER_DIRECT | CREATE, CAPTYPE_PAGE))
|
|
return ret
|
|
Thread create_thread (unsigned slots, Thread ret = Cap (0, alloc_cap ())):
|
|
call (ret, Num (CAP_MASTER_DIRECT | CREATE, CAPTYPE_THREAD), slots)
|
|
return ret
|
|
Receiver create_receiver (Receiver ret = Cap (0, alloc_cap ())):
|
|
call (ret, Num (CAP_MASTER_DIRECT | CREATE, CAPTYPE_RECEIVER))
|
|
return ret
|
|
Memory create_memory (Memory ret = Cap (0, alloc_cap ())):
|
|
call (ret, Num (CAP_MASTER_DIRECT | CREATE, CAPTYPE_MEMORY))
|
|
return ret
|
|
Caps create_caps (unsigned size, Caps ret = Cap (0, alloc_cap ())):
|
|
call (ret, Num (CAP_MASTER_DIRECT | CREATE, CAPTYPE_CAPS), size)
|
|
return ret
|
|
void destroy (Cap target):
|
|
invoke (target, CAP_MASTER_DIRECT | DESTROY)
|
|
// TODO: LIST
|
|
void map (Cap page, unsigned address, bool readonly):
|
|
if readonly:
|
|
address |= Page::READONLY
|
|
invoke (page, CAP_MASTER_DIRECT | MAP, address)
|
|
Page mapping (void *address, Page ret = Cap (0, alloc_cap ())):
|
|
call (ret, CAP_MASTER_DIRECT | MAPPING, Num ((unsigned)address))
|
|
return ret
|
|
unsigned get_limit (unsigned limit):
|
|
return call (CAP_MASTER_DIRECT | GET_LIMIT).l
|
|
void set_limit (unsigned limit):
|
|
invoke (CAP_MASTER_DIRECT | SET_LIMIT, limit)
|
|
|
|
struct Kernel:
|
|
static void wait (Cap::OMessage *o, unsigned slot = __tmp_slot):
|
|
Cap::IMessage i
|
|
i.slot = slot
|
|
i.num = 0
|
|
Cap ().copy ().invoke (&i, o)
|
|
static void schedule ():
|
|
__my_thread.invoke (CAP_MASTER_DIRECT | Thread::SCHEDULE)
|
|
static void register_interrupt (unsigned num):
|
|
__my_thread.invoke (__my_receiver, CAP_MASTER_DIRECT | Thread::PRIV_REGISTER_INTERRUPT, num)
|
|
static void unregister_interrupt (unsigned num):
|
|
__my_thread.invoke (CAP_MASTER_DIRECT | Thread::PRIV_REGISTER_INTERRUPT, num)
|
|
static Cap get_top_memory ():
|
|
__my_thread.call (__tmp_slot, CAP_MASTER_DIRECT | Thread::PRIV_GET_TOP_MEMORY)
|
|
return Cap (__tmp_slot, 0)
|
|
static unsigned alloc_range (Cap memory, unsigned pages):
|
|
__my_thread.call (memory, CAP_MASTER_DIRECT | Thread::PRIV_ALLOC_RANGE, pages)
|
|
|
|
void Receiver::sleep (unsigned value, OMessage *ret, unsigned slot):
|
|
__my_receiver.set_alarm (value)
|
|
Kernel::wait (ret, slot)
|
|
|
|
#if 1
|
|
// Use a define instead of an inline function, because this is better visible in disassembly, even when not optimizing.
|
|
#define kdebug_char(c) do { unsigned d = (c); __asm__ volatile ("move $a0, $zero\nlw $a1, %0\nbreak" :: "m"(d) : "a0", "a1", "memory"); } while (0)
|
|
#else
|
|
#define kdebug_char(c) do {} while (0)
|
|
#endif
|
|
#define kdebug(str) do { const char *s = (str); while (*s) { kdebug_char (*s); ++s; } } while (0)
|
|
|
|
static void kdebug_num (unsigned n):
|
|
unsigned i
|
|
const char *encode = "0123456789abcdef"
|
|
for i = 0; i < 8; ++i:
|
|
kdebug_char (encode[(n >> (4 * (7 - i))) & 0xf])
|
|
|
|
#endif
|