mirror of
git://projects.qi-hardware.com/iris.git
synced 2025-01-30 19:01:05 +02:00
more
This commit is contained in:
parent
e7f42c6b7e
commit
ef1b9bfe10
2
Makefile
2
Makefile
@ -9,7 +9,7 @@ OBJCOPY = $(CROSS)objcopy
|
||||
OBJDUMP = $(CROSS)objdump
|
||||
STRIP = $(CROSS)strip
|
||||
|
||||
kernel_sources = interrupts.cc panic.cc data.cc test.cc alloc.cc memory.cc arch.cc invoke.cc schedule.cc
|
||||
kernel_sources = interrupts.cc panic.cc data.cc test.cc alloc.cc arch.cc invoke.cc schedule.cc
|
||||
boot_sources = init.cc
|
||||
BUILT_SOURCES = $(kernel_sources) $(boot_sources)
|
||||
|
||||
|
70
alloc.ccp
70
alloc.ccp
@ -20,6 +20,23 @@ void Memory::unuse ():
|
||||
for Memory *m = this; m; m = m->address_space:
|
||||
--m->used
|
||||
|
||||
unsigned raw_zalloc ():
|
||||
FreePage *ret = zero_pages
|
||||
if !ret:
|
||||
ret = junk_pages
|
||||
for unsigned i = 1; i < (PAGE_SIZE >> 2); ++i:
|
||||
((unsigned *)ret)[i] = 0
|
||||
junk_pages = ret->next
|
||||
else:
|
||||
zero_pages = ret->next
|
||||
ret->next = NULL
|
||||
return (unsigned)ret
|
||||
|
||||
void raw_pfree (unsigned page):
|
||||
FreePage *p = (FreePage *)page
|
||||
p->next = junk_pages
|
||||
junk_pages = p
|
||||
|
||||
unsigned Memory::palloc ():
|
||||
if !use ():
|
||||
return NULL
|
||||
@ -34,23 +51,14 @@ unsigned Memory::palloc ():
|
||||
unsigned Memory::zalloc ():
|
||||
if !use ():
|
||||
return NULL
|
||||
FreePage *ret = zero_pages
|
||||
if !ret:
|
||||
ret = junk_pages
|
||||
for unsigned i = 1; i < (PAGE_SIZE >> 2); ++i:
|
||||
((unsigned *)ret)[i] = 0
|
||||
junk_pages = ret->next
|
||||
else:
|
||||
zero_pages = ret->next
|
||||
ret->next = NULL
|
||||
return (unsigned)ret
|
||||
return raw_zalloc ()
|
||||
|
||||
void Memory::pfree (unsigned page):
|
||||
FreePage *p = (FreePage *)page
|
||||
p->next = junk_pages
|
||||
junk_pages = p
|
||||
unuse ()
|
||||
return raw_pfree (page)
|
||||
|
||||
void Memory::zfree (unsigned page):
|
||||
unuse ()
|
||||
FreePage *p = (FreePage *)page
|
||||
p->next = zero_pages
|
||||
zero_pages = p
|
||||
@ -177,6 +185,8 @@ Receiver *Memory::alloc_receiver ():
|
||||
ret->next_owned = NULL
|
||||
ret->capabilities = NULL
|
||||
ret->messages = NULL
|
||||
ret->reply_protected_data = ~0
|
||||
ret->protected_only = false
|
||||
return ret
|
||||
|
||||
Capability *Memory::alloc_capability (Receiver *target, Capability *parent, Capability **parent_ptr, unsigned protected_data, Capability *ret):
|
||||
@ -290,12 +300,17 @@ void Memory::free_capability (Capability *capability):
|
||||
free_obj (capability)
|
||||
|
||||
void Capability::invalidate ():
|
||||
if !target:
|
||||
return
|
||||
if sibling_prev:
|
||||
sibling_prev->sibling_next = sibling_next
|
||||
else if target:
|
||||
target->capabilities = sibling_next
|
||||
if sibling_next:
|
||||
sibling_next->sibling_prev = sibling_prev
|
||||
parent = NULL
|
||||
sibling_prev = NULL
|
||||
sibling_next = NULL
|
||||
Capability *c = this
|
||||
while c->children:
|
||||
c = c->children
|
||||
@ -332,3 +347,32 @@ void Memory::free_memory (Memory *mem):
|
||||
free_memory (mem->memories)
|
||||
Memory_arch_free (mem)
|
||||
free_obj (mem)
|
||||
|
||||
void Page::forget ():
|
||||
if data.share_prev || data.share_next:
|
||||
if data.share_prev:
|
||||
((Page *)data.share_prev)->data.share_next = data.share_next
|
||||
if data.share_next:
|
||||
((Page *)data.share_next)->data.share_prev = data.share_prev
|
||||
data.share_prev = NULL
|
||||
data.share_next = NULL
|
||||
else:
|
||||
raw_pfree (data.frame)
|
||||
data.frame = 0
|
||||
data.flags &= ~(PAGE_FLAG_FRAME | PAGE_FLAG_SHARED)
|
||||
Page_arch_update_mapping (this)
|
||||
|
||||
void Cappage::forget ():
|
||||
if data.share_prev || data.share_next:
|
||||
if data.share_prev:
|
||||
((Cappage *)data.share_prev)->data.share_next = data.share_next
|
||||
if data.share_next:
|
||||
((Cappage *)data.share_next)->data.share_prev = data.share_prev
|
||||
data.share_prev = NULL
|
||||
data.share_next = NULL
|
||||
else:
|
||||
for unsigned i = 0; i < CAPPAGE_SIZE; ++i:
|
||||
((Capability *)data.frame)[i].invalidate ()
|
||||
raw_pfree (data.frame)
|
||||
data.frame = 0
|
||||
data.flags &= ~(PAGE_FLAG_FRAME | PAGE_FLAG_SHARED)
|
||||
|
@ -24,86 +24,96 @@ extern "C" {
|
||||
#define CAP_RECEIVER_SET_OWNER 1
|
||||
#define CAP_RECEIVER_CREATE_CAPABILITY 2
|
||||
#define CAP_RECEIVER_CREATE_CALL_CAPABILITY 3
|
||||
#define CAP_RECEIVER_GET_REPLY_PROTECTED_DATA 4
|
||||
#define CAP_RECEIVER_SET_REPLY_PROTECTED_DATA 5
|
||||
#define CAP_RECEIVER_ALL_RIGHTS 0x7f
|
||||
/* Not an operation; a capability with this bit set is a call capability. */
|
||||
#define CAP_RECEIVER_CALL 7
|
||||
/* Same thing for reply capability. */
|
||||
#define CAP_RECEIVER_REPLY 8
|
||||
/* If set on a call capability, waiting for only this reply is disabled. */
|
||||
#define CAP_RECEIVER_CALL_ASYNC 1
|
||||
|
||||
#define CAP_MEMORY_CREATE 1
|
||||
#define CAP_MEMORY_DESTROY 2
|
||||
#define CAP_MEMORY_LIST 3
|
||||
#define CAP_MEMORY_MAPPING 4
|
||||
#define CAP_MEMORY_SET_LIMIT 5
|
||||
#define CAP_MEMORY_GET_LIMIT 6
|
||||
#define CAP_MEMORY_DROP 7
|
||||
#define CAP_MEMORY_MAP 4
|
||||
#define CAP_MEMORY_MAPPING 5
|
||||
#define CAP_MEMORY_SET_LIMIT 6
|
||||
#define CAP_MEMORY_GET_LIMIT 7
|
||||
#define CAP_MEMORY_DROP 8
|
||||
#define CAP_MEMORY_ALL_RIGHTS 0x1ff
|
||||
|
||||
#define CAP_THREAD_GET_INFO 1 /* Details of this are arch-specific. */
|
||||
#define CAP_THREAD_SET_INFO 2 /* Details of this are arch-specific. */
|
||||
#define CAP_THREAD_SCHEDULE 3
|
||||
#define CAP_THREAD_INFO 1 /* Details of this are arch-specific. */
|
||||
#define CAP_THREAD_SCHEDULE 2
|
||||
#define CAP_THREAD_MAKE_PRIV 6
|
||||
#define CAP_THREAD_GET_TOP_MEMORY 7
|
||||
#define CAP_THREAD_REGISTER_INTERRUPT 8
|
||||
#define CAP_THREAD_ALL_RIGHTS 0xff
|
||||
#define CAP_THREAD_ALL_PRIV_RIGHTS (CAP_THREAD_ALL_RIGHTS | (1 << CAP_THREAD_REGISTER_INTERRUPT) | (1 << CAP_THREAD_GET_TOP_MEMORY))
|
||||
#define CAP_THREAD_ALL_RIGHTS 0x3f
|
||||
#define CAP_THREAD_ALL_PRIV_RIGHTS (CAP_THREAD_ALL_RIGHTS | (1 << CAP_THREAD_REGISTER_INTERRUPT) | (1 << CAP_THREAD_GET_TOP_MEMORY) | (1 << CAP_THREAD_MAKE_PRIV))
|
||||
|
||||
/* These get/set_info are not arch-specific. */
|
||||
#define CAP_THREAD_INFO_PC ~0
|
||||
#define CAP_THREAD_INFO_SP ~1
|
||||
#define CAP_THREAD_INFO_FLAGS ~2
|
||||
/* Flag values for processor state */
|
||||
#define THREAD_FLAG_WAITING 0x80000000
|
||||
#define THREAD_FLAG_RUNNING 0x40000000
|
||||
#define THREAD_FLAG_PRIV 0x20000000
|
||||
#define THREAD_FLAG_PRIV 0x80000000
|
||||
#define THREAD_FLAG_WAITING 0x40000000
|
||||
#define THREAD_FLAG_RUNNING 0x20000000
|
||||
#define THREAD_FLAG_USER 0x1fffffff
|
||||
|
||||
#define CAP_PAGE_MAP 1
|
||||
#define CAP_PAGE_COPY 2
|
||||
#define CAP_PAGE_MOVE 3
|
||||
#define CAP_PAGE_GET_FLAGS 4
|
||||
#define CAP_PAGE_SET_FLAGS 5
|
||||
#define CAP_PAGE_SHARE 1
|
||||
#define CAP_PAGE_FLAGS 2
|
||||
/* Not an operation; a capability without this bit cannot write to the page. */
|
||||
#define CAP_PAGE_WRITE 6
|
||||
#define CAP_PAGE_WRITE 3
|
||||
#define CAP_PAGE_ALL_RIGHTS 0x1ff
|
||||
|
||||
/* Operation details for PAGE_SHARE */
|
||||
/* Forget the source page during the operation. This makes it a move. */
|
||||
#define PAGE_SHARE_FORGET 0x10000
|
||||
/* Make the target unwritable. */
|
||||
#define PAGE_SHARE_READONLY 0x20000
|
||||
/* Make the target independent of the source (make a copy if needed). */
|
||||
#define PAGE_SHARE_COPY 0x40000
|
||||
|
||||
/* Flag values for Page and Cappage objects. */
|
||||
/* A writable page can be written to. This flag can not be set while the frame is shared. */
|
||||
#define PAGE_FLAG_WRITABLE 1
|
||||
/* When paying, the memory's use is incremented if the page holds a frame. It cannot be lost. Frames are lost when the last payer forgets them. */
|
||||
#define PAGE_FLAG_PAYING 2
|
||||
/* Frames can be moved to this Page if they are not shared; copying will always fail. Sharing a page while this flag is set will fail; moving it will move the flag with it. A page is guaranteed to be unshared when this flag is set. Trying to set this flag on a shared page has no effect. */
|
||||
#define PAGE_FLAG_NOSHARE 4
|
||||
/* Set if this page has a frame associated with it. This flag is automatically reset if the frame is lost because of payment problems. */
|
||||
#define PAGE_FLAG_FRAME 4
|
||||
/* This is a read-only flag, which is set if the Page is shared. */
|
||||
#define PAGE_FLAG_SHARED 8
|
||||
|
||||
#define CAP_CAPABILITY_GET 1
|
||||
#define CAP_CAPABILITY_SET_DEATH_NOTIFY 2
|
||||
#define CAP_CAPABILITY_ALL_RIGHTS 0x1ff
|
||||
|
||||
#define CAPPAGE_SIZE 102
|
||||
/* Cappage has page's operations as well. */
|
||||
#define CAP_CAPPAGE_SET 7
|
||||
/* Cappage has Page's operations as well. */
|
||||
#define CAP_CAPPAGE_SET 4
|
||||
#define CAP_CAPPAGE_ALL_RIGHTS 0x1ff
|
||||
|
||||
#ifndef __KERNEL
|
||||
typedef unsigned __Capability;
|
||||
typedef unsigned Capability;
|
||||
|
||||
extern __Capability __my_receiver;
|
||||
extern __Capability __my_thread;
|
||||
extern __Capability __my_memory;
|
||||
extern __Capability __my_call;
|
||||
extern Capability my_receiver;
|
||||
extern Capability my_thread;
|
||||
extern Capability my_memory;
|
||||
extern Capability my_call;
|
||||
|
||||
__Capability __cap_copy (__Capability src)
|
||||
Capability cap_copy (Capability src)
|
||||
{
|
||||
return src | 2;
|
||||
}
|
||||
|
||||
typedef struct __Message
|
||||
typedef struct Message
|
||||
{
|
||||
unsigned data[4];
|
||||
__Capability cap[4];
|
||||
} __Message;
|
||||
Capability cap[4];
|
||||
} Message;
|
||||
|
||||
static int __invoke (__Capability target, __Message *msg)
|
||||
static int invoke (Capability target, Message *msg)
|
||||
{
|
||||
register int ret __asm__ ("v0");
|
||||
register unsigned v0 __asm__ ("v0") = target;
|
||||
@ -119,7 +129,7 @@ static int __invoke (__Capability target, __Message *msg)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __call (__Capability target, __Message *msg)
|
||||
static int call (Capability target, Message *msg)
|
||||
{
|
||||
register int ret __asm__ ("v0");
|
||||
register unsigned v0 __asm__ ("v0") = target;
|
||||
@ -143,242 +153,302 @@ static int __call (__Capability target, __Message *msg)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __invoke_01 (__Capability t, unsigned d)
|
||||
static int invoke_01 (Capability t, unsigned d)
|
||||
{
|
||||
__Message msg;
|
||||
Message msg;
|
||||
int ret;
|
||||
msg.data[0] = d;
|
||||
return __invoke (t, &msg);
|
||||
return invoke (t, &msg);
|
||||
}
|
||||
|
||||
static int __invoke_02 (__Capability t, unsigned d0, unsigned d1)
|
||||
static int invoke_02 (Capability t, unsigned d0, unsigned d1)
|
||||
{
|
||||
__Message msg;
|
||||
Message msg;
|
||||
int ret;
|
||||
msg.data[0] = d0;
|
||||
msg.data[1] = d1;
|
||||
return __invoke (t, &msg);
|
||||
return invoke (t, &msg);
|
||||
}
|
||||
|
||||
static int __invoke_04 (__Capability t, unsigned d0, unsigned d1, unsigned d2, unsigned d3)
|
||||
static int invoke_04 (Capability t, unsigned d0, unsigned d1, unsigned d2, unsigned d3)
|
||||
{
|
||||
__Message msg;
|
||||
Message msg;
|
||||
int ret;
|
||||
msg.data[0] = d0;
|
||||
msg.data[1] = d1;
|
||||
msg.data[2] = d2;
|
||||
msg.data[3] = d3;
|
||||
return __invoke (t, &msg);
|
||||
return invoke (t, &msg);
|
||||
}
|
||||
|
||||
static int __invoke_11 (__Capability t, __Capability c, unsigned d)
|
||||
static int invoke_11 (Capability t, Capability c, unsigned d)
|
||||
{
|
||||
__Message msg;
|
||||
Message msg;
|
||||
int ret;
|
||||
msg.cap[0] = c;
|
||||
msg.data[0] = d;
|
||||
return __invoke (t, &msg);
|
||||
return invoke (t, &msg);
|
||||
}
|
||||
|
||||
static int __invoke_12 (__Capability t, __Capability c, unsigned d0, unsigned d1)
|
||||
static int invoke_12 (Capability t, Capability c, unsigned d0, unsigned d1)
|
||||
{
|
||||
__Message msg;
|
||||
Message msg;
|
||||
int ret;
|
||||
msg.cap[0] = c;
|
||||
msg.data[0] = d0;
|
||||
msg.data[1] = d1;
|
||||
return __invoke (t, &msg);
|
||||
return invoke (t, &msg);
|
||||
}
|
||||
|
||||
static __Capability __call_c01 (__Capability c, unsigned d)
|
||||
static Capability call_c01 (Capability c, unsigned d)
|
||||
{
|
||||
__Message msg;
|
||||
Message msg;
|
||||
int ret;
|
||||
msg.cap[0] = c;
|
||||
msg.data[0] = d;
|
||||
ret = __call (__my_call, &msg);
|
||||
ret = call (my_call, &msg);
|
||||
return ret ? msg.cap[0] : 0;
|
||||
}
|
||||
|
||||
static __Capability __call_c02 (__Capability c, unsigned d0, unsigned d1)
|
||||
static Capability call_c02 (Capability c, unsigned d0, unsigned d1)
|
||||
{
|
||||
__Message msg;
|
||||
Message msg;
|
||||
int ret;
|
||||
msg.cap[0] = c;
|
||||
msg.data[0] = d0;
|
||||
msg.data[1] = d1;
|
||||
ret = __call (__my_call, &msg);
|
||||
ret = call (my_call, &msg);
|
||||
return ret ? msg.cap[0] : 0;
|
||||
}
|
||||
|
||||
static unsigned __call_n02 (__Capability c, unsigned d0, unsigned d1)
|
||||
static Capability call_c12 (Capability c, Capability c1, unsigned d0, unsigned d1)
|
||||
{
|
||||
__Message msg;
|
||||
Message msg;
|
||||
int ret;
|
||||
msg.cap[0] = c;
|
||||
msg.cap[1] = c1;
|
||||
msg.data[0] = d0;
|
||||
msg.data[1] = d1;
|
||||
ret = __call (__my_call, &msg);
|
||||
ret = call (my_call, &msg);
|
||||
return ret ? msg.cap[0] : 0;
|
||||
}
|
||||
|
||||
static unsigned call_n01 (Capability c, unsigned d)
|
||||
{
|
||||
Message msg;
|
||||
int ret;
|
||||
msg.cap[0] = c;
|
||||
msg.data[0] = d;
|
||||
ret = call (my_call, &msg);
|
||||
return ret ? msg.data[0] : 0;
|
||||
}
|
||||
|
||||
static __Capability __degrade (__Capability src, unsigned mask)
|
||||
static unsigned call_n02 (Capability c, unsigned d0, unsigned d1)
|
||||
{
|
||||
return __call_c02 (src, CAP_DEGRADE, mask);
|
||||
Message msg;
|
||||
int ret;
|
||||
msg.cap[0] = c;
|
||||
msg.data[0] = d0;
|
||||
msg.data[1] = d1;
|
||||
ret = call (my_call, &msg);
|
||||
return ret ? msg.data[0] : 0;
|
||||
}
|
||||
|
||||
static void __schedule ()
|
||||
static unsigned call_n03 (Capability c, unsigned d0, unsigned d1, unsigned d2)
|
||||
{
|
||||
__invoke_01 (__my_thread, CAP_THREAD_SCHEDULE);
|
||||
Message msg;
|
||||
int ret;
|
||||
msg.cap[0] = c;
|
||||
msg.data[0] = d0;
|
||||
msg.data[1] = d1;
|
||||
msg.data[2] = d2;
|
||||
ret = call (my_call, &msg);
|
||||
return ret ? msg.data[0] : 0;
|
||||
}
|
||||
|
||||
static void __register_interrupt (unsigned num)
|
||||
static unsigned call_n04 (Capability c, unsigned d0, unsigned d1, unsigned d2, unsigned d3)
|
||||
{
|
||||
__invoke_12 (__my_thread, __my_receiver, CAP_THREAD_REGISTER_INTERRUPT, num);
|
||||
Message msg;
|
||||
int ret;
|
||||
msg.cap[0] = c;
|
||||
msg.data[0] = d0;
|
||||
msg.data[1] = d1;
|
||||
msg.data[2] = d2;
|
||||
msg.data[3] = d3;
|
||||
ret = call (my_call, &msg);
|
||||
return ret ? msg.data[0] : 0;
|
||||
}
|
||||
|
||||
static __Capability __get_top_memory ()
|
||||
static Capability degrade (Capability src, unsigned mask)
|
||||
{
|
||||
return __call_c01 (__my_thread, CAP_THREAD_GET_TOP_MEMORY);
|
||||
return call_c02 (src, CAP_DEGRADE, mask);
|
||||
}
|
||||
|
||||
static void __unregister_interrupt (unsigned num)
|
||||
static void schedule ()
|
||||
{
|
||||
__invoke_02 (__my_thread, CAP_THREAD_REGISTER_INTERRUPT, num);
|
||||
invoke_01 (my_thread, CAP_THREAD_SCHEDULE);
|
||||
}
|
||||
|
||||
static int __receiver_set_owner (__Capability receiver, __Capability owner)
|
||||
static void register_interrupt (unsigned num)
|
||||
{
|
||||
return __invoke_11 (receiver, owner, CAP_RECEIVER_SET_OWNER);
|
||||
invoke_12 (my_thread, my_receiver, CAP_THREAD_REGISTER_INTERRUPT, num);
|
||||
}
|
||||
|
||||
static __Capability __receiver_create_capability (__Capability receiver, unsigned protected_data)
|
||||
static Capability get_top_memory ()
|
||||
{
|
||||
return __call_c02 (receiver, CAP_RECEIVER_CREATE_CAPABILITY, protected_data);
|
||||
return call_c01 (my_thread, CAP_THREAD_GET_TOP_MEMORY);
|
||||
}
|
||||
|
||||
static __Capability __receiver_create_call_capability (__Capability receiver, unsigned protected_data)
|
||||
static void unregister_interrupt (unsigned num)
|
||||
{
|
||||
return __call_c02 (receiver, CAP_RECEIVER_CREATE_CALL_CAPABILITY, protected_data);
|
||||
invoke_02 (my_thread, CAP_THREAD_REGISTER_INTERRUPT, num);
|
||||
}
|
||||
|
||||
static __Capability __memory_create (__Capability memory, unsigned type)
|
||||
static int receiver_set_owner (Capability receiver, Capability owner)
|
||||
{
|
||||
return __call_c02 (memory, CAP_MEMORY_CREATE, type);
|
||||
return invoke_11 (receiver, owner, CAP_RECEIVER_SET_OWNER);
|
||||
}
|
||||
|
||||
static __Capability __memory_create_page (__Capability memory)
|
||||
static Capability receiver_create_capability (Capability receiver, unsigned protected_data)
|
||||
{
|
||||
return __memory_create (memory, CAPTYPE_PAGE);
|
||||
return call_c02 (receiver, CAP_RECEIVER_CREATE_CAPABILITY, protected_data);
|
||||
}
|
||||
|
||||
static __Capability __memory_create_thread (__Capability memory)
|
||||
static int receiver_get_reply_protected_data (Capability receiver, unsigned data)
|
||||
{
|
||||
return __memory_create (memory, CAPTYPE_THREAD);
|
||||
return call_n01 (receiver, CAP_RECEIVER_GET_REPLY_PROTECTED_DATA);
|
||||
}
|
||||
|
||||
static __Capability __memory_create_receiver (__Capability memory)
|
||||
static int receiver_set_reply_protected_data (Capability receiver, unsigned data)
|
||||
{
|
||||
return __memory_create (memory, CAPTYPE_RECEIVER);
|
||||
return invoke_02 (receiver, CAP_RECEIVER_SET_REPLY_PROTECTED_DATA, data);
|
||||
}
|
||||
|
||||
static __Capability __memory_create_memory (__Capability memory)
|
||||
static Capability receiver_create_call_capability (Capability receiver)
|
||||
{
|
||||
return __memory_create (memory, CAPTYPE_MEMORY);
|
||||
return call_c02 (receiver, CAP_RECEIVER_CREATE_CALL_CAPABILITY, 0);
|
||||
}
|
||||
|
||||
static __Capability __memory_create_cappage (__Capability memory)
|
||||
static Capability receiver_create_async_call_capability (Capability receiver)
|
||||
{
|
||||
return __memory_create (memory, CAPTYPE_CAPPAGE);
|
||||
return call_c02 (receiver, CAP_RECEIVER_CREATE_CALL_CAPABILITY, 1);
|
||||
}
|
||||
|
||||
static int __memory_destroy (__Capability memory, __Capability target)
|
||||
static Capability memory_create (Capability memory, unsigned type)
|
||||
{
|
||||
return __invoke_11 (memory, target, CAP_MEMORY_DESTROY);
|
||||
return call_c02 (memory, CAP_MEMORY_CREATE, type);
|
||||
}
|
||||
|
||||
static Capability memory_create_page (Capability memory)
|
||||
{
|
||||
return memory_create (memory, CAPTYPE_PAGE);
|
||||
}
|
||||
|
||||
static Capability memory_create_thread (Capability memory)
|
||||
{
|
||||
return memory_create (memory, CAPTYPE_THREAD);
|
||||
}
|
||||
|
||||
static Capability memory_create_receiver (Capability memory)
|
||||
{
|
||||
return memory_create (memory, CAPTYPE_RECEIVER);
|
||||
}
|
||||
|
||||
static Capability memory_create_memory (Capability memory)
|
||||
{
|
||||
return memory_create (memory, CAPTYPE_MEMORY);
|
||||
}
|
||||
|
||||
static Capability memory_create_cappage (Capability memory)
|
||||
{
|
||||
return memory_create (memory, CAPTYPE_CAPPAGE);
|
||||
}
|
||||
|
||||
static int memory_destroy (Capability memory, Capability target)
|
||||
{
|
||||
return invoke_11 (memory, target, CAP_MEMORY_DESTROY);
|
||||
}
|
||||
|
||||
/* TODO: #define CAP_MEMORY_LIST 3 */
|
||||
|
||||
static __Capability __memory_mapping (__Capability memory, unsigned address)
|
||||
static int memory_map (Capability memory, Capability page, unsigned address)
|
||||
{
|
||||
return __call_c02 (memory, CAP_MEMORY_MAPPING, address);
|
||||
return invoke_12 (memory, page, CAP_MEMORY_MAP, address);
|
||||
}
|
||||
|
||||
static void __drop (__Capability cap)
|
||||
static Capability memory_mapping (Capability memory, unsigned address)
|
||||
{
|
||||
__invoke_11 (__my_memory, cap, CAP_MEMORY_DROP);
|
||||
return call_c02 (memory, CAP_MEMORY_MAPPING, address);
|
||||
}
|
||||
|
||||
static int __thread_set_info (__Capability thread, unsigned info, unsigned value, unsigned mask)
|
||||
static void drop (Capability cap)
|
||||
{
|
||||
return __invoke_04 (thread, CAP_THREAD_SET_INFO, info, value, mask);
|
||||
invoke_11 (my_memory, cap, CAP_MEMORY_DROP);
|
||||
}
|
||||
|
||||
static int __thread_set_pc (__Capability thread, unsigned pc)
|
||||
static Capability thread_make_priv (Capability thread)
|
||||
{
|
||||
return __thread_set_info (thread, CAP_THREAD_INFO_PC, pc, ~0);
|
||||
return call_c12 (my_thread, thread, CAP_THREAD_MAKE_PRIV, ~0);
|
||||
}
|
||||
|
||||
static int __thread_set_sp (__Capability thread, unsigned sp)
|
||||
static unsigned thread_info (Capability thread, unsigned info, unsigned value, unsigned mask)
|
||||
{
|
||||
return __thread_set_info (thread, CAP_THREAD_INFO_SP, sp, ~0);
|
||||
return call_n04 (thread, CAP_THREAD_INFO, info, value, mask);
|
||||
}
|
||||
|
||||
static int __thread_set_flags (__Capability thread, unsigned value, unsigned mask)
|
||||
static unsigned thread_set_pc (Capability thread, unsigned pc)
|
||||
{
|
||||
return __thread_set_info (thread, CAP_THREAD_INFO_FLAGS, value, mask);
|
||||
return thread_info (thread, CAP_THREAD_INFO_PC, pc, ~0);
|
||||
}
|
||||
|
||||
static int __thread_run (__Capability thread, int run)
|
||||
static unsigned thread_set_sp (Capability thread, unsigned sp)
|
||||
{
|
||||
return __thread_set_flags (thread, run ? THREAD_FLAG_RUNNING : 0, THREAD_FLAG_RUNNING);
|
||||
return thread_info (thread, CAP_THREAD_INFO_SP, sp, ~0);
|
||||
}
|
||||
|
||||
static int __thread_wait (__Capability thread, int wait)
|
||||
static unsigned thread_flags (Capability thread, unsigned value, unsigned mask)
|
||||
{
|
||||
return __thread_set_flags (thread, wait ? THREAD_FLAG_WAITING : 0, THREAD_FLAG_WAITING);
|
||||
return thread_info (thread, CAP_THREAD_INFO_FLAGS, value, mask);
|
||||
}
|
||||
|
||||
static unsigned __thread_get_info (__Capability thread, unsigned info)
|
||||
static unsigned thread_run (Capability thread, int run)
|
||||
{
|
||||
return __call_n02 (thread, CAP_THREAD_GET_INFO, info);
|
||||
return thread_flags (thread, run ? THREAD_FLAG_RUNNING : 0, THREAD_FLAG_RUNNING);
|
||||
}
|
||||
|
||||
static unsigned __thread_get_pc (__Capability thread)
|
||||
static unsigned thread_wait (Capability thread, int wait)
|
||||
{
|
||||
return __thread_get_info (thread, CAP_THREAD_INFO_PC);
|
||||
return thread_flags (thread, wait ? THREAD_FLAG_WAITING : 0, THREAD_FLAG_WAITING);
|
||||
}
|
||||
|
||||
static unsigned __thread_get_sp (__Capability thread)
|
||||
static unsigned thread_get_pc (Capability thread)
|
||||
{
|
||||
return __thread_get_info (thread, CAP_THREAD_INFO_SP);
|
||||
return thread_info (thread, CAP_THREAD_INFO_PC, 0, 0);
|
||||
}
|
||||
|
||||
static unsigned __thread_get_flags (__Capability thread)
|
||||
static unsigned thread_get_sp (Capability thread)
|
||||
{
|
||||
return __thread_get_info (thread, CAP_THREAD_INFO_FLAGS);
|
||||
return thread_info (thread, CAP_THREAD_INFO_SP, 0, 0);
|
||||
}
|
||||
|
||||
/* TODO. All except map should also work for cappages.
|
||||
#define CAP_PAGE_MAP 1
|
||||
#define CAP_PAGE_SHARE 2
|
||||
#define CAP_PAGE_SHARE_COW 3
|
||||
#define CAP_PAGE_FORGET 4
|
||||
*/
|
||||
|
||||
static __Capability __capability_get (__Capability cap)
|
||||
static int page_share (Capability page, Capability target, unsigned flags)
|
||||
{
|
||||
return __call_c01 (cap, CAP_CAPABILITY_GET);
|
||||
return invoke_12 (page, target, CAP_PAGE_SHARE, flags);
|
||||
}
|
||||
|
||||
static int __capability_set_death_notify (__Capability source, __Capability target)
|
||||
static unsigned page_flags (Capability page, unsigned new_flags, unsigned mask)
|
||||
{
|
||||
return __invoke_11 (source, target, CAP_CAPABILITY_SET_DEATH_NOTIFY);
|
||||
return call_n03 (page, CAP_PAGE_FLAGS, new_flags, mask);
|
||||
}
|
||||
|
||||
static int __capability_cappage_set (__Capability page, __Capability cap, unsigned index)
|
||||
static Capability capability_get (Capability cap)
|
||||
{
|
||||
return __invoke_12 (page, cap, CAP_CAPPAGE_SET, index);
|
||||
return call_c01 (cap, CAP_CAPABILITY_GET);
|
||||
}
|
||||
|
||||
static int cappage_set (Capability page, Capability cap, unsigned index)
|
||||
{
|
||||
return invoke_12 (page, cap, CAP_CAPPAGE_SET, index);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
18
entry.S
18
entry.S
@ -13,17 +13,15 @@
|
||||
addr_000:
|
||||
#if 0
|
||||
// TLB refill
|
||||
bne $zero, $k0, slow_refill
|
||||
nop
|
||||
bne $zero, $k1, slow_refill
|
||||
nop
|
||||
bne $zero, $k0, slow_refill
|
||||
lw $k1, -0xd94($zero)
|
||||
beq $zero, $k1, slow_refill
|
||||
mfc0 $k0, $CP0_ENTRY_HI
|
||||
srl $k0, $k0, 19
|
||||
and $k0, $k0, 0x3fc
|
||||
addu $k0, $k0, $k1
|
||||
beq $zero, $k0, slow_refill0
|
||||
beq $zero, $k0, zero_refill
|
||||
lw $k0, 0($k0)
|
||||
mfc0 $k1, $CP0_ENTRY_HI
|
||||
srl $k1, $k1, 10
|
||||
@ -33,14 +31,18 @@ addr_000:
|
||||
mtc0 $k1, $CP0_ENTRY_LO0
|
||||
lw $k1, 4($k0)
|
||||
mtc0 $k1, $CP0_ENTRY_LO1
|
||||
tlbwr
|
||||
1: tlbwr
|
||||
move $zero, $k0
|
||||
move $zero, $k1
|
||||
eret
|
||||
|
||||
slow_refill0:
|
||||
move $k1, $zero
|
||||
zero_refill:
|
||||
mtc0 $zero, $CP_ENTRY_LO0
|
||||
b 1b
|
||||
mtc0 $zero, $CP_ENTRY_LO1
|
||||
|
||||
slow_refill:
|
||||
move $k1, $zero
|
||||
#endif
|
||||
sw $ra, -0xd88($zero)
|
||||
bal save_regs
|
||||
@ -61,7 +63,7 @@ addr_100:
|
||||
addr_180:
|
||||
// General exception
|
||||
// Allow new exceptions to update EPC and friends.
|
||||
mtc0 $zero, $CP0_STATUS
|
||||
//mtc0 $zero, $CP0_STATUS
|
||||
sw $ra, -0xd88($zero)
|
||||
bal save_regs
|
||||
nop
|
||||
|
8
init.ccp
8
init.ccp
@ -32,6 +32,7 @@ static void init_idle ():
|
||||
idle_page.prev = NULL
|
||||
idle_page.next = NULL
|
||||
idle_page.data.frame = 0x80000000
|
||||
idle_page.data.flags = PAGE_FLAG_WRITABLE | PAGE_FLAG_PAYING | PAGE_FLAG_FRAME
|
||||
idle_page.refs = NULL
|
||||
idle_page.address_space = NULL
|
||||
current = &idle
|
||||
@ -124,6 +125,7 @@ static void init_threads ():
|
||||
if !pages[idx]:
|
||||
pages[idx] = mem->alloc_page ()
|
||||
pages[idx]->data.frame = thread_start[i] + (idx << PAGE_BITS)
|
||||
pages[idx]->data.flags = (writable ? PAGE_FLAG_WRITABLE : 0) | PAGE_FLAG_PAYING | PAGE_FLAG_FRAME
|
||||
++top_memory.limit
|
||||
if !mem->map (pages[idx], p, writable):
|
||||
panic (0x22446688, "unable to map initial page")
|
||||
@ -136,6 +138,7 @@ static void init_threads ():
|
||||
if !page:
|
||||
panic (0x00220022, "out of memory")
|
||||
page->data.frame = mem->zalloc ()
|
||||
page->data.flags = (writable ? PAGE_FLAG_WRITABLE : 0) | PAGE_FLAG_PAYING | PAGE_FLAG_FRAME
|
||||
if !page->data.frame || !mem->map (page, p, true):
|
||||
panic (0x33557799, "unable to map initial bss page")
|
||||
else:
|
||||
@ -154,6 +157,7 @@ static void init_threads ():
|
||||
top_memory.pfree (thread_start[i] + (p << PAGE_BITS))
|
||||
Page *stackpage = mem->alloc_page ()
|
||||
stackpage->data.frame = mem->zalloc ()
|
||||
stackpage->data.flags = PAGE_FLAG_WRITABLE | PAGE_FLAG_PAYING | PAGE_FLAG_FRAME
|
||||
if !stackpage || !mem->map (stackpage, 0x7ffff000, true):
|
||||
panic (0x13151719, "unable to map initial stack page")
|
||||
Receiver *recv = mem->alloc_receiver ()
|
||||
@ -211,9 +215,9 @@ void init ():
|
||||
|
||||
init_threads ()
|
||||
|
||||
// Enable all interrupts and say we're handling an exception.
|
||||
// Say we're handling an exception. Don't enable interrupts; this will happen when handlers are registered.
|
||||
// Since we're going to enter the idle task, allow access to cp0.
|
||||
cp0_set (CP0_STATUS, 0x1000ff13)
|
||||
cp0_set (CP0_STATUS, 0x10000013)
|
||||
|
||||
// Done; return to user space (the idle task).
|
||||
__asm__ volatile ("eret")
|
||||
|
1002
invoke.ccp
1002
invoke.ccp
File diff suppressed because it is too large
Load Diff
@ -63,8 +63,11 @@ struct Receiver : public Object <Receiver>:
|
||||
Receiver *prev_owned, *next_owned
|
||||
Capability *capabilities
|
||||
Message *messages
|
||||
unsigned reply_protected_data
|
||||
bool protected_only
|
||||
void own (Thread *o)
|
||||
void orphan ()
|
||||
bool try_deliver ()
|
||||
bool send_message (unsigned protected_data, unsigned data[4], Capability *cap[4], bool copy[4])
|
||||
|
||||
struct Capability : public Object <Capability>:
|
||||
@ -85,9 +88,11 @@ struct ShareData :
|
||||
struct Page : public Object <Page>:
|
||||
ShareData data
|
||||
Page_arch arch
|
||||
void forget ()
|
||||
|
||||
struct Cappage : public Object <Cappage>:
|
||||
ShareData data
|
||||
void forget ()
|
||||
|
||||
struct Memory : public Object <Memory>:
|
||||
Free *frees
|
||||
@ -158,6 +163,10 @@ EXTERN Page idle_page
|
||||
EXTERN Thread *first_scheduled
|
||||
EXTERN Thread *current
|
||||
|
||||
// Defined in alloc.cc
|
||||
unsigned raw_zalloc ()
|
||||
void raw_pfree (unsigned page)
|
||||
|
||||
// Defined in arch.cc
|
||||
void Thread_arch_init (Thread *thread)
|
||||
void Thread_arch_receive (Thread *thread, unsigned d[4], Capability *c[4])
|
||||
|
@ -1,2 +0,0 @@
|
||||
#pypp 0
|
||||
#include "kernel.hh"
|
58
mips.ccp
58
mips.ccp
@ -170,6 +170,18 @@ static void free_page_table (arch_page_table *t, unsigned idx):
|
||||
mem->arch.directory = NULL
|
||||
mem->arch.shadow = NULL
|
||||
|
||||
static void tlb_reset (unsigned address, unsigned asid, unsigned value):
|
||||
cp0_set (CP0_ENTRY_HI, address | asid)
|
||||
__asm__ volatile ("tlbp")
|
||||
unsigned idx
|
||||
cp0_get (CP0_INDEX, idx)
|
||||
if ~idx & 0x80000000:
|
||||
if address & (1 << PAGE_BITS):
|
||||
cp0_set (CP0_ENTRY_LO1, value)
|
||||
else:
|
||||
cp0_set (CP0_ENTRY_LO0, value)
|
||||
__asm__ volatile ("tlbwi")
|
||||
|
||||
static void free_page (arch_page_table *t, arch_page *p):
|
||||
if p->next:
|
||||
p->next->prev = p->prev
|
||||
@ -183,12 +195,26 @@ static void free_page (arch_page_table *t, arch_page *p):
|
||||
p->page->arch.first_mapped = p->next_mapped
|
||||
if p->next_mapped:
|
||||
p->next_mapped->prev_mapped = p->prev_mapped
|
||||
tlb_reset (p->mapping, p->address_space->arch.asid, 0)
|
||||
unsigned idx = p->mapping >> 21
|
||||
p->address_space->free_obj (p)
|
||||
if !t->first_page:
|
||||
free_page_table (t, idx)
|
||||
|
||||
static unsigned make_entry_lo (Page *page, bool write):
|
||||
if !page->data.frame:
|
||||
return 0
|
||||
unsigned flags
|
||||
if write:
|
||||
flags = 0x18 | 0x4 | 0x2
|
||||
else
|
||||
flags = 0x18 | 0x2
|
||||
return ((page->data.frame & ~0x80000000) >> 6) | flags
|
||||
|
||||
bool Memory_arch_map (Memory *mem, Page *page, unsigned address, bool write):
|
||||
if address >= 0x80000000:
|
||||
return false
|
||||
address &= PAGE_MASK
|
||||
if !mem->arch.directory:
|
||||
mem->arch.directory = (unsigned **)mem->zalloc ()
|
||||
if !mem->arch.directory:
|
||||
@ -229,9 +255,9 @@ bool Memory_arch_map (Memory *mem, Page *page, unsigned address, bool write):
|
||||
unsigned idx = (address >> 12) & ((1 << 9) - 1)
|
||||
if table[idx]:
|
||||
mem->unmap ((Page *)table[idx + 0x200], address)
|
||||
table[idx] = page->data.frame ? ((page->data.frame & ~0x80000000) >> 6) | 0x18 | (write ? 0x4 : 0) | 0x2 : 0
|
||||
table[idx] = make_entry_lo (page, write)
|
||||
table[idx + 0x200] = (unsigned)p
|
||||
p->mapping = address
|
||||
p->mapping = address + write
|
||||
p->page = page
|
||||
p->next_mapped = page->arch.first_mapped
|
||||
if p->next_mapped:
|
||||
@ -250,15 +276,31 @@ void Memory_arch_unmap (Memory *mem, Page *page, unsigned address):
|
||||
free_page (t, p)
|
||||
|
||||
Page *Memory_arch_get_mapping (Memory *mem, unsigned address, bool *writable):
|
||||
if address >= 0x80000000:
|
||||
return NULL
|
||||
unsigned *table = mem->arch.directory[address >> 21]
|
||||
unsigned idx = (address >> 12) & ((1 << 9) - 1)
|
||||
arch_page *page = (arch_page *)table[idx + 0x200]
|
||||
if writable:
|
||||
*writable = (table[idx] & 4 ? 1 : 0)
|
||||
*writable = table[idx] & 4
|
||||
return page->page
|
||||
|
||||
void Page_arch_update_mapping (Page *page):
|
||||
// TODO
|
||||
if !page->arch.first_mapped:
|
||||
return
|
||||
Memory *as = page->address_space
|
||||
unsigned target = make_entry_lo (page, page->data.flags & PAGE_FLAG_WRITABLE)
|
||||
for arch_page *p = page->arch.first_mapped; p; p = p->next_mapped:
|
||||
unsigned de = p->mapping >> 21
|
||||
unsigned te = (p->mapping >> 12) & ((1 << 9) - 1)
|
||||
bool write = p->mapping & 1
|
||||
unsigned t
|
||||
if p->mapping & 1:
|
||||
t = target
|
||||
else:
|
||||
t = target & ~0x4
|
||||
as->arch.directory[de][te] = t
|
||||
tlb_reset (p->mapping & ~1, as->arch.asid, t)
|
||||
|
||||
void arch_invoke ():
|
||||
Capability *target, *c[4]
|
||||
@ -310,7 +352,11 @@ void arch_invoke ():
|
||||
|
||||
void arch_register_interrupt (unsigned num, Receiver *r):
|
||||
arch_interrupt_receiver[num] = r
|
||||
// And enable the interrupt.
|
||||
unsigned status
|
||||
cp0_get (CP0_STATUS, status)
|
||||
cp0_set (CP0_STATUS, status | (1 << (num + 8)))
|
||||
// And enable or disable the interrupt.
|
||||
if r:
|
||||
status |= 1 << (num + 8)
|
||||
else:
|
||||
status &= ~(1 << (num + 8))
|
||||
cp0_set (CP0_STATUS, status)
|
||||
|
@ -9,7 +9,8 @@ This document briefly describes the inner workings of my kernel, including the
|
||||
reasons for the choices that were made. It is meant to be understandable (with
|
||||
effort) for people who know nothing of operating systems. On the other hand,
|
||||
it should also be readable for people who know about computer architecture, but
|
||||
want to know about this kernel.
|
||||
want to know about this kernel. It is probably better suited for the latter
|
||||
category.
|
||||
\end{abstract}
|
||||
|
||||
\tableofcontents
|
||||
@ -126,16 +127,21 @@ any part of the system, except the parts that it really needs to perform its
|
||||
operation, it cannot leak or damage the other parts of the system either. The
|
||||
reason that this is relevant is not that users will run programs that try to
|
||||
ruin their system (although this may happen as well), but that programs may
|
||||
break and damage random parts of the system, or be taken over by crackers. If
|
||||
the broken or malicious process has fewer rights, it will also do less damage
|
||||
to the system.
|
||||
break and damage random parts of the system, or be taken over by
|
||||
crackers\footnote{Crackers are better known by the public as ``hackers''.
|
||||
However, I use this word to describe people who like to play with software (or
|
||||
sometimes also with other things). Therefore the malicious people who use
|
||||
hacking skills for evil need a different name.}. If the broken or malicious
|
||||
process has fewer rights, it will also do less damage to the system.
|
||||
|
||||
This leads to the goal of giving each process as little rights as possible.
|
||||
For this, it is best to have rights in a very fine-grained way. Every
|
||||
operation of a driver (be it a hardware device driver, or just a shared program
|
||||
such as a file system) should have its own key, which can be given out without
|
||||
giving keys to the entire driver (or even multiple drivers). Such a key is
|
||||
called a capability.
|
||||
called a capability. For example, a capability can allow the holder to access
|
||||
a single file, or to use one specific network connection, or to see what keys
|
||||
are typed by the user.
|
||||
|
||||
Some operations are performed directly on the kernel itself. For those, the
|
||||
kernel can provide its own capabilities. Processes can create their own
|
||||
@ -143,9 +149,9 @@ objects which can receive capability calls, and capabilities for those can be
|
||||
generated by them. Processes can copy capabilities to other processes, if they
|
||||
have a channel to send them (using an existing capability). This way, any
|
||||
operation of the process with the external world goes through a capability, and
|
||||
only one system call is needed, namely \textit{invoke}.
|
||||
only one system call is needed: \textit{invoke}.
|
||||
|
||||
This has a very nice side-effect, namely that it becomes very easy to tap
|
||||
This has a very nice side-effect, which is that it becomes very easy to tap
|
||||
communication of a task you control. This means that a user can redirect
|
||||
certain requests from programs which don't do exactly what is desired to do
|
||||
nicer things. For example, a program can be prevented from opening pop-up
|
||||
@ -155,64 +161,143 @@ This is a very good thing.
|
||||
|
||||
\section{Kernel objects}
|
||||
This section describes all the kernel objects, and the operations that can be
|
||||
performed on them.
|
||||
performed on them. One operation is possible on any kernel object (except a
|
||||
message and reply and call Capabilities). This operation is \textit{degrade}.
|
||||
It creates a copy of the capability with some rights removed. This can be
|
||||
useful when giving away a capability.
|
||||
|
||||
\subsection{Memory}
|
||||
A memory object is a container for storing things. All objects live inside a
|
||||
memory object. A memory object can contain other memory objects, capabilities,
|
||||
receivers, threads and pages.
|
||||
A Memory object is a container for storing things. All objects live inside a
|
||||
Memory object. A Memory object can contain other Memory objects, Capabilities,
|
||||
Receivers, Threads, Pages and Cappages.
|
||||
|
||||
A memory object is also an address space. Pages can be mapped (and unmapped).
|
||||
Any Thread in a memory object uses this address space while it is running.
|
||||
A Memory object is also an address space. Pages can be mapped (and unmapped).
|
||||
Any Thread in a Memory object uses this address space while it is running.
|
||||
|
||||
Every memory object has a limit. When this limit is reached, no more pages can
|
||||
be allocated for it (including pages which it uses to store other objects).
|
||||
Using a new page in a memory object implies using it in all ancestor memory
|
||||
Every Memory object has a limit. When this limit is reached, no more Pages can
|
||||
be allocated for it (including Pages which it uses to store other objects).
|
||||
Using a new Page in a Memory object implies using it in all ancestor Memory
|
||||
objects. This means that setting a limit which is higher than the parent's
|
||||
limit means that the parent's limit applies anyway.
|
||||
|
||||
Operations on memory objects:
|
||||
Operations on Memory objects:
|
||||
\begin{itemize}
|
||||
\item
|
||||
\item Create a new item of type Receiver, Memory, Thread, Page, or Cappage.
|
||||
\item Destroy an item of any type, which is owned by the Memory.
|
||||
\item List items owned by the Memory, Pages mapped in it, and messages in owned
|
||||
Receiver's queues.
|
||||
\item Map a Page at an address.
|
||||
\item Get the Page which is mapped at a certain address.
|
||||
\item Get and set the limit, which is checked when allocating pages for this
|
||||
Memory or any sub-structure.
|
||||
\item Drop a capability. This can only be done by Threads owned by the Memory,
|
||||
because only they can present capabilities owned by it.\footnote{The kernel
|
||||
checks if presented capabilities are owned by the Thread's Memory. If they
|
||||
aren't, no capability is passed instead. The destroy operation destroys an
|
||||
object that a capability points to. Drop destroys the capability itself. If a
|
||||
Thread from an other Memory would try to drop a capability, the kernel would
|
||||
refuse to send it in the message, or it would not be dropped because it would
|
||||
be owned by a different Memory.}
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Page}
|
||||
A page can be used to store user data. It can be mapped into an address space (a memory object). Threads can then use the data directly.
|
||||
|
||||
A page has no operations of itself; mapping a page is achieved using an
|
||||
operation on a memory object.
|
||||
|
||||
\subsection{Receiver}
|
||||
A receiver object is used for inter-process communication. Capabilities can be
|
||||
created from it. When those are invoked, the receiver can be used to retrieve
|
||||
the message.
|
||||
|
||||
Operations on receiver objects:
|
||||
Operations on Receiver objects:
|
||||
\begin{itemize}
|
||||
\item
|
||||
\item Set the owner. This is the Thread that messages will be sent to when
|
||||
they arrive. Messages are stored in the receiver until the owner is ready to
|
||||
accept them. If it is waiting while the message arrives, it is immediately
|
||||
delivered.
|
||||
\item Create a capability. The new capability should be given to Threads who
|
||||
need to send a message to the receiver.
|
||||
\item Create a call capability. This is an optimization. Because
|
||||
\textit{calls} happen a lot, where a capability is created, sent in a message,
|
||||
then a reply is sent over this new capability, and then it is dropped. This
|
||||
can be done using a call capability. The call capability is invoked instead of
|
||||
the target, and the target is specified where the reply capability should be.
|
||||
The message is sent to the call capability (which is handled by the Receiver in
|
||||
the kernel). It creates a new reply capability and sends the message to the
|
||||
target with it. When the reply capability is invoked, the message is sent to
|
||||
the owner, and the capability is dropped. This approach reduces the number of
|
||||
kernel calls from four (create, call, reply, drop) to two (call, reply).
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Thread}
|
||||
Thread objects hold the information about the current state of a thread. This
|
||||
state is used to continue running the thread. The address space is used to map
|
||||
the memory for the Thread. Different Threads in the same address space have
|
||||
the same memory mapping. All Threads in one address space (often there is only
|
||||
one) together are called a process.
|
||||
|
||||
Because all threads have a capability to their own Thread object (for claiming
|
||||
Receivers), this is also used to make some calls which don't actually need an
|
||||
object. The reason that these are not operations on some fake object which
|
||||
every process implicitly owns, is that for debugging it may be useful to see
|
||||
every action of a process. In that case, all its capabilities must be pointing
|
||||
to the watcher, which will send them through to the actual target (or not).
|
||||
With such an implicit capability, it would be impossible to intercept these
|
||||
calls.
|
||||
|
||||
Operations on Thread objects:
|
||||
\begin{itemize}
|
||||
\item Get information about the thread. Details of this are
|
||||
architecture-specific. Standard ways are defined for getting and setting some
|
||||
flags (whether the process is running or waiting for a message, setting these
|
||||
flags is a way to control this for other Threads), the program counter and the
|
||||
stack pointer. This call is also used to get the contents of processor
|
||||
registers and possibly other information which is different per Thread.
|
||||
\item Let the kernel schedule the next process. This is not thread-specific.
|
||||
\item Get the top Memory object. This is not thread-specific. Most Threads
|
||||
are not allowed to perform this operation. It is given to the initial Threads.
|
||||
They can pass it on to Threads that need it (mostly device drivers).
|
||||
\item In the same category, register a Receiver for an interrupt. Upon
|
||||
registration, the interrupt is enabled. When the interrupt arrives, the
|
||||
registered Receiver gets a message from the kernel and the interrupt is
|
||||
disabled again. After the Thread has handled the interrupt, it must reregister
|
||||
it in order to enable it again.
|
||||
\item And similarly, allow these priviledged operations (or some of them) in an
|
||||
other thread. This is a property of the caller, because the target thread
|
||||
normally doesn't have the permission to do this (otherwise the call would not
|
||||
be needed). The result of this operation is a new Thread capability with all specified rights set. Normally this is inserted in a priviledged process's address space during setup, before it is run (instead of the capability which is obtained during Thread creation).
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Page and Cappage}
|
||||
A Page can be used to store user data. It can be mapped into an address space
|
||||
(a Memory object). Threads can then use the data directly. A Cappage is very
|
||||
similar, in that it is owned by the user. However, the user cannot see its
|
||||
contents directly. It contains a frame with Capabilities. They can be invoked
|
||||
like other owned capabilities. The main feature of a Cappage, however, is that
|
||||
they can be shared. It is a fast way to copy many capabilities to a different
|
||||
address space. Capabilities in a Cappage are not directly owned by the Memory,
|
||||
and thus cannot be dropped.
|
||||
|
||||
Operations on Page and Cappage objects:
|
||||
\begin{itemize}
|
||||
\item Copy or move the frame to a different Page, which is usually in a
|
||||
different Memory. This way, large amounts of data can be copied between
|
||||
address spaces without needing to really copy it.
|
||||
\item Set or get flags, which contain information on whether the page is
|
||||
shared, is writable, has a frame allocated, and is paying for the frame. Not
|
||||
all flags can be set in all cases.
|
||||
\item Cappages can also set a capability in the frame (pointed to with an index).
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Capability}
|
||||
A capability object can be invoked to send a message to a receiver or the
|
||||
kernel. The owner cannot see from the capability where it points. This is
|
||||
important, because the user must be able to substitute the capability for a
|
||||
different one, without the program noticing.
|
||||
different one, without the program noticing. In some cases, it is needed to
|
||||
say things about capabilities. For example, a Memory can list the Capabilities
|
||||
owned by it. In such a case, the list consists of Capabilities which point to
|
||||
other Capabilities. These capabilities can also be used to destroy the target
|
||||
capability (using an operation on the owning Memory object), for example.
|
||||
|
||||
Operations or capability objects:
|
||||
\begin{itemize}
|
||||
\item
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Thread}
|
||||
Thread objects hold the information about the current state of a thread. This
|
||||
state is used to continue running the thread. The address space is used to map
|
||||
the memory for the thread. Different threads in the same address space have
|
||||
the same memory mapping. All threads in one address space (often just one)
|
||||
together are called a process.
|
||||
|
||||
Operations on thread objects:
|
||||
\begin{itemize}
|
||||
\item
|
||||
\item Get a copy of the capability.
|
||||
\end{itemize}
|
||||
|
||||
\end{document}
|
||||
|
@ -80,6 +80,28 @@ document about how to do this. Please read that if you don't have a working
|
||||
cross-compiler, or if you would like to install libraries for cross-building
|
||||
more easily.
|
||||
|
||||
\section{Choosing a language to write in}
|
||||
Having a cross-compiler, the next thing to do is choose a language. I prefer
|
||||
to use C++ for most things. I have used C for a previous kernel, though,
|
||||
because it is more low-level. This time, I decided to try C++. But since I'm
|
||||
not linking any libraries, I need to avoid things like new and delete. For
|
||||
performance reasons I also don't use exceptions. They might need library
|
||||
support as well. So what I use C++ for is classes with member functions, and
|
||||
default function arguments. I'm not even using these all the time, and the
|
||||
whole thing is very much like C anyway.
|
||||
|
||||
Except for one change I made: I'm using a \textit{pythonic preprocessor} I
|
||||
wrote. It changes python-style indented code into something a C compiler
|
||||
accepts. It shouldn't be too hard to understand if you see the kernel source.
|
||||
Arguments to flow control instructions (if, while, for) do not need
|
||||
parenthesis, but instead have a colon at the end of the line. After a colon at
|
||||
the end of a line follows a possibly empty indented block, which is put in
|
||||
brackets. Indenting a line with respect to the previous one without a colon
|
||||
will not do anything: it makes it a continuation. Any line which is not empty
|
||||
or otherwise special gets a semicolon at the end, so you don't need to type
|
||||
those. When using both spaces and tabs (which I don't recommend), set the tab
|
||||
width to 8 spaces.
|
||||
|
||||
\section{Making things run}
|
||||
For loading a program, it must be a binary executable with a header. The
|
||||
header is inserted by mkimage. It needs a load address and an entry point.
|
||||
@ -414,6 +436,27 @@ personal copy of the data without actually copying anything. The result for
|
||||
the sender is a Page without a frame. Any mappings it has are remembered, but
|
||||
until a new frame is requested, no frame will be mapped at the address. A Page
|
||||
is also able to \textit{forget} its frame, thereby freeing some of its memory
|
||||
quota.
|
||||
quota (if it stops paying for it as well; a payed-for frame costs quota, and is
|
||||
guaranteed to be allocatable at any time).
|
||||
|
||||
Another optimization is to specify a minimum number of bytes for a page move.
|
||||
If the page needs to be copied, this reduces the time needed to complete that
|
||||
operation. The rest of the page should not contain secret data: it is possible
|
||||
that the entire page is copied, for example if it doesn't need to be copied,
|
||||
but can be reused.
|
||||
|
||||
\section{Copy on write}
|
||||
Another nice optimization is \textit{copy on write}: a page is shared
|
||||
read-only, and when a page-fault happens, the kernel will copy the contents, so
|
||||
that the other owner(s) don't see the changes. For the moment, I don't
|
||||
implemnt this. I'm not sure if I want it in the kernel at all. It can well be
|
||||
implemented using an exception handler in user space, and is not used enough to
|
||||
spend kernel space on, I think. But I can change my mind on that later.
|
||||
|
||||
\section{Memory listing}
|
||||
The last thing to do for now is allowing a memory to be listed. That is,
|
||||
having a suitably priviledged capability to a Memory should allow a program to
|
||||
see what's in it. In particular, what objects it holds, and where pages are
|
||||
mapped. Probably also what messages are in a receiver's queue.
|
||||
|
||||
\end{document}
|
||||
|
@ -32,6 +32,10 @@ void Thread::wait ():
|
||||
if flags & THREAD_FLAG_RUNNING:
|
||||
unrun ()
|
||||
flags |= THREAD_FLAG_WAITING
|
||||
// Try to receive a message from a Receiver immediately.
|
||||
for Receiver *r = receivers; r; r = r->next_owned:
|
||||
if r->try_deliver ():
|
||||
break
|
||||
|
||||
void Thread::unwait ():
|
||||
if !(flags & THREAD_FLAG_WAITING):
|
||||
|
Loading…
x
Reference in New Issue
Block a user