2021-05-23 20:13:14 +08:00

217 lines
5.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/exception.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>
/**
* A helper function to transfer all the ipc_msg's capbilities of client's
* process to server's process
*/
#define MAX_CAP_TRANSFER 8
int ipc_send_cap(struct ipc_connection *conn, ipc_msg_t * ipc_msg)
{
int i, r;
u64 cap_slot_number;
u64 cap_slots_offset;
u64 *cap_buf;
r = copy_from_user((char *)&cap_slot_number,
(char *)&ipc_msg->cap_slot_number,
sizeof(cap_slot_number));
if (r < 0)
goto out;
if (likely(cap_slot_number == 0)) {
r = 0;
goto out;
} else if (cap_slot_number >= MAX_CAP_TRANSFER) {
r = -EINVAL;
goto out;
}
r = copy_from_user((char *)&cap_slots_offset,
(char *)&ipc_msg->cap_slots_offset,
sizeof(cap_slots_offset));
if (r < 0)
goto out;
cap_buf = kmalloc(cap_slot_number * sizeof(*cap_buf));
if (!cap_buf) {
r = -ENOMEM;
goto out;
}
r = copy_from_user((char *)cap_buf, (char *)ipc_msg + cap_slots_offset,
sizeof(*cap_buf) * cap_slot_number);
if (r < 0)
goto out;
for (i = 0; i < cap_slot_number; i++) {
u64 dest_cap;
kdebug("[IPC] send cap:%d\n", cap_buf[i]);
dest_cap = cap_copy(current_process, conn->target->process,
cap_buf[i], false, 0);
if (dest_cap < 0)
goto out_free_cap;
cap_buf[i] = dest_cap;
}
r = copy_to_user((char *)ipc_msg + cap_slots_offset, (char *)cap_buf,
sizeof(*cap_buf) * cap_slot_number);
if (r < 0)
goto out_free_cap;
kfree(cap_buf);
return 0;
out_free_cap:
for (--i; i >= 0; i--)
cap_free(conn->target->process, cap_buf[i]);
kfree(cap_buf);
out:
return r;
}
/**
* Lab4
* Helper function
* Client thread calls this function and then return to server thread
* This function should never return
*
* Replace the place_holder to correct value!
*/
static u64 thread_migrate_to_server(struct ipc_connection *conn, u64 arg)
{
struct thread *target = conn->target;
conn->source = current_thread;
target->active_conn = conn;
current_thread->thread_ctx->state = TS_WAITING;
obj_put(conn);
/**
* Lab4
* This command set the sp register, read the file to find which field
* of the ipc_connection stores the stack of the server thread?
* */
arch_set_thread_stack(target, conn->server_stack_top);
/**
* Lab4
* This command set the ip register, read the file to find which field
* of the ipc_connection stores the instruction to be called when switch
* to the server?
* */
arch_set_thread_next_ip(target, conn->target->server_ipc_config->callback);
/**
* Lab4
* The argument set by sys_ipc_call;
*/
arch_set_thread_arg(target, arg);
/**
* Passing the scheduling context of the current thread to thread of
* connection
*/
target->thread_ctx->sc = current_thread->thread_ctx->sc;
/**
* Switch to the server
*/
switch_to_thread(target);
eret_to_thread(switch_context());
/* Function never return */
BUG_ON(1);
return 0;
}
/**
* Lab4
* The client thread calls sys_ipc_call to migrate to the server thread.
* When you transfer the ipc_msg (which is the virtual address in the client
* vmspace), do not forget to change the virtual address to server's vmspace.
* This function should never return!
*/
u64 sys_ipc_call(u32 conn_cap, ipc_msg_t * ipc_msg)
{
struct ipc_connection *conn = NULL;
u64 arg;
int r;
conn = obj_get(current_thread->process, conn_cap, TYPE_CONNECTION);
if (!conn) {
r = -ECAPBILITY;
goto out_fail;
}
/**
* Lab4
* Here, you need to transfer all the capabilities of client thread to
* capabilities in server thread in the ipc_msg.
*/
ipc_send_cap(conn, ipc_msg);
r = copy_to_user((char *)&ipc_msg->server_conn_cap,
(char *)&conn->server_conn_cap, sizeof(u64));
if (r < 0)
goto out_obj_put;
/**
* Lab4
* The arg is actually the 64-bit arg for ipc_dispatcher
* Then what value should the arg be?
* */
arg = (u64) ipc_msg;
arg = arg - conn->buf.client_user_addr + conn->buf.server_user_addr;
thread_migrate_to_server(conn, arg);
BUG("This function should never\n");
out_obj_put:
obj_put(conn);
out_fail:
return r;
}
/**
* Lab4
* Implement your sys_ipc_reg_call
* */
u64 sys_ipc_reg_call(u32 conn_cap, u64 arg0)
{
struct ipc_connection *conn = NULL;
int r;
conn = obj_get(current_thread->process, conn_cap, TYPE_CONNECTION);
if (!conn) {
r = -ECAPBILITY;
goto out_fail;
}
thread_migrate_to_server(conn, arg0);
BUG("This function should never\n");
out_fail:
return r;
}