#include #include #include #include #include #include /* 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 * 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); }