393 lines
9.0 KiB
C
393 lines
9.0 KiB
C
/*
|
|
* Copyright (c) 2020 Institute of Parallel And Distributed Systems (IPADS), Shanghai Jiao Tong University (SJTU)
|
|
* OS-Lab-2020 (i.e., ChCore) is licensed under the Mulan PSL v1.
|
|
* You can use this software according to the terms and conditions of the Mulan PSL v1.
|
|
* You may obtain a copy of Mulan PSL v1 at:
|
|
* http://license.coscl.org.cn/MulanPSL
|
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
* PURPOSE.
|
|
* See the Mulan PSL v1 for more details.
|
|
*/
|
|
|
|
#include <process/capability.h>
|
|
#include <process/process.h>
|
|
#include <process/thread.h>
|
|
#include <common/kmalloc.h>
|
|
#include <common/uaccess.h>
|
|
#include <common/printk.h>
|
|
|
|
const obj_deinit_func obj_deinit_tbl[TYPE_NR] = {
|
|
[0 ... TYPE_NR - 1] = NULL,
|
|
[TYPE_THREAD] = thread_deinit,
|
|
};
|
|
|
|
/* local object operation methods */
|
|
static void *get_opaque(struct process *process, int slot_id,
|
|
bool type_valid, int type)
|
|
{
|
|
struct slot_table *slot_table = &process->slot_table;
|
|
struct object_slot *slot;
|
|
void *obj;
|
|
|
|
if (!is_valid_slot_id(slot_table, slot_id)) {
|
|
obj = NULL;
|
|
goto out_unlock_table;
|
|
}
|
|
|
|
slot = get_slot(process, slot_id);
|
|
BUG_ON(slot->isvalid == false);
|
|
BUG_ON(slot->object == NULL);
|
|
|
|
if (!type_valid || slot->object->type == type) {
|
|
obj = slot->object->opaque;
|
|
} else {
|
|
obj = NULL;
|
|
goto out_unlock_slot;
|
|
}
|
|
|
|
atomic_fetch_add_64(&slot->object->refcount, 1);
|
|
|
|
out_unlock_slot:
|
|
out_unlock_table:
|
|
return obj;
|
|
}
|
|
|
|
static struct object *__object_get(struct process *process, int slot_id)
|
|
{
|
|
void *obj;
|
|
obj = get_opaque(process, slot_id, false, 0);
|
|
if (!obj)
|
|
return NULL;
|
|
else
|
|
return container_of(obj, struct object, opaque);
|
|
}
|
|
|
|
static void __object_put(struct object *object)
|
|
{
|
|
u64 old_refcount;
|
|
old_refcount = atomic_fetch_sub_64(&object->refcount, 1);
|
|
if (old_refcount == 1)
|
|
kfree(object);
|
|
}
|
|
|
|
/* object refenrence */
|
|
void *obj_get(struct process *process, int slot_id, int type)
|
|
{
|
|
return get_opaque(process, slot_id, true, type);
|
|
}
|
|
|
|
void obj_put(void *obj)
|
|
{
|
|
struct object *object = container_of(obj, struct object, opaque);
|
|
__object_put(object);
|
|
}
|
|
|
|
void *obj_alloc(u64 type, u64 size)
|
|
{
|
|
u64 total_size;
|
|
struct object *object;
|
|
|
|
// opaque is u64 so sizeof(*object) is 8-byte aligned.
|
|
// Thus the address of object-defined data is always 8-byte aligned.
|
|
total_size = sizeof(*object) + size;
|
|
object = kmalloc(total_size);
|
|
if (!object)
|
|
return NULL;
|
|
|
|
object->type = type;
|
|
object->size = size;
|
|
object->refcount = 0;
|
|
init_list_head(&object->copies_head);
|
|
|
|
return object->opaque;
|
|
}
|
|
|
|
/*
|
|
* obj_free can be used in two conditions:
|
|
* 1. In initialization of a cap (after obj_alloc and before cap_aloc) as a
|
|
* fallback to obj_alloc
|
|
* 2. To all slots which point to it. In this use case, the
|
|
* caller must guarantee that the object is not freed (i.e., should hold a
|
|
* reference to it).
|
|
*/
|
|
void obj_free(void *obj)
|
|
{
|
|
struct object *object;
|
|
struct object_slot *slot_iter = NULL, *slot_iter_tmp = NULL;
|
|
int r;
|
|
|
|
if (!obj)
|
|
return;
|
|
object = container_of(obj, struct object, opaque);
|
|
|
|
/* fallback of obj_alloc */
|
|
if (object->refcount == 0) {
|
|
kfree(object);
|
|
return;
|
|
}
|
|
|
|
/* free all copied slots */
|
|
for_each_in_list_safe(slot_iter, slot_iter_tmp,
|
|
copies, &object->copies_head) {
|
|
u64 iter_slot_id = slot_iter->slot_id;
|
|
struct process *iter_process = slot_iter->process;
|
|
|
|
r = cap_free(iter_process, iter_slot_id);
|
|
BUG_ON(r != 0);
|
|
}
|
|
}
|
|
|
|
int cap_alloc(struct process *process, void *obj, u64 rights)
|
|
{
|
|
struct object *object;
|
|
struct object_slot *slot;
|
|
int r, slot_id;
|
|
|
|
object = container_of(obj, struct object, opaque);
|
|
|
|
slot_id = alloc_slot_id(process);
|
|
if (slot_id < 0) {
|
|
r = -ENOMEM;
|
|
goto out_unlock_table;
|
|
}
|
|
|
|
slot = kmalloc(sizeof(*slot));
|
|
if (!slot) {
|
|
r = -ENOMEM;
|
|
goto out_free_slot_id;
|
|
}
|
|
slot->slot_id = slot_id;
|
|
slot->process = process;
|
|
slot->isvalid = true;
|
|
slot->rights = rights;
|
|
slot->object = object;
|
|
list_add(&slot->copies, &object->copies_head);
|
|
|
|
BUG_ON(object->refcount != 0);
|
|
object->refcount = 1;
|
|
|
|
install_slot(process, slot_id, slot);
|
|
|
|
return slot_id;
|
|
out_free_slot_id:
|
|
free_slot_id(process, slot_id);
|
|
out_unlock_table:
|
|
return r;
|
|
}
|
|
|
|
int cap_free(struct process *process, int slot_id)
|
|
{
|
|
struct object_slot *slot;
|
|
struct object *object;
|
|
int r = 0;
|
|
u64 old_refcount;
|
|
obj_deinit_func func;
|
|
|
|
slot = get_slot(process, slot_id);
|
|
if (!slot || slot->isvalid == false) {
|
|
r = -ECAPBILITY;
|
|
goto out_unlock_table;
|
|
}
|
|
|
|
free_slot_id(process, slot_id);
|
|
/* no need to get slot_guard as it can not be accessed */
|
|
|
|
object = slot->object;
|
|
old_refcount = atomic_fetch_sub_64(&object->refcount, 1);
|
|
if (old_refcount == 1) {
|
|
func = obj_deinit_tbl[object->type];
|
|
if (func)
|
|
func(object->opaque);
|
|
if (object->refcount == 0)
|
|
kfree(object);
|
|
}
|
|
|
|
slot->isvalid = false;
|
|
slot->object = NULL;
|
|
list_del(&slot->copies);
|
|
kfree(slot);
|
|
|
|
return r;
|
|
out_unlock_table:
|
|
return r;
|
|
}
|
|
|
|
int cap_copy(struct process *src_process, struct process *dest_process,
|
|
int src_slot_id, bool new_rights_valid, u64 new_rights)
|
|
{
|
|
struct object_slot *src_slot, *dest_slot;
|
|
int r, dest_slot_id;
|
|
|
|
src_slot = get_slot(src_process, src_slot_id);
|
|
if (!src_slot || src_slot->isvalid == false) {
|
|
r = -ECAPBILITY;
|
|
goto out_unlock;
|
|
}
|
|
|
|
dest_slot_id = alloc_slot_id(dest_process);
|
|
if (dest_slot_id == -1) {
|
|
r = -ENOMEM;
|
|
goto out_unlock;
|
|
}
|
|
|
|
dest_slot = kmalloc(sizeof(*dest_slot));
|
|
if (!dest_slot) {
|
|
r = -ENOMEM;
|
|
goto out_free_slot_id;
|
|
}
|
|
src_slot = get_slot(src_process, src_slot_id);
|
|
atomic_fetch_add_64(&src_slot->object->refcount, 1);
|
|
|
|
dest_slot->slot_id = dest_slot_id;
|
|
dest_slot->process = dest_process;
|
|
dest_slot->isvalid = true;
|
|
dest_slot->object = src_slot->object;
|
|
dest_slot->rights = new_rights_valid ? new_rights : src_slot->rights;
|
|
list_add(&dest_slot->copies, &src_slot->copies);
|
|
|
|
install_slot(dest_process, dest_slot_id, dest_slot);
|
|
|
|
return dest_slot_id;
|
|
out_free_slot_id:
|
|
free_slot_id(dest_process, dest_slot_id);
|
|
out_unlock:
|
|
return r;
|
|
}
|
|
|
|
/*
|
|
* Copy capability within the same process.
|
|
* Returns new cap or error code.
|
|
*/
|
|
int cap_copy_local(struct process *process, int src_slot_id, u64 new_rights)
|
|
{
|
|
return cap_copy(process, process, src_slot_id, true, new_rights);
|
|
}
|
|
|
|
int cap_move(struct process *src_process, struct process *dest_process,
|
|
int src_slot_id, bool new_rights_valid, u64 new_rights)
|
|
{
|
|
int r;
|
|
|
|
r = cap_copy(src_process, dest_process, src_slot_id,
|
|
new_rights_valid, new_rights);
|
|
if (r < 0)
|
|
return r;
|
|
r = cap_free(src_process, src_slot_id);
|
|
BUG_ON(r); /* if copied successfully, free should not fail */
|
|
|
|
return r;
|
|
}
|
|
|
|
int cap_revoke(struct process *process, int slot_id)
|
|
{
|
|
struct object *object;
|
|
int r = 0;
|
|
|
|
object = __object_get(process, slot_id);
|
|
if (!object) {
|
|
return -ECAPBILITY;
|
|
}
|
|
obj_free(object->opaque);
|
|
|
|
__object_put(object);
|
|
return r;
|
|
}
|
|
|
|
int sys_cap_copy_to(u64 dest_process_cap, u64 src_slot_id)
|
|
{
|
|
struct process *dest_process;
|
|
int r;
|
|
|
|
dest_process = obj_get(current_process, dest_process_cap, TYPE_PROCESS);
|
|
if (!dest_process)
|
|
return -ECAPBILITY;
|
|
r = cap_copy(current_process, dest_process, src_slot_id, 0, 0);
|
|
obj_put(dest_process);
|
|
return r;
|
|
}
|
|
|
|
int sys_cap_copy_from(u64 src_process_cap, u64 src_slot_id)
|
|
{
|
|
struct process *src_process;
|
|
int r;
|
|
|
|
src_process = obj_get(current_process, src_process_cap, TYPE_PROCESS);
|
|
if (!src_process)
|
|
return -ECAPBILITY;
|
|
r = cap_copy(src_process, current_process, src_slot_id, 0, 0);
|
|
obj_put(src_process);
|
|
return r;
|
|
}
|
|
|
|
int sys_transfer_caps(u64 dest_group_cap, u64 src_caps_buf, int nr_caps,
|
|
u64 dst_caps_buf)
|
|
{
|
|
struct process *dest_process;
|
|
int i;
|
|
int *src_caps;
|
|
int *dst_caps;
|
|
size_t size;
|
|
|
|
dest_process = obj_get(current_process, dest_group_cap, TYPE_PROCESS);
|
|
if (!dest_process)
|
|
return -ECAPBILITY;
|
|
|
|
size = sizeof(int) * nr_caps;
|
|
src_caps = kmalloc(size);
|
|
dst_caps = kmalloc(size);
|
|
|
|
/* get args from user buffer */
|
|
copy_from_user((void *)src_caps, (void *)src_caps_buf, size);
|
|
|
|
for (i = 0; i < nr_caps; ++i) {
|
|
dst_caps[i] = cap_copy(current_process, dest_process,
|
|
src_caps[i], 0, 0);
|
|
}
|
|
|
|
/* write results to user buffer */
|
|
copy_to_user((void *)dst_caps_buf, (void *)dst_caps, size);
|
|
|
|
obj_put(dest_process);
|
|
return 0;
|
|
}
|
|
|
|
int sys_cap_move(u64 dest_process_cap, u64 src_slot_id)
|
|
{
|
|
struct process *dest_process;
|
|
int r;
|
|
|
|
dest_process = obj_get(current_process, dest_process_cap, TYPE_PROCESS);
|
|
if (!dest_process)
|
|
return -ECAPBILITY;
|
|
r = cap_move(current_process, dest_process, src_slot_id, 0, 0);
|
|
obj_put(dest_process);
|
|
return r;
|
|
}
|
|
|
|
// for debug
|
|
int sys_get_all_caps(u64 process_cap)
|
|
{
|
|
struct process *process;
|
|
struct slot_table *slot_table;
|
|
int i;
|
|
|
|
process = obj_get(current_process, process_cap, TYPE_PROCESS);
|
|
if (!process)
|
|
return -ECAPBILITY;
|
|
printk("thread %p cap:\n", current_thread);
|
|
|
|
slot_table = &process->slot_table;
|
|
for (i = 0; i < slot_table->slots_size; i++) {
|
|
struct object_slot *slot = get_slot(process, i);
|
|
if (!slot)
|
|
continue;
|
|
BUG_ON(slot->isvalid != true);
|
|
printk("slot_id:%d type:%d\n", i,
|
|
slot_table->slots[i]->object->type);
|
|
}
|
|
|
|
obj_put(process);
|
|
return 0;
|
|
}
|