432 lines
10 KiB
C
432 lines
10 KiB
C
#include <lib/defs.h>
|
|
#include <lib/errno.h>
|
|
#include <lib/launcher.h>
|
|
#include <lib/print.h>
|
|
#include <lib/proc.h>
|
|
#include <lib/syscall.h>
|
|
|
|
/* main thread info */
|
|
struct mt_info {
|
|
u64 stack_base;
|
|
u64 stack_size;
|
|
u64 prio;
|
|
};
|
|
|
|
/* An example to launch an (libc) application process */
|
|
|
|
#define AT_NULL 0 /* end of vector */
|
|
#define AT_IGNORE 1 /* entry should be ignored */
|
|
#define AT_EXECFD 2 /* file descriptor of program */
|
|
#define AT_PHDR 3 /* program headers for program */
|
|
#define AT_PHENT 4 /* size of program header entry */
|
|
#define AT_PHNUM 5 /* number of program headers */
|
|
#define AT_PAGESZ 6 /* system page size */
|
|
#define AT_BASE 7 /* base address of interpreter */
|
|
#define AT_FLAGS 8 /* flags */
|
|
#define AT_ENTRY 9 /* entry point of program */
|
|
#define AT_NOTELF 10 /* program is not ELF */
|
|
#define AT_UID 11 /* real uid */
|
|
#define AT_EUID 12 /* effective uid */
|
|
#define AT_GID 13 /* real gid */
|
|
#define AT_EGID 14 /* effective gid */
|
|
#define AT_PLATFORM 15 /* string identifying CPU for optimizations */
|
|
#define AT_HWCAP 16 /* arch dependent hints at CPU capabilities */
|
|
#define AT_CLKTCK 17 /* frequency at which times() increments */
|
|
/* AT_* values 18 through 22 are reserved */
|
|
#define AT_SECURE 23 /* secure mode boolean */
|
|
#define AT_BASE_PLATFORM \
|
|
24 /* string identifying real platform, may \
|
|
* differ from AT_PLATFORM. */
|
|
#define AT_RANDOM 25 /* address of 16 random bytes */
|
|
#define AT_HWCAP2 26 /* extension of AT_HWCAP */
|
|
|
|
#define AT_EXECFN 31 /* filename of program */
|
|
|
|
#define NO_AFF -1
|
|
|
|
char init_env[PAGE_SIZE];
|
|
const char PLAT[] = "aarch64";
|
|
|
|
static void construct_init_env(char *env, u64 top_vaddr,
|
|
struct process_metadata *meta, char *name,
|
|
struct pmo_map_request *pmo_map_reqs,
|
|
int nr_pmo_map_reqs, int caps[], int nr_caps)
|
|
{
|
|
int i, j;
|
|
char *name_str;
|
|
char *plat_str;
|
|
u64 *buf;
|
|
|
|
int argc = 1; /* at least 1 for binary name */
|
|
|
|
/* clear init_env */
|
|
for (i = 0; i < PAGE_SIZE; ++i) {
|
|
env[i] = 0;
|
|
}
|
|
|
|
/* strings */
|
|
/* the last 64 bytes */
|
|
name_str = env + PAGE_SIZE - 64;
|
|
i = 0;
|
|
while (name[i] != '\0') {
|
|
name_str[i] = name[i];
|
|
++i;
|
|
}
|
|
// printf("name_str: %s\n", name_str);
|
|
|
|
/* the second last 64 bytes */
|
|
plat_str = env + PAGE_SIZE - 2 * 64;
|
|
i = 0;
|
|
while (PLAT[i] != '\0') {
|
|
plat_str[i] = PLAT[i];
|
|
++i;
|
|
}
|
|
// printf("plat_str: %s\n", plat_str);
|
|
|
|
buf = (u64 *) env;
|
|
/* argc */
|
|
*buf = argc;
|
|
|
|
/* argv */
|
|
*(buf + 1) = top_vaddr - 64;
|
|
/* add more argv here */
|
|
*(buf + 2) = (u64) NULL;
|
|
|
|
/* envp */
|
|
i = 3;
|
|
/*
|
|
* e.g.,
|
|
* info_page_vaddr = (u64)envp[0];
|
|
* fs_server_cap = (u64)envp[1];
|
|
*/
|
|
if (nr_pmo_map_reqs == 0) {
|
|
/* set info_page_vaddr to 0x0 */
|
|
*(buf + i) = (u64) 0;
|
|
i = i + 1;
|
|
} else {
|
|
for (j = 0; j < nr_pmo_map_reqs; ++j) {
|
|
*(buf + i + j) = (u64) pmo_map_reqs[j].addr;
|
|
}
|
|
i = i + j;
|
|
}
|
|
for (j = 0; j < nr_caps; ++j) {
|
|
/* e.g., fs_server_cap = (u64)envp[1]; */
|
|
*(buf + i + j) = (u64) caps[j];
|
|
}
|
|
i = i + j;
|
|
/* add more envp here */
|
|
*(buf + i) = (u64) NULL;
|
|
|
|
/* auxv */
|
|
i = i + 1;
|
|
*(buf + i + 0) = AT_SECURE;
|
|
*(buf + i + 1) = 0;
|
|
|
|
*(buf + i + 2) = AT_PAGESZ;
|
|
*(buf + i + 3) = PAGE_SIZE;
|
|
|
|
*(buf + i + 4) = AT_PHDR;
|
|
*(buf + i + 5) = meta->phdr_addr;
|
|
|
|
// printf("phdr_addr is 0x%lx\n", meta->phdr_addr);
|
|
|
|
*(buf + i + 6) = AT_PHENT;
|
|
*(buf + i + 7) = meta->phentsize;
|
|
|
|
*(buf + i + 8) = AT_PHNUM;
|
|
*(buf + i + 9) = meta->phnum;
|
|
|
|
*(buf + i + 10) = AT_FLAGS;
|
|
*(buf + i + 11) = meta->flags;
|
|
|
|
*(buf + i + 12) = AT_ENTRY;
|
|
*(buf + i + 13) = meta->entry;
|
|
|
|
*(buf + i + 14) = AT_UID;
|
|
*(buf + i + 15) = 1000;
|
|
|
|
*(buf + i + 16) = AT_EUID;
|
|
*(buf + i + 17) = 1000;
|
|
|
|
*(buf + i + 18) = AT_GID;
|
|
*(buf + i + 19) = 1000;
|
|
|
|
*(buf + i + 20) = AT_EGID;
|
|
*(buf + i + 21) = 1000;
|
|
|
|
*(buf + i + 22) = AT_CLKTCK;
|
|
*(buf + i + 23) = 100;
|
|
|
|
*(buf + i + 24) = AT_HWCAP;
|
|
*(buf + i + 25) = 0;
|
|
|
|
*(buf + i + 25) = AT_PLATFORM;
|
|
*(buf + i + 26) = top_vaddr - 64 * 2;
|
|
|
|
*(buf + i + 27) = AT_RANDOM;
|
|
*(buf + i + 28) = top_vaddr - 64; /* random 16 bytes */
|
|
|
|
*(buf + i + 29) = AT_NULL;
|
|
*(buf + i + 30) = 0;
|
|
|
|
/* add more auxv here */
|
|
}
|
|
|
|
#define MAX_NR_CAPS 16
|
|
/*
|
|
* Lab4
|
|
* The core function used by spawn
|
|
* user_elf: elf struct
|
|
* child_process_cap: if not NULL, set to child_process_cap that can be
|
|
* used in current process.
|
|
*
|
|
* child_main_thread_cap: if not NULL, set to child_main_thread_cap
|
|
* that can be used in current process.
|
|
*
|
|
* pmo_map_reqs, nr_pmo_map_reqs: input pmos to map in the new process
|
|
*
|
|
* caps, nr_caps: copy from farther process to child process
|
|
*
|
|
* aff: affinity
|
|
*
|
|
*/
|
|
#define LAB4_SPAWN_BLANK 0
|
|
int launch_process_with_pmos_caps(struct user_elf *user_elf,
|
|
int *child_process_cap,
|
|
int *child_main_thread_cap,
|
|
struct pmo_map_request *pmo_map_reqs,
|
|
int nr_pmo_map_reqs, int caps[], int nr_caps,
|
|
s32 aff)
|
|
{
|
|
int new_process_cap;
|
|
int main_thread_cap;
|
|
int ret;
|
|
|
|
long pc;
|
|
|
|
/* for creating pmos */
|
|
struct pmo_request pmo_requests[1];
|
|
int main_stack_cap;
|
|
u64 stack_offset;
|
|
u64 stack_top;
|
|
u64 stack_va;
|
|
u64 p_vaddr;
|
|
int i;
|
|
/* for mapping pmos */
|
|
struct pmo_map_request pmo_map_requests[1];
|
|
int transfer_caps[MAX_NR_CAPS];
|
|
// Lab4 useless code, help avoid compile warning, you can delete it as you wish
|
|
transfer_caps[0] = 0;
|
|
|
|
{
|
|
/**
|
|
* Step 1: create a new process with an empty vmspace
|
|
* You do not need to modify code in this scope
|
|
*/
|
|
new_process_cap = usys_create_process();
|
|
if (new_process_cap < 0) {
|
|
printf("%s: fail to create new_process_cap (ret: %d)\n",
|
|
__func__, new_process_cap);
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
{
|
|
/**
|
|
* Step 2:
|
|
* Map each segment in the elf binary to child process
|
|
* You do not need to modify code in this scope
|
|
*/
|
|
for (i = 0; i < 2; ++i) {
|
|
p_vaddr = user_elf->user_elf_seg[i].p_vaddr;
|
|
ret = usys_map_pmo(new_process_cap,
|
|
user_elf->user_elf_seg[i].elf_pmo,
|
|
ROUND_DOWN(p_vaddr, PAGE_SIZE),
|
|
user_elf->user_elf_seg[i].flags);
|
|
|
|
if (ret < 0) {
|
|
printf("usys_map_pmo ret %d\n", ret);
|
|
usys_exit(-1);
|
|
}
|
|
}
|
|
pc = user_elf->elf_meta.entry;
|
|
}
|
|
|
|
{
|
|
/**
|
|
* Step 3:
|
|
* create pmo for the stack of main thread stack in current
|
|
* process.
|
|
* You do not need to modify code in this scope
|
|
*/
|
|
pmo_requests[0].size = MAIN_THREAD_STACK_SIZE;
|
|
pmo_requests[0].type = PMO_DATA;
|
|
|
|
ret = usys_create_pmos((void *)pmo_requests, 1);
|
|
if (ret != 0) {
|
|
printf("%s: fail to create_pmos (ret: %d)\n", __func__,
|
|
ret);
|
|
goto fail;
|
|
}
|
|
|
|
/* get result caps */
|
|
main_stack_cap = pmo_requests[0].ret_cap;
|
|
if (main_stack_cap < 0) {
|
|
printf("%s: fail to create_pmos (ret: %d)\n", __func__,
|
|
ret);
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
{
|
|
/**
|
|
* Step A :
|
|
* Transfer the capbilities (nr_caps) of current process to the
|
|
* capbilities of child process
|
|
*/
|
|
// 将需要转移的 Caps 进行转移
|
|
if (nr_caps > 0) {
|
|
/* usys_transfer_caps is used during process creation */
|
|
ret = usys_transfer_caps(new_process_cap, caps, nr_caps,
|
|
transfer_caps);
|
|
if (ret != 0) {
|
|
printf("usys_transfer_caps ret %d\n", ret);
|
|
usys_exit(-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
/**
|
|
* Step B :
|
|
* Use the given pmo_mao_reqs to map the vmspace in the child
|
|
* process
|
|
*/
|
|
// 将参数中需要映射子进程的 PMOs 进行映射,即可实现共享内存
|
|
if (nr_pmo_map_reqs > 0) {
|
|
ret =
|
|
usys_map_pmos(new_process_cap, (void *)pmo_map_reqs,
|
|
nr_pmo_map_reqs);
|
|
if (ret != 0) {
|
|
printf("%s: fail to map_pmos (ret: %d)\n",
|
|
__func__, ret);
|
|
goto fail;
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
/**
|
|
* Step 4
|
|
* Prepare the arguments in the top page of the main thread's
|
|
* stack.
|
|
* You should calculate the correct value of stack_top and
|
|
* stack_offset
|
|
*/
|
|
|
|
/**
|
|
* Hints: refer to <defs.h>
|
|
* For stack_top, what's the virtual address of top of the main
|
|
* thread's stack?
|
|
*
|
|
* For stack_offset, when the main thread gets
|
|
* to execute the first time, what's the virtual address the sp
|
|
* register points to?
|
|
* stack_offset is the offset from main thread's stack base to
|
|
* that address.
|
|
*/
|
|
stack_top = MAIN_THREAD_STACK_BASE + MAIN_THREAD_STACK_SIZE;
|
|
stack_offset = MAIN_THREAD_STACK_SIZE - PAGE_SIZE;
|
|
|
|
/* Construct the parameters on the top page of the stack */
|
|
construct_init_env(init_env, stack_top, &user_elf->elf_meta,
|
|
user_elf->path, pmo_map_reqs,
|
|
nr_pmo_map_reqs, transfer_caps, nr_caps);
|
|
}
|
|
|
|
{
|
|
/**
|
|
* Step 5
|
|
* Update the main thread stack's pmo
|
|
* You only need to write the modified page
|
|
*/
|
|
ret = usys_write_pmo(main_stack_cap, stack_offset, init_env,
|
|
PAGE_SIZE);
|
|
if (ret != 0) {
|
|
printf("%s: fail to write_pmo (ret: %d)\n", __func__,
|
|
ret);
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
{
|
|
/**
|
|
* Step 6
|
|
* map the the main thread stack's pmo in the new process.
|
|
* Both VM_READ and VM_WRITE permission should be set.
|
|
*/
|
|
pmo_map_requests[0].pmo_cap = main_stack_cap;
|
|
pmo_map_requests[0].addr = MAIN_THREAD_STACK_BASE;
|
|
pmo_map_requests[0].perm = VM_READ | VM_WRITE;
|
|
|
|
ret =
|
|
usys_map_pmos(new_process_cap, (void *)pmo_map_requests, 1);
|
|
|
|
if (ret != 0) {
|
|
printf("%s: fail to map_pmos (ret: %d)\n", __func__,
|
|
ret);
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
{
|
|
/**
|
|
* Step 7
|
|
* create main thread in the new process.
|
|
* Please fill the stack_va!
|
|
*/
|
|
stack_va = MAIN_THREAD_STACK_BASE + stack_offset;
|
|
main_thread_cap =
|
|
usys_create_thread(new_process_cap, stack_va, pc,
|
|
(u64) NULL, MAIN_THREAD_PRIO, aff);
|
|
if (main_thread_cap < 0) {
|
|
printf("%s: fail to create thread (ret: %d)\n",
|
|
__func__, ret);
|
|
goto fail;
|
|
}
|
|
}
|
|
|
|
{
|
|
/* Step C: Output the child process & thread capabilities */
|
|
if (child_process_cap != NULL) {
|
|
*child_process_cap = new_process_cap;
|
|
}
|
|
if (child_main_thread_cap != NULL) {
|
|
*child_main_thread_cap = main_thread_cap;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
fail:
|
|
return -EINVAL;
|
|
}
|
|
|
|
int spawn(char *path, int *new_process_cap, int *new_thread_cap,
|
|
struct pmo_map_request *pmo_map_reqs, int nr_pmo_map_reqs, int caps[],
|
|
int nr_caps, int aff)
|
|
{
|
|
struct user_elf user_elf;
|
|
int ret;
|
|
|
|
ret = readelf_from_kernel_cpio(path, &user_elf);
|
|
if (ret < 0) {
|
|
printf("[Client] Cannot create server.\n");
|
|
return ret;
|
|
}
|
|
|
|
return launch_process_with_pmos_caps(&user_elf, new_process_cap,
|
|
new_thread_cap, pmo_map_reqs,
|
|
nr_pmo_map_reqs, caps, nr_caps,
|
|
aff);
|
|
}
|