/* * 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 #include #include #include #include #include int sys_create_device_pmo(u64 paddr, u64 size) { int cap, r; struct pmobject *pmo; BUG_ON(size == 0); pmo = obj_alloc(TYPE_PMO, sizeof(*pmo)); if (!pmo) { r = -ENOMEM; goto out_fail; } pmo_init(pmo, PMO_DEVICE, size, paddr); cap = cap_alloc(current_process, pmo, 0); if (cap < 0) { r = cap; goto out_free_obj; } return cap; out_free_obj: obj_free(pmo); out_fail: return r; } int sys_create_pmo(u64 size, u64 type) { int cap, r; struct pmobject *pmo; BUG_ON(size == 0); pmo = obj_alloc(TYPE_PMO, sizeof(*pmo)); if (!pmo) { r = -ENOMEM; goto out_fail; } pmo_init(pmo, type, size, 0); cap = cap_alloc(current_process, pmo, 0); if (cap < 0) { r = cap; goto out_free_obj; } return cap; out_free_obj: obj_free(pmo); out_fail: return r; } struct pmo_request { /* args */ u64 size; u64 type; /* return value */ u64 ret_cap; }; #define MAX_CNT 32 int sys_create_pmos(u64 user_buf, u64 cnt) { u64 size; struct pmo_request *requests; int i; int cap; /* in case of integer overflow */ if (cnt > MAX_CNT) { kwarn("create too many pmos for one time (max: %d)\n", MAX_CNT); return -EINVAL; } size = sizeof(*requests) * cnt; requests = (struct pmo_request *)kmalloc(size); if (requests == NULL) { kwarn("cannot allocate more memory\n"); return -EAGAIN; } copy_from_user((char *)requests, (char *)user_buf, size); for (i = 0; i < cnt; ++i) { cap = sys_create_pmo(requests[i].size, requests[i].type); requests[i].ret_cap = cap; } /* return pmo_caps */ copy_to_user((char *)user_buf, (char *)requests, size); /* free temporary buffer */ kfree(requests); return 0; } #define WRITE_PMO 0 #define READ_PMO 1 static int read_write_pmo(u64 pmo_cap, u64 offset, u64 user_buf, u64 size, u64 type) { struct pmobject *pmo; int r = 0; /* caller should have the pmo_cap */ pmo = obj_get(current_process, pmo_cap, TYPE_PMO); if (!pmo) { r = -ECAPBILITY; goto out_fail; } /* we only allow writing PMO_DATA now. */ if (pmo->type != PMO_DATA) { r = -EINVAL; goto out_obj_put; } if (offset + size < offset || offset + size > pmo->size) { r = -EINVAL; goto out_obj_put; } if (type == WRITE_PMO) r = copy_from_user((char *)phys_to_virt(pmo->start) + offset, (char *)user_buf, size); else if (type == READ_PMO) r = copy_to_user((char *)user_buf, (char *)phys_to_virt(pmo->start) + offset, size); else BUG("read write pmo invalid type\n"); out_obj_put: obj_put(pmo); out_fail: return r; } /* * A process can send a PMO (with msgs) to others. * It can write the msgs without mapping the PMO with this function. */ int sys_write_pmo(u64 pmo_cap, u64 offset, u64 user_ptr, u64 len) { return read_write_pmo(pmo_cap, offset, user_ptr, len, WRITE_PMO); } int sys_read_pmo(u64 pmo_cap, u64 offset, u64 user_ptr, u64 len) { return read_write_pmo(pmo_cap, offset, user_ptr, len, READ_PMO); } /* * A process can not only map a PMO into its private address space, * but also can map a PMO to some others (e.g., load code for others). */ int sys_map_pmo(u64 target_process_cap, u64 pmo_cap, u64 addr, u64 perm) { struct vmspace *vmspace; struct pmobject *pmo; struct process *target_process; int r; pmo = obj_get(current_process, pmo_cap, TYPE_PMO); if (!pmo) { r = -ECAPBILITY; goto out_fail; } /* map the pmo to the target process */ target_process = obj_get(current_process, target_process_cap, TYPE_PROCESS); if (!target_process) { r = -ECAPBILITY; goto out_obj_put_pmo; } vmspace = obj_get(target_process, VMSPACE_OBJ_ID, TYPE_VMSPACE); BUG_ON(vmspace == NULL); r = vmspace_map_range(vmspace, addr, pmo->size, perm, pmo); if (r != 0) { r = -EPERM; goto out_obj_put_vmspace; } /* * when a process maps a pmo to others, * this func returns the new_cap in the target process. */ if (target_process != current_process) /* if using cap_move, we need to consider remove the mappings */ r = cap_copy(current_process, target_process, pmo_cap, 0, 0); else r = 0; out_obj_put_vmspace: obj_put(vmspace); obj_put(target_process); out_obj_put_pmo: obj_put(pmo); out_fail: return r; } struct pmo_map_request { /* args */ u64 pmo_cap; u64 addr; u64 perm; /* return caps or return value */ u64 ret; }; int sys_map_pmos(u64 target_process_cap, u64 user_buf, u64 cnt) { u64 size; struct pmo_map_request *requests; int i; int ret; /* in case of integer overflow */ if (cnt > MAX_CNT) { kwarn("create too many pmos for one time (max: %d)\n", MAX_CNT); return -EINVAL; } size = sizeof(*requests) * cnt; requests = (struct pmo_map_request *)kmalloc(size); if (requests == NULL) { kwarn("cannot allocate more memory\n"); return -EAGAIN; } copy_from_user((char *)requests, (char *)user_buf, size); for (i = 0; i < cnt; ++i) { /* * if target_process is not current_process, * ret is cap on success. */ ret = sys_map_pmo(target_process_cap, requests[i].pmo_cap, requests[i].addr, requests[i].perm); requests[i].ret = ret; } copy_to_user((char *)user_buf, (char *)requests, size); kfree(requests); return 0; } int sys_unmap_pmo(u64 target_process_cap, u64 pmo_cap, u64 addr) { struct vmspace *vmspace; struct pmobject *pmo; struct process *target_process; int ret; /* caller should have the pmo_cap */ pmo = obj_get(current_process, pmo_cap, TYPE_PMO); if (!pmo) return -EPERM; /* map the pmo to the target process */ target_process = obj_get(current_process, target_process_cap, TYPE_PROCESS); if (!target_process) { ret = -EPERM; goto fail1; } vmspace = obj_get(target_process, VMSPACE_OBJ_ID, TYPE_VMSPACE); if (!vmspace) { ret = -EPERM; goto fail2; } ret = vmspace_unmap_range(vmspace, addr, pmo->size); if (ret != 0) ret = -EPERM; obj_put(vmspace); fail2: obj_put(target_process); fail1: obj_put(pmo); return ret; } /* * User process heap start: 0x600000000000 * * defined in mm/vmregion.c */ u64 sys_handle_brk(u64 addr) { struct vmspace *vmspace; struct pmobject *pmo; u64 retval; vmspace = obj_get(current_process, VMSPACE_OBJ_ID, TYPE_VMSPACE); /* * Lab3: Your code here * The sys_handle_brk syscall modifies the top address of heap to addr. * * If addr is 0, this function should initialize the heap, implemeted by: * 1. Create a new pmo with size 0 and type PMO_ANONYM. * 2. Initialize vmspace->heap_vmr using function init_heap_vmr(), which generates * the mapping between user heap's virtual address (already stored in * vmspace->user_current_heap) and the pmo you just created. * * HINT: For more details about how to create and initiailze a pmo, check function * 'load_binary' for reference. * * If addr is larger than heap, the size of vmspace->heap_vmr and the size of its * coresponding pmo should be updated. Real physical memory allocation are done in * a lazy manner using pagefault handler later at the first access time. * * If addr is smaller than heap, do nothing and return -EINVAL since we are not going * to support shink heap. For all other cases, return the virtual address of the heap * top. * */ if (vmspace->heap_vmr != NULL) { retval = vmspace->heap_vmr->start + vmspace->heap_vmr->size; } else { retval = vmspace->user_current_heap; } if (addr == 0) { // 初始化堆 if (vmspace->heap_vmr != NULL) { // 已经初始化 return -EINVAL; } // 创建堆 PMO 对象 pmo = obj_alloc(TYPE_PMO, sizeof(*pmo)); if (!pmo) { return -ENOMEM; } pmo_init(pmo, PMO_ANONYM, 0, 0); // 初始化堆映射 vmspace->heap_vmr = init_heap_vmr(vmspace, vmspace->user_current_heap, pmo); } else if (vmspace->heap_vmr != NULL && addr > retval) { // 增长堆 vmspace->heap_vmr->size = addr - vmspace->heap_vmr->start; retval = addr; } else if (vmspace->heap_vmr != NULL && addr < retval) { // 缩减堆 retval = -EINVAL; } /* * return origin heap addr on failure; * return new heap addr on success. */ obj_put(vmspace); return retval; }