mirror of
https://github.com/drasko/codezero.git
synced 2026-01-14 03:43:15 +01:00
590 lines
16 KiB
C
590 lines
16 KiB
C
/*
|
|
* Pager's capabilities for kernel resources
|
|
*
|
|
* Copyright (C) 2009 Bahadir Balban
|
|
*/
|
|
#include <bootm.h>
|
|
#include <init.h>
|
|
#include <memory.h>
|
|
#include <capability.h>
|
|
#include <l4/api/errno.h>
|
|
#include <l4/lib/list.h>
|
|
#include L4LIB_INC_ARCH(syscalls.h)
|
|
#include <l4/generic/cap-types.h> /* TODO: Move this to API */
|
|
#include L4LIB_INC_ARCH(syslib.h)
|
|
#include <mem/malloc.h>
|
|
#include <user.h>
|
|
|
|
/* Capability descriptor list */
|
|
struct cap_list capability_list;
|
|
|
|
__initdata static struct capability *caparray;
|
|
__initdata static int total_caps = 0;
|
|
|
|
void cap_list_print(struct cap_list *cap_list)
|
|
{
|
|
struct capability *cap;
|
|
printf("Capabilities\n"
|
|
"~~~~~~~~~~~~\n");
|
|
|
|
list_foreach_struct(cap, &cap_list->caps, list)
|
|
cap_print(cap);
|
|
|
|
printf("\n");
|
|
}
|
|
|
|
#if 0
|
|
#define PAGER_TOTAL_MUTEX 5
|
|
int setup_children_mutex(int total_caps, struct cap_list *cap_list)
|
|
{
|
|
struct capability *diff_cap, *mutex_cap;
|
|
|
|
struct task_ids ids;
|
|
int err;
|
|
|
|
l4_getid(&ids);
|
|
|
|
//cap_list_print(cap_list);
|
|
|
|
/* Find out own mutex capability on our own container */
|
|
list_foreach_struct(mutex_cap, &cap_list->caps, list) {
|
|
if (cap_type(mutex_cap) == CAP_TYPE_QUANTITY &&
|
|
cap_rtype(mutex_cap) == CAP_RTYPE_MUTEXPOOL)
|
|
goto found;
|
|
}
|
|
printf("cont%d: %s: FATAL: Could not find ipc "
|
|
"capability to own container.\n",
|
|
__cid(ids.tid), __FUNCTION__);
|
|
BUG();
|
|
|
|
found:
|
|
/* Create a new capability */
|
|
BUG_ON(!(diff_cap = kzalloc(sizeof(*mutex_cap))));
|
|
|
|
/* Copy it over to new mutex cap buffer */
|
|
memcpy(diff_cap, mutex_cap, sizeof (*mutex_cap));
|
|
|
|
/*
|
|
* We would like to take some mutexes,
|
|
* and leave the rest to children.
|
|
*
|
|
* We set up a capability that we want
|
|
* to separate out from the original
|
|
*/
|
|
if (mutex_cap->size <= PAGER_TOTAL_MUTEX) {
|
|
printf("%s: FATAL: Can't reserve enough mutexes "
|
|
"for children. capid = %d, mutexes = %lu, "
|
|
"pager needs = %d\n", __FUNCTION__,
|
|
mutex_cap->capid, mutex_cap->size,
|
|
PAGER_TOTAL_MUTEX);
|
|
BUG();
|
|
}
|
|
|
|
/* Reserve out some mutexes to self */
|
|
diff_cap->size = PAGER_TOTAL_MUTEX;
|
|
|
|
/* Split the mutex capability, passing the difference */
|
|
if ((err = l4_capability_control(CAP_CONTROL_SPLIT,
|
|
0, diff_cap)) < 0) {
|
|
printf("l4_capability_control() replication of "
|
|
"ipc capability failed.\n Could not "
|
|
"complete CAP_CONTROL_SPLIT request on cap (%d), "
|
|
"err = %d.\n", diff_cap->capid, err);
|
|
BUG();
|
|
}
|
|
|
|
/*
|
|
* The returned one is the given diff, but
|
|
* created as a new capability, add it to list
|
|
*/
|
|
cap_list_insert(diff_cap, cap_list);
|
|
// cap_list_print(cap_list);
|
|
|
|
/*
|
|
* Share the remainder capability with our container.
|
|
*
|
|
* This effectively enables all threads/spaces in this container
|
|
* to use this pool of mutexes.
|
|
*/
|
|
if ((err = l4_capability_control(CAP_CONTROL_SHARE, CAP_SHARE_SINGLE,
|
|
mutex_cap)) < 0) {
|
|
printf("l4_capability_control() sharing of "
|
|
"capabilities failed.\n Could not "
|
|
"complete CAP_CONTROL_SHARE request.\n");
|
|
BUG();
|
|
}
|
|
// cap_list_print(cap_list);
|
|
|
|
/* Find mutex syscall operation capability on our own container */
|
|
list_foreach_struct(mutex_cap, &cap_list->caps, list) {
|
|
if (cap_type(mutex_cap) == CAP_TYPE_UMUTEX &&
|
|
cap_rtype(mutex_cap) == CAP_RTYPE_CONTAINER)
|
|
goto found2;
|
|
}
|
|
printf("cont%d: %s: FATAL: Could not find UMUTEX "
|
|
"capability to own container.\n",
|
|
__cid(ids.tid), __FUNCTION__);
|
|
BUG();
|
|
|
|
found2:
|
|
|
|
/*
|
|
* Share it with our container.
|
|
*
|
|
* This effectively enables all threads/spaces in this container
|
|
* to use this pool of mutexes.
|
|
*/
|
|
if ((err = l4_capability_control(CAP_CONTROL_SHARE, CAP_SHARE_SINGLE,
|
|
mutex_cap)) < 0) {
|
|
printf("l4_capability_control() sharing of "
|
|
"capabilities failed.\n Could not "
|
|
"complete CAP_CONTROL_SHARE request.\n");
|
|
BUG();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Replicate, deduce and grant to children the capability to
|
|
* talk to us only.
|
|
*
|
|
* We are effectively creating an ipc capability from what we already
|
|
* own, and the new one has a reduced privilege in terms of the
|
|
* targetable resource.
|
|
*
|
|
* We are replicating our capability to talk to our complete container
|
|
* into a capability to only talk to our current space. Our space is a
|
|
* reduced target, since it is a subset contained in our container.
|
|
*/
|
|
int setup_children_ipc(int total_caps, struct cap_list *cap_list)
|
|
{
|
|
struct capability *ipc_cap, *cap;
|
|
struct task_ids ids;
|
|
int err;
|
|
|
|
l4_getid(&ids);
|
|
|
|
// cap_list_print(cap_list);
|
|
|
|
/* Find out our own ipc capability on our own container */
|
|
list_foreach_struct(cap, &cap_list->caps, list) {
|
|
if (cap_type(cap) == CAP_TYPE_IPC &&
|
|
cap_rtype(cap) == CAP_RTYPE_CONTAINER &&
|
|
cap->resid == __cid(ids.tid))
|
|
goto found;
|
|
}
|
|
printf("cont%d: %s: FATAL: Could not find ipc "
|
|
"capability to own container.\n",
|
|
__cid(ids.tid), __FUNCTION__);
|
|
BUG();
|
|
|
|
found:
|
|
/* Create a new capability */
|
|
BUG_ON(!(ipc_cap = kzalloc(sizeof(*ipc_cap))));
|
|
|
|
/* Copy it over to new ipc cap buffer */
|
|
memcpy(ipc_cap, cap, sizeof (*cap));
|
|
|
|
/* Replicate the ipc capability, giving original as reference */
|
|
if ((err = l4_capability_control(CAP_CONTROL_REPLICATE,
|
|
0, ipc_cap)) < 0) {
|
|
printf("l4_capability_control() replication of "
|
|
"ipc capability failed.\n Could not "
|
|
"complete CAP_CONTROL_REPLICATE request on cap (%d), "
|
|
"err = %d.\n", ipc_cap->capid, err);
|
|
BUG();
|
|
}
|
|
|
|
/* Add it to list */
|
|
cap_list_insert(ipc_cap, cap_list);
|
|
// cap_list_print(cap_list);
|
|
|
|
/*
|
|
* The returned capability is a replica.
|
|
*
|
|
* Now deduce it such that it applies to talking only to us,
|
|
* instead of to the whole container as original.
|
|
*/
|
|
cap_set_rtype(ipc_cap, CAP_RTYPE_SPACE);
|
|
ipc_cap->resid = ids.spid; /* This space is target resource */
|
|
if ((err = l4_capability_control(CAP_CONTROL_DEDUCE,
|
|
0, ipc_cap)) < 0) {
|
|
printf("l4_capability_control() deduction of "
|
|
"ipc capability failed.\n Could not "
|
|
"complete CAP_CONTROL_DEDUCE request on cap (%d), "
|
|
"err = %d.\n", ipc_cap->capid, err);
|
|
BUG();
|
|
}
|
|
|
|
// cap_list_print(cap_list);
|
|
|
|
/*
|
|
* Share it with our container.
|
|
*
|
|
* This effectively enables all threads/spaces in this container
|
|
* to communicate to us only, and be able to do nothing else.
|
|
*/
|
|
if ((err = l4_capability_control(CAP_CONTROL_SHARE,
|
|
CAP_SHARE_SINGLE,
|
|
ipc_cap)) < 0) {
|
|
printf("l4_capability_control() sharing of "
|
|
"capabilities failed.\n Could not "
|
|
"complete CAP_CONTROL_SHARE request.\n");
|
|
BUG();
|
|
}
|
|
// cap_list_print(cap_list);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
int setup_children_caps(int total_caps, struct cap_list *cap_list)
|
|
{
|
|
setup_children_ipc(total_caps, cap_list);
|
|
setup_children_mutex(total_caps, cap_list);
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/* Copy all init-memory allocated capabilities */
|
|
void copy_boot_capabilities(int ncaps)
|
|
{
|
|
struct capability *cap;
|
|
|
|
capability_list.ncaps = 0;
|
|
link_init(&capability_list.caps);
|
|
|
|
for (int i = 0; i < ncaps; i++) {
|
|
cap = kzalloc(sizeof(struct capability));
|
|
|
|
/* This copies kernel-allocated unique cap id as well */
|
|
memcpy(cap, &caparray[i], sizeof(struct capability));
|
|
|
|
/* Initialize capability list */
|
|
link_init(&cap->list);
|
|
|
|
/* Add capability to global cap list */
|
|
cap_list_insert(cap, &capability_list);
|
|
}
|
|
}
|
|
|
|
int cap_read_all()
|
|
{
|
|
int ncaps;
|
|
int err;
|
|
struct capability *cap;
|
|
|
|
/* Read number of capabilities */
|
|
if ((err = l4_capability_control(CAP_CONTROL_NCAPS,
|
|
0, &ncaps)) < 0) {
|
|
printf("l4_capability_control() reading # of"
|
|
" capabilities failed.\n Could not "
|
|
"complete CAP_CONTROL_NCAPS request.\n");
|
|
BUG();
|
|
}
|
|
total_caps = ncaps;
|
|
|
|
printf("%s: Total of %d capabilities.\n", __FUNCTION__, total_caps);
|
|
|
|
/* Allocate array of caps from boot memory */
|
|
caparray = alloc_bootmem(sizeof(struct capability) * ncaps, 0);
|
|
|
|
/* Read all capabilities */
|
|
if ((err = l4_capability_control(CAP_CONTROL_READ,
|
|
0, caparray)) < 0) {
|
|
printf("l4_capability_control() reading of "
|
|
"capabilities failed.\n Could not "
|
|
"complete CAP_CONTROL_READ_CAPS request.\n");
|
|
BUG();
|
|
}
|
|
|
|
|
|
/* Copy them to real allocated structures */
|
|
copy_boot_capabilities(ncaps);
|
|
|
|
// cap_list_print(&capability_list);
|
|
|
|
memset(&cont_mem_regions, 0, sizeof(cont_mem_regions));
|
|
|
|
/* Set up pointers to important capabilities */
|
|
list_foreach_struct(cap, &capability_list.caps, list) {
|
|
/* Physical memory bank */
|
|
if (cap_type(cap) == CAP_TYPE_MAP_PHYSMEM)
|
|
cont_mem_regions.physmem = cap;
|
|
|
|
/* Virtual regions */
|
|
if (cap_type(cap) == CAP_TYPE_MAP_VIRTMEM) {
|
|
|
|
/* Pager address region (get from linker-defined) */
|
|
if (__pfn_to_addr(cap->start)
|
|
== (unsigned long)virtual_base)
|
|
cont_mem_regions.pager = cap;
|
|
|
|
/* UTCB address region */
|
|
else if (UTCB_REGION_START ==
|
|
__pfn_to_addr(cap->start)) {
|
|
if (UTCB_REGION_END !=
|
|
__pfn_to_addr(cap->end)) {
|
|
printf("FATAL: Region designated "
|
|
"for UTCB allocation does not "
|
|
"match on start/end marks");
|
|
BUG();
|
|
}
|
|
|
|
if (!(cap->access & CAP_MAP_UTCB)) {
|
|
printf("FATAL: Region designated "
|
|
"for UTCB allocation does not "
|
|
"have UTCB map permissions");
|
|
BUG();
|
|
}
|
|
cont_mem_regions.utcb = cap;
|
|
}
|
|
|
|
/* Shared memory disjoint region */
|
|
else if (SHMEM_REGION_START ==
|
|
__pfn_to_addr(cap->start)) {
|
|
if (SHMEM_REGION_END !=
|
|
__pfn_to_addr(cap->end)) {
|
|
printf("FATAL: Region designated "
|
|
"for SHM allocation does not "
|
|
"match on start/end marks");
|
|
BUG();
|
|
}
|
|
|
|
cont_mem_regions.shmem = cap;
|
|
}
|
|
|
|
/* Task memory region */
|
|
else if (TASK_REGION_START ==
|
|
__pfn_to_addr(cap->start)) {
|
|
if (TASK_REGION_END !=
|
|
__pfn_to_addr(cap->end)) {
|
|
printf("FATAL: Region designated "
|
|
"for Task address space does"
|
|
"not match on start/end mark.");
|
|
BUG();
|
|
}
|
|
cont_mem_regions.task = cap;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!cont_mem_regions.task ||
|
|
!cont_mem_regions.shmem ||
|
|
!cont_mem_regions.utcb ||
|
|
!cont_mem_regions.physmem ||
|
|
!cont_mem_regions.pager) {
|
|
printf("%s: Error, pager does not have one of the required "
|
|
"mem capabilities defined. (TASK, SHM, PHYSMEM, UTCB, PAGER)\n",
|
|
__TASKNAME__);
|
|
if (cont_mem_regions.task) {
|
|
printf("task start: %lx\n", cont_mem_regions.task->start << 12);
|
|
printf("task end: %lx\n", cont_mem_regions.task->end << 12);
|
|
}
|
|
if (cont_mem_regions.shmem) {
|
|
printf("shmem start: %lx\n", cont_mem_regions.shmem->start << 12);
|
|
printf("shmem end: %lx\n", cont_mem_regions.shmem->end << 12);
|
|
}
|
|
if (cont_mem_regions.physmem) {
|
|
printf("physmem start: %lx\n", cont_mem_regions.physmem->start << 12);
|
|
printf("physmem end: %lx\n", cont_mem_regions.physmem->end << 12);
|
|
}
|
|
if (cont_mem_regions.utcb) {
|
|
printf("utcb start: %lx\n", cont_mem_regions.utcb->start << 12);
|
|
printf("utcb end: %lx\n", cont_mem_regions.utcb->end << 12);
|
|
}
|
|
if (cont_mem_regions.pager) {
|
|
printf("pager start: %lx\n", cont_mem_regions.pager->start << 12);
|
|
printf("pager end: %lx\n", cont_mem_regions.pager->end << 12);
|
|
}
|
|
BUG();
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void setup_caps()
|
|
{
|
|
cap_read_all();
|
|
// setup_children_caps(total_caps, &capability_list);
|
|
}
|
|
|
|
#if 0
|
|
/*
|
|
* Find our own, widened replicable capability of same type as given,
|
|
* replicate, reduce and grant as described with given parameters.
|
|
* Assumes parameters have already been validated and security-checked.
|
|
*/
|
|
int cap_find_replicate_reduce_grant(struct capability *cap)
|
|
{
|
|
struct capability *possessed;
|
|
struct capability new_cap;
|
|
int err;
|
|
|
|
/* Merely match type, kernel does actual check on suitability */
|
|
list_foreach_struct(possessed, &capability_list.caps, list) {
|
|
/* Different type, pass */
|
|
if (cap_type(possessed) != cap_type(cap))
|
|
continue;
|
|
|
|
/* Copy possessed one to new one's buffer */
|
|
memcpy(&new_cap, possessed, sizeof(*possessed));
|
|
|
|
/* Replicate capability, giving original as reference */
|
|
if ((err = l4_capability_control(CAP_CONTROL_REPLICATE,
|
|
0, &new_cap)) < 0) {
|
|
printf("l4_capability_control() replication of "
|
|
"capability failed.\n Could not complete "
|
|
"CAP_CONTROL_REPLICATE request on cap (%d), "
|
|
"err = %d.\n", new_cap.capid, err);
|
|
return err;
|
|
}
|
|
|
|
/*
|
|
* The returned capability is a replica.
|
|
*
|
|
* We don't add the newly created one to our own internal
|
|
* list because we will grant it shortly and lose its
|
|
* possession
|
|
*
|
|
* Now deduce it such that it looks like the one requested.
|
|
* Note, we assume the request had been validated before.
|
|
* Also note, the owner shall be still us.
|
|
*/
|
|
new_cap.resid = cap->resid;
|
|
new_cap.type = cap->type;
|
|
new_cap.access = cap->access;
|
|
new_cap.start = cap->start;
|
|
new_cap.end = cap->end;
|
|
new_cap.size = cap->size;
|
|
new_cap.used = cap->used;
|
|
|
|
/*
|
|
* Make sure it is transferable,
|
|
* since we will need to grant it soon
|
|
*/
|
|
new_cap.access |= CAP_TRANSFERABLE;
|
|
|
|
|
|
if ((err = l4_capability_control(CAP_CONTROL_DEDUCE,
|
|
0, &new_cap)) < 0) {
|
|
/* Couldn't deduce this one, destroy the replica */
|
|
if ((err =
|
|
l4_capability_control(CAP_CONTROL_DESTROY,
|
|
0, &new_cap)) < 0) {
|
|
printf("l4_capability_control() destruction of "
|
|
"capability failed.\n Could not "
|
|
"complete CAP_CONTROL_DESTROY request "
|
|
" on cap (%d), err = %d.\n",
|
|
new_cap.capid, err);
|
|
BUG();
|
|
}
|
|
} else /* Success */
|
|
goto success;
|
|
}
|
|
|
|
return -ENOCAP;
|
|
|
|
success:
|
|
/*
|
|
* Found suitable one to replicate/deduce.
|
|
* Grant it to requested owner.
|
|
*
|
|
* This effectively enables the owner to have all
|
|
* operations defined in the capability. However,
|
|
* we use a flag to make the capability immutable
|
|
* as we grant it. (We wouldn't be able to grant
|
|
* it if it had no grant permission originally. We
|
|
* remove it _as_ we grant it)
|
|
*/
|
|
new_cap.owner = cap->owner; /* Indicate new owner */
|
|
if ((err = l4_capability_control(CAP_CONTROL_GRANT,
|
|
CAP_GRANT_SINGLE |
|
|
CAP_GRANT_IMMUTABLE,
|
|
&new_cap)) < 0) {
|
|
printf("l4_capability_control() granting of "
|
|
"capability (%d) failed.\n Could not "
|
|
"complete CAP_CONTROL_GRANT request.\n",
|
|
new_cap.capid);
|
|
return err;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* A task that we manage requests a capability to do an operation
|
|
* from us, such as the capability to do a particular ipc to a
|
|
* particular thread. We consider the request and give the
|
|
* capability if it is appropriate. This currently supports only
|
|
* ipc.
|
|
*/
|
|
int sys_request_cap(struct tcb *task, struct capability *__cap_userptr)
|
|
{
|
|
struct tcb *target;
|
|
struct capability *cap;
|
|
int ret;
|
|
|
|
if (!(cap = pager_get_user_page(task, __cap_userptr,
|
|
sizeof(*__cap_userptr),
|
|
VM_READ | VM_WRITE)))
|
|
return -EFAULT;
|
|
|
|
/* Only support IPC requests for now */
|
|
if (cap_type(cap) != CAP_TYPE_IPC) {
|
|
ret = -EPERM;
|
|
}
|
|
|
|
/* Validate rest of the fields */
|
|
if (cap->start || cap->end || cap->used || cap->size) {
|
|
ret = -EINVAL;
|
|
goto out;
|
|
}
|
|
|
|
if (cap_generic_perms(cap) != CAP_IMMUTABLE) {
|
|
ret = -EPERM;
|
|
goto out;
|
|
}
|
|
|
|
/* Find out who the task wants to ipc */
|
|
switch (cap_rtype(cap)) {
|
|
/* Is it a thread? */
|
|
case CAP_RTYPE_THREAD:
|
|
/* Find the thread */
|
|
if (!(target = find_task(cap->resid))) {
|
|
ret = -ESRCH;
|
|
goto out;
|
|
}
|
|
|
|
/* Requester must be the owner */
|
|
if (cap->owner != task->tid) {
|
|
ret = -EPERM;
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* It is a thread that we are managing, nothing
|
|
* special requested here, just grant it
|
|
*/
|
|
if ((ret = cap_find_replicate_reduce_grant(cap)) < 0)
|
|
goto out;
|
|
break;
|
|
case CAP_RTYPE_SPACE:
|
|
/* Space requests not allowed */
|
|
ret = -EPERM;
|
|
goto out;
|
|
case CAP_RTYPE_CONTAINER:
|
|
/* Container requests not allowed */
|
|
ret = -EPERM;
|
|
goto out;
|
|
}
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
#endif
|
|
|