2021-05-23 22:24:02 +08:00

224 lines
6.2 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 <common/errno.h>
#include <common/kprint.h>
#include <common/macro.h>
#include <common/util.h>
#include <ipc/ipc.h>
#include <exception/irq.h>
#include <common/kmalloc.h>
#include <common/mm.h>
#include <common/uaccess.h>
#include <process/thread.h>
#include <sched/context.h>
#include <sched/sched.h>
#define SHADOW_THREAD_PRIO MAX_PRIO - 1
/**
* Helper function called when an ipc_connection is created
*/
static struct thread *create_server_thread(struct thread *src)
{
struct thread *new;
new = kmalloc(sizeof(struct thread));
BUG_ON(new == NULL);
new->vmspace = obj_get(src->process, VMSPACE_OBJ_ID, TYPE_VMSPACE);
BUG_ON(!new->vmspace);
// Init the thread ctx
new->thread_ctx = create_thread_ctx();
if (!new->thread_ctx)
goto out_fail;
memcpy((char *)&(new->thread_ctx->ec),
(const char *)&(src->thread_ctx->ec), sizeof(arch_exec_cont_t));
new->thread_ctx->prio = SHADOW_THREAD_PRIO;
new->thread_ctx->state = TS_INIT;
new->thread_ctx->affinity = NO_AFF;
new->thread_ctx->sc = NULL;
new->thread_ctx->type = TYPE_SHADOW;
// Init the server ipc
new->server_ipc_config = kzalloc(sizeof(struct server_ipc_config));
if (!new->server_ipc_config)
goto out_destroy_thread_ctx;
new->server_ipc_config->callback = src->server_ipc_config->callback;
new->server_ipc_config->vm_config = src->server_ipc_config->vm_config;
new->process = src->process;
obj_put(new->vmspace);
return new;
out_destroy_thread_ctx:
destroy_thread_ctx(new);
out_fail:
obj_put(new->vmspace);
kfree(new);
return NULL;
}
/**
* Helper function to create an ipc_connection by the client thread
*/
static int create_connection(struct thread *source, struct thread *target,
struct ipc_vm_config *client_vm_config)
{
struct ipc_connection *conn = NULL;
int ret = 0;
int conn_cap = 0, server_conn_cap = 0;
struct pmobject *stack_pmo, *buf_pmo;
int conn_idx;
struct server_ipc_config *server_ipc_config;
struct ipc_vm_config *vm_config;
u64 server_stack_base, server_buf_base, client_buf_base;
u64 stack_size, buf_size;
BUG_ON(source == NULL);
BUG_ON(target == NULL);
// Get the ipc_connection
conn = obj_alloc(TYPE_CONNECTION, sizeof(*conn));
if (!conn) {
ret = -ENOMEM;
goto out_fail;
}
conn->target = create_server_thread(target);
if (!conn->target) {
ret = -ENOMEM;
goto out_fail;
}
// Get the server's ipc config
server_ipc_config = target->server_ipc_config;
vm_config = &server_ipc_config->vm_config;
conn_idx = find_next_zero_bit(server_ipc_config->conn_bmp,
server_ipc_config->max_client, 0);
set_bit(conn_idx, server_ipc_config->conn_bmp);
// Create the server thread's stack
server_stack_base =
vm_config->stack_base_addr + conn_idx * vm_config->stack_size;
stack_size = vm_config->stack_size;
kdebug("server stack base:%lx size:%lx\n", server_stack_base,
stack_size);
stack_pmo = kmalloc(sizeof(struct pmobject));
if (!stack_pmo) {
ret = -ENOMEM;
goto out_free_obj;
}
pmo_init(stack_pmo, PMO_DATA, stack_size, 0);
vmspace_map_range(target->vmspace, server_stack_base, stack_size,
VMR_READ | VMR_WRITE, stack_pmo);
conn->server_stack_top = server_stack_base + stack_size;
// Create and map the shared buffer for client and server
server_buf_base =
vm_config->buf_base_addr + conn_idx * vm_config->buf_size;
client_buf_base = client_vm_config->buf_base_addr;
buf_size = MIN(vm_config->buf_size, client_vm_config->buf_size);
client_vm_config->buf_size = buf_size;
kdebug("server buf base:%lx size:%lx, client base:%lx\n",
server_stack_base, stack_size, client_buf_base);
buf_pmo = kmalloc(sizeof(struct pmobject));
if (!buf_pmo) {
ret = -ENOMEM;
goto out_free_stack_pmo;
}
pmo_init(buf_pmo, PMO_DATA, buf_size, 0);
vmspace_map_range(current_thread->vmspace, client_buf_base, buf_size,
VMR_READ | VMR_WRITE, buf_pmo);
vmspace_map_range(target->vmspace, server_buf_base, buf_size,
VMR_READ | VMR_WRITE, buf_pmo);
conn->buf.client_user_addr = client_buf_base;
conn->buf.server_user_addr = server_buf_base;
conn_cap = cap_alloc(current_process, conn, 0);
if (conn_cap < 0) {
ret = conn_cap;
goto out_free_obj;
}
server_conn_cap =
cap_copy(current_process, target->process, conn_cap, 0, 0);
if (server_conn_cap < 0) {
ret = server_conn_cap;
goto out_free_obj;
}
conn->server_conn_cap = server_conn_cap;
return conn_cap;
out_free_stack_pmo:
kfree(stack_pmo);
out_free_obj:
obj_free(conn);
out_fail:
return ret;
}
u32 sys_register_client(u32 server_cap, u64 vm_config_ptr)
{
struct thread *client = current_thread;
struct thread *server = NULL;
struct ipc_connection *conn;
struct ipc_vm_config vm_config = { 0 };
u64 client_buf_size;
int conn_cap = 0;
int r = 0;
r = copy_from_user((char *)&vm_config, (char *)vm_config_ptr,
sizeof(vm_config));
if (r < 0)
goto out_fail;
if (!is_user_addr_range(vm_config.buf_base_addr, vm_config.buf_size) ||
!IS_ALIGNED(vm_config.buf_base_addr, PAGE_SIZE) ||
!IS_ALIGNED(vm_config.buf_size, PAGE_SIZE)) {
r = -EINVAL;
goto out_fail;
}
server = obj_get(current_thread->process, server_cap, TYPE_THREAD);
if (!server) {
r = -ECAPBILITY;
goto out_fail;
}
client_buf_size = vm_config.buf_size;
conn_cap = create_connection(client, server, &vm_config);
if (conn_cap < 0) {
r = conn_cap;
goto out_obj_put_thread;
}
conn = obj_get(current_process, conn_cap, TYPE_CONNECTION);
if (client_buf_size != vm_config.buf_size) {
r = copy_to_user((char *)vm_config_ptr, (char *)&vm_config,
sizeof(vm_config));
if (r < 0)
goto out_obj_put_conn;
}
r = conn_cap;
out_obj_put_conn:
obj_put(conn);
out_obj_put_thread:
obj_put(server);
out_fail:
return r;
}