mirror of
git://projects.qi-hardware.com/iris.git
synced 2024-11-05 13:35:19 +02:00
509 lines
16 KiB
Plaintext
509 lines
16 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_??? 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)
|
|
|
|
#define __receiver_num 0
|
|
#define __thread_num 1
|
|
#define __memory_num 2
|
|
#define __call_num 3
|
|
#define __parent_num 4
|
|
|
|
// 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 Kernel:
|
|
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
|
|
|
|
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
|
|
|
|
struct Cap
|
|
struct Caps
|
|
struct Receiver
|
|
struct Thread
|
|
struct Page
|
|
struct Memory
|
|
|
|
unsigned alloc_slot ()
|
|
Cap alloc_cap ()
|
|
void free_slot (unsigned slot)
|
|
void free_cap (Cap cap)
|
|
|
|
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 Receiver my_receiver
|
|
extern Thread my_thread
|
|
extern Memory my_memory
|
|
extern Cap my_call
|
|
extern Cap 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
|
|
_call (&i)
|
|
return recv.data[0]
|
|
Num Cap::icall (Num d0, Num d1):
|
|
IMessage i
|
|
i.data[0] = d0
|
|
i.data[1] = d1
|
|
_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_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_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)
|
|
static 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_PANIC
|
|
// 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.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 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)
|
|
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
|
|
// 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):
|
|
ocall (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):
|
|
call (CAP_MASTER_DIRECT | SET_FLAGS, Num (new_flags, mask))
|
|
unsigned physical_address ():
|
|
return my_thread.ocall (*this, CAP_MASTER_DIRECT | Thread::PRIV_PHYSICAL_ADDRESS).l
|
|
void alloc_physical (unsigned 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 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 ()
|
|
void destroy (Cap target):
|
|
ocall (target, CAP_MASTER_DIRECT | DESTROY)
|
|
// TODO: LIST
|
|
void map (Cap page, unsigned address, bool readonly = false):
|
|
if readonly:
|
|
address |= Page::READONLY
|
|
ocall (page, CAP_MASTER_DIRECT | MAP, address)
|
|
Page mapping (void *address):
|
|
icall (CAP_MASTER_DIRECT | MAPPING, Num ((unsigned)address))
|
|
return get_arg ()
|
|
unsigned get_limit (unsigned 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 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 panic (unsigned code):
|
|
my_thread.call (CAP_MASTER_DIRECT | Thread::PRIV_PANIC, code)
|
|
|
|
void Receiver::sleep (unsigned value):
|
|
my_receiver.set_alarm (value)
|
|
wait ()
|
|
|
|
// The start function has this prototype (there is no main function).
|
|
Kernel::Num start ()
|
|
|
|
#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 (__stringify (__LINE__)); kdebug_char ('\n'); } while (0)
|
|
|
|
static void kdebug_num (unsigned n, unsigned digits = 8):
|
|
unsigned i
|
|
const char *encode = "0123456789abcdef"
|
|
for i = 0; i < digits; ++i:
|
|
kdebug_char (encode[(n >> (4 * ((digits - 1) - i))) & 0xf])
|
|
|
|
#endif
|