1
0
mirror of git://projects.qi-hardware.com/iris.git synced 2024-11-16 23:35:21 +02:00
iris/include/iris.hhp

690 lines
20 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))
#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_LIST 0xc00
#define CAPTYPE_LISTITEM 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)
#define __caps_num 0
#define __receiver_num 1
#define __thread_num 2
#define __memory_num 3
#define __call_num 4
#define __parent_num 5
// 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)
namespace Iris:
struct Parent
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
ERR_INVALID_ARGUMENT
NUM_EXCEPTION_CODES
#ifndef NDEBUG
static inline const char *exception_name(int code):
const char *names[] = {
"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"
}
return names[code]
#endif
struct Num:
unsigned l, h
inline Num (unsigned long long n = 0) : l (n), h (n >> 32):
inline Num (unsigned ll, unsigned hh) : l (ll), h (hh):
inline unsigned long long value () const:
return ((unsigned long long)h << 32) | l
inline unsigned &low ():
return l
inline unsigned &high ():
return h
inline unsigned const &low () const:
return l
inline unsigned const &high () const:
return h
struct Cap
struct Caps
struct Receiver
struct Thread
struct Page
struct Memory
struct List
void print_caps ()
unsigned alloc_slot ()
Cap alloc_cap ()
void free_slot (unsigned slot)
void free_cap (Cap cap)
extern bool enable_debug
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
inline void invoke (Num d0 = 0, Num d1 = 0, Cap arg = Cap (CAP_NONE))
inline Num call (Num d0 = 0, Num d1 = 0)
inline Num icall (Num d0 = 0, Num d1 = 0)
inline Num ocall (Cap c, Num d0 = 0, Num d1 = 0)
inline Num iocall (Cap c, Num d0 = 0, Num d1 = 0)
inline void _invoke (IMessage const *i)
inline void _call (IMessage *i)
struct __recv_data_t:
Num data[2]
Num protected_data
Cap reply, arg
extern Caps my_caps
extern Receiver my_receiver
extern Thread my_thread
extern Memory my_memory
extern Cap my_call
extern Parent my_parent
extern __recv_data_t recv
inline Cap get_reply ():
Cap ret = recv.reply
recv.reply = alloc_cap ()
return ret
inline Cap get_arg ():
Cap ret = recv.arg
recv.arg = alloc_cap ()
return ret
inline void set_recv_arg (Cap c):
free_cap (recv.arg)
recv.arg = c
struct Cap::IMessage:
Num data[2]
Cap reply, arg
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) & 0x7fff
unsigned Cap::idx () const:
return code & 0xffff
void Cap::_invoke (IMessage const *i):
__recv_data_t *r = &recv
__asm__ volatile ("lw $v0, %1\n"
"\tlw $a0, 0($v0)\n"
"\tlw $a1, 4($v0)\n"
"\tlw $a2, 8($v0)\n"
"\tlw $a3, 12($v0)\n"
"\tlw $t0, 16($v0)\n"
"\tlw $t1, 20($v0)\n"
"\tlw $v0, %2\n"
"\tlw $t2, 24($v0)\n"
"\tlw $t3, 28($v0)\n"
"\tlw $v0, %0\n"
"\tsyscall\n"
"\tlw $v0, %2\n"
"\tsw $a0, 0($v0)\n"
"\tsw $a1, 4($v0)\n"
"\tsw $a2, 8($v0)\n"
"\tsw $a3, 12($v0)\n"
"\tsw $t0, 16($v0)\n"
"\tsw $t1, 20($v0)"
:: "m"(code), "m"(i), "m"(r)
: "memory", "v0", "a0", "a1", "a2", "a3", "t0", "t1", "t2", "t3")
void Cap::_call (IMessage *i):
i->reply = *this
my_call.copy ()._invoke (i)
void Cap::invoke (Num d0, Num d1, Cap arg):
IMessage i
i.data[0] = d0
i.data[1] = d1
i.reply = Cap (CAP_NONE)
i.arg = arg
_invoke (&i)
Num Cap::call (Num d0, Num d1):
IMessage i
i.data[0] = d0
i.data[1] = d1
i.arg = Cap (CAP_NONE)
_call (&i)
return recv.data[0]
Num Cap::icall (Num d0, Num d1):
IMessage i
i.data[0] = d0
i.data[1] = d1
i.arg = Cap (CAP_NONE)
_call (&i)
return recv.data[0]
Num Cap::ocall (Cap c, Num d0, Num d1):
IMessage i
i.data[0] = d0
i.data[1] = d1
i.arg = c
_call (&i)
return recv.data[0]
Num Cap::iocall (Cap c, Num d0, Num d1):
IMessage i
i.data[0] = d0
i.data[1] = d1
i.arg = c
_call (&i)
return recv.data[0]
struct Receiver : public Cap:
Receiver (unsigned slot, unsigned idx) : Cap (slot, idx):
Receiver (Cap c = Cap ()) : Cap (c):
enum request:
// Operations
SET_OWNER = CAPTYPE_RECEIVER + 1
CREATE_CAPABILITY
CREATE_CALL_CAPABILITY
CREATE_ASYNC_CALL_CAPABILITY
GET_PROTECTED
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):
ocall (owner, CAP_MASTER_DIRECT | Receiver::SET_OWNER)
Cap create_capability (Num protected_data):
icall (CAP_MASTER_DIRECT | CREATE_CAPABILITY, protected_data)
return get_arg ()
Num get_protected (Cap target):
return ocall (target, CAP_MASTER_DIRECT | GET_PROTECTED)
Num get_reply_protected_data ():
return call (CAP_MASTER_DIRECT | GET_REPLY_PROTECTED_DATA)
void set_reply_protected_data (Num data):
call (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):
call (CAP_MASTER_DIRECT | SET_ALARM, data)
inline void sleep (unsigned value)
Cap create_call_capability ():
icall (CAP_MASTER_DIRECT | CREATE_CALL_CAPABILITY)
return get_arg ()
Cap create_async_call_capability ():
icall (CAP_MASTER_DIRECT | CREATE_ASYNC_CALL_CAPABILITY)
return get_arg ()
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 = CAPTYPE_THREAD + 1
SET_INFO
USE_SLOT
GET_CAPS
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
DBG_SEND
PRIV_REBOOT
PRIV_POWEROFF
PRIV_SUSPEND
PRIV_BOOT
PRIV_PANIC
// These get/set_info are not arch-specific.
enum info_type:
PC = ~0
SP = ~1
FLAGS = ~2
// These are arch-specific.
AT = 1
V0
V1
A0
A1
A2
A3
T0
T1
T2
T3
T4
T5
T6
T7
S0
S1
S2
S3
S4
S5
S6
S7
T8
T9
K0
K1
GP
SP_
FP
RA
enum flags:
PRIV = 1 << 31
WAITING = 1 << 30
RUNNING = 1 << 29
USER_FLAGS = ~(PRIV | WAITING | RUNNING)
void make_priv ():
my_thread.ocall (*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):
call (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 set, unsigned reset = 0):
set_info (FLAGS, set, set | reset)
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 = true):
set_flags (run ? RUNNING : 0, run ? 0 : RUNNING)
void wait (bool wait):
set_flags (wait ? WAITING : 0, wait ? 0 : WAITING)
inline unsigned use (Caps caps, unsigned slot = alloc_slot ())
inline Caps get_caps (unsigned slot)
unsigned get_num_caps ():
return call (CAP_MASTER_DIRECT | GET_CAPS, ~0).l
struct Caps : public Cap:
Caps (unsigned slot, unsigned idx) : Cap (slot, idx):
Caps (Cap c = Cap ()) : Cap (c):
enum request:
// Not an operation; this capability can be used in Thread::USE_SLOT.
USE = CAPTYPE_CAPS + 1
GET
GET_SIZE
SET
TRUNCATE
PRINT
unsigned use (unsigned slot = alloc_slot ()):
return my_thread.use (*this, slot)
Cap get (unsigned idx):
call (CAP_MASTER_DIRECT | GET, idx)
return get_arg ()
unsigned get_size ():
return call (CAP_MASTER_DIRECT | GET_SIZE).l
void set (unsigned idx, Cap cap):
ocall (cap, CAP_MASTER_DIRECT | SET, idx)
void truncate (unsigned new_size):
call (CAP_MASTER_DIRECT | TRUNCATE, new_size)
void print (unsigned idx):
invoke (CAP_MASTER_DIRECT | PRINT, idx)
Caps Thread::get_caps (unsigned slot):
call (CAP_MASTER_DIRECT | GET_CAPS, slot)
return get_arg ()
unsigned Thread::use (Caps caps, unsigned slot):
ocall (caps, CAP_MASTER_DIRECT | USE_SLOT, slot)
return slot
struct Page : public Cap:
Page (unsigned slot, unsigned idx) : Cap (slot, idx):
Page (Cap c = Cap ()) : Cap (c):
enum request:
SHARE = CAPTYPE_PAGE + 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 = 1
// Make the target independent of the source (make a copy if needed).
COPY = 2
// 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
// If this flag is set, the page is or will be mapped read-only.
MAPPED_READONLY = 0x10
// This is a read-only flag, saying if this is physical memory, which mustn't be freed.
PHYSICAL = 0x20
// This is a read-only flag, saying if this is uncachable memory.
UNCACHED = 0x40
void share (Cap target, unsigned flags = 0):
ocall (target, Num (CAP_MASTER_DIRECT | SHARE, flags))
unsigned get_flags ():
return call (CAP_MASTER_DIRECT | GET_FLAGS).l
bool set_flags (unsigned set, unsigned reset = 0):
call (CAP_MASTER_DIRECT | SET_FLAGS, Num (set, set | reset))
return recv.data[0].l == NO_ERROR
unsigned long physical_address ():
return my_thread.ocall (*this, CAP_MASTER_DIRECT | Thread::PRIV_PHYSICAL_ADDRESS).l
void alloc_physical (unsigned long address, bool cachable, bool freeable):
my_thread.ocall (*this, CAP_MASTER_DIRECT | Thread::PRIV_ALLOC_PHYSICAL, (address & PAGE_MASK) | (cachable ? 1 : 0) | (freeable ? 2 : 0))
struct Listitem : public Cap:
Listitem (Cap c = Cap ()) : Cap (c):
enum request:
CLEAR = CAPTYPE_LISTITEM + 1
SET_CAP
ADD
LIST
// Remove the listitem from its list.
void clear ():
call (CAP_MASTER_DIRECT | CLEAR)
// Set the capability of an item.
void set_cap (Cap c):
call (CAP_MASTER_DIRECT | SET_CAP, c.code)
// To add a listitem to a list, the listitem capability must be of type ADD or MASTER.
// A list iterator must be of type LIST or MASTER
struct List : public Cap:
List (Cap c = Cap ()) : Cap (c):
enum request:
GET_NEXT = CAPTYPE_LIST + 1
SET_CB
ADD_ITEM
GET_INFO
SET_INFO
GET_CAP
// Get the next listitem from the given one. Use this to loop over all listitems.
Listitem get_next (Listitem current = Listitem ()):
iocall (current, CAP_MASTER_DIRECT | GET_NEXT)
if recv.data[0].l:
return Cap ()
return get_arg ()
// Set the callback. This is called when an item is removed from the list.
// When called, data[0] is 0 when the list is now empty; 1 otherwise. data[1] is the removed item's info.
void set_cb (Cap cb):
ocall (cb, CAP_MASTER_DIRECT | SET_CB)
// Add an item to the front of the list.
void add_item (Listitem item):
ocall (item, CAP_MASTER_DIRECT | ADD_ITEM)
// Return item info.
Num get_info (Listitem item):
return ocall (item, CAP_MASTER_DIRECT | GET_INFO)
// Set item info.
void set_info (Listitem item, Num info):
ocall (item, CAP_MASTER_DIRECT | SET_INFO, info)
// Get item capability.
Cap get_cap (Listitem item):
iocall (item, CAP_MASTER_DIRECT | GET_CAP)
return get_arg ()
struct Memory : public Cap:
Memory (unsigned slot, unsigned idx) : Cap (slot, idx):
Memory (Cap c = Cap ()) : Cap (c):
enum request:
CREATE = CAPTYPE_MEMORY + 1
DESTROY
LIST
MAP
MAPPING
GET_LIMIT
SET_LIMIT
Page create_page ():
icall (Num (CAP_MASTER_DIRECT | CREATE, CAPTYPE_PAGE))
return get_arg ()
Thread create_thread (unsigned slots):
icall (Num (CAP_MASTER_DIRECT | CREATE, CAPTYPE_THREAD), slots)
return get_arg ()
Receiver create_receiver ():
icall (Num (CAP_MASTER_DIRECT | CREATE, CAPTYPE_RECEIVER))
return get_arg ()
Memory create_memory ():
icall (Num (CAP_MASTER_DIRECT | CREATE, CAPTYPE_MEMORY))
return get_arg ()
Caps create_caps (unsigned size):
icall (Num (CAP_MASTER_DIRECT | CREATE, CAPTYPE_CAPS), size)
return get_arg ()
List create_list ():
icall (Num (CAP_MASTER_DIRECT | CREATE, CAPTYPE_LIST))
return get_arg ()
Listitem create_listitem ():
icall (Num (CAP_MASTER_DIRECT | CREATE, CAPTYPE_LISTITEM))
return get_arg ()
void destroy (Cap target):
ocall (target, CAP_MASTER_DIRECT | DESTROY)
// TODO: LIST
bool map (Cap page, unsigned long address):
return ocall (page, CAP_MASTER_DIRECT | MAP, address).l == NO_ERROR
bool unmap (Cap page):
return map (page, ~0)
Page mapping (void *address):
icall (CAP_MASTER_DIRECT | MAPPING, Num ((unsigned long)address))
return get_arg ()
unsigned get_limit ():
return call (CAP_MASTER_DIRECT | GET_LIMIT).l
void set_limit (unsigned limit):
call (CAP_MASTER_DIRECT | SET_LIMIT, limit)
unsigned alloc_range (unsigned pages):
return my_thread.ocall (*this, CAP_MASTER_DIRECT | Thread::PRIV_ALLOC_RANGE, pages).l
inline void wait ():
Cap::IMessage i
Cap ().copy ()._invoke (&i)
inline void schedule ():
my_thread.invoke (CAP_MASTER_DIRECT | Thread::SCHEDULE)
inline void register_interrupt (unsigned num):
my_thread.ocall (my_receiver, CAP_MASTER_DIRECT | Thread::PRIV_REGISTER_INTERRUPT, num)
inline void unregister_interrupt (unsigned num):
my_thread.call (CAP_MASTER_DIRECT | Thread::PRIV_REGISTER_INTERRUPT, num)
inline unsigned wait_for_interrupt ():
my_receiver.set_reply_protected_data (0)
Cap ().call ()
unsigned ret = recv.data[0].l
my_receiver.set_reply_protected_data (~0)
return ret
inline Cap get_top_memory ():
my_thread.icall (CAP_MASTER_DIRECT | Thread::PRIV_GET_TOP_MEMORY)
return get_arg ()
inline void dbg_send (unsigned code, unsigned bits = 32):
my_thread.call (CAP_MASTER_DIRECT | Thread::DBG_SEND, Num (code, bits))
inline void reboot ():
my_thread.call (CAP_MASTER_DIRECT | Thread::PRIV_REBOOT)
inline void poweroff ():
my_thread.call (CAP_MASTER_DIRECT | Thread::PRIV_POWEROFF)
inline void suspend ():
my_thread.call (CAP_MASTER_DIRECT | Thread::PRIV_SUSPEND)
inline void boot (unsigned address, unsigned arg):
my_thread.call (CAP_MASTER_DIRECT | Thread::PRIV_BOOT, Iris::Num (address, arg))
void Receiver::sleep (unsigned value):
set_alarm (value)
Cap ().call ()
inline void sleep (unsigned value):
my_receiver.sleep (value)
// The start function has this prototype (there is no main function).
Iris::Num start ()
#ifndef __KERNEL__
#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)
#define __stringify2(x) #x
#define __stringify(x) __stringify2 (x)
#define kdebug_line() do { kdebug (__FILE__ ":"); kdebug (__PRETTY_FUNCTION__); kdebug (":"); kdebug (__stringify (__LINE__)); kdebug_char ('\n'); } while (0)
static void kdebug_num (unsigned n, unsigned digits = 8):
unsigned i
char const *encode = "0123456789abcdef"
for i = 0; i < digits; ++i:
kdebug_char (encode[(n >> (4 * ((digits - 1) - i))) & 0xf])
namespace Iris:
inline void panic (unsigned code, char const *message = NULL):
if message:
kdebug_num (code)
kdebug_char ('\n')
kdebug (message)
kdebug_char ('\n')
my_thread.call (CAP_MASTER_DIRECT | Thread::PRIV_PANIC, code)
if code == 0xdeaddead:
return
while true:
wait ()
inline void debug_num (unsigned num, unsigned base):
char const *encode = "0123456789abcdef"
unsigned digits = 1
unsigned power = 1
while power <= num / base:
power *= base
++digits
for unsigned i = 0; i < digits; ++i:
unsigned d = num / power
kdebug_char (encode[d])
num -= d * power
power /= base
inline void debug (const char *f, ...):
unsigned *last = (unsigned *)&f
while *f:
if *f == '%':
++f
switch *f:
case '%':
kdebug_char ('%')
break
case 'd':
++last
debug_num (*last, 10)
break
case 'x':
++last
debug_num (*last, 0x10)
break
case 's':
++last
kdebug ((char *)*last)
break
case 'c':
++last
kdebug_char (*last)
break
default:
panic (*f, "invalid character in dbg format string")
else:
kdebug_char (*f)
++f
#endif
#endif