// Map the page of memory at 'srcva' in srcenvid's address space // at 'dstva' in dstenvid's address space with permission 'perm'. // Perm has the same restrictions as in sys_page_alloc, except // that it also must not grant write access to a read-only // page. // // Return 0 on success, < 0 on error. Errors are: // -E_BAD_ENV if srcenvid and/or dstenvid doesn't currently exist, // or the caller doesn't have permission to change one of them. // -E_INVAL if srcva >= UTOP or srcva is not page-aligned, // or dstva >= UTOP or dstva is not page-aligned. // -E_INVAL is srcva is not mapped in srcenvid's address space. // -E_INVAL if perm is inappropriate (see sys_page_alloc). // -E_INVAL if (perm & PTE_W), but srcva is read-only in srcenvid's // address space. // -E_NO_MEM if there's no memory to allocate any necessary page tables. static int sys_page_map(envid_t srcenvid, void *srcva, envid_t dstenvid, void *dstva, int perm) { // Hint: This function is a wrapper around page_lookup() and // page_insert() from kern/pmap.c. // Again, most of the new code you write should be to check the // parameters for correctness. // Use the third argument to page_lookup() to // check the current permissions on the page. // LAB 4: Your code here. if (srcva >= (void *)UTOP || dstva >= (void *)UTOP || (perm & 0x5) != 0x5 || PGOFF(srcva) || PGOFF(dstva) || (perm & (~PTE_SYSCALL))) return -E_INVAL; struct Env *src_env, *dst_env; envid2env(srcenvid, &src_env, 1); envid2env(dstenvid, &dst_env, 1); if (!src_env || !dst_env) { return -E_BAD_ENV; } pte_t *pte; struct Page *page = page_lookup(src_env->env_pgdir, srcva, &pte); if (!page || (!(*pte & PTE_W) && (perm & PTE_W))) { return -E_INVAL; } if (page_insert(dst_env->env_pgdir, page, dstva, perm)) { return -E_NO_MEM; } return 0; // panic("sys_page_map not implemented"); }
/* Returns true if uva and kva both resolve to the same phys addr. If uva is * unmapped, it will return FALSE. This is probably what you want, since after * all uva isn't kva. */ bool uva_is_kva(struct proc *p, void *uva, void *kva) { struct page *u_page; assert(kva); /* catch bugs */ /* Check offsets first */ if (PGOFF(uva) != PGOFF(kva)) return FALSE; /* Check to see if it is the same physical page */ u_page = page_lookup(p->env_pgdir, uva, 0); if (!u_page) return FALSE; return (kva2page(kva) == u_page) ? TRUE : FALSE; }
// Map the page of memory at 'srcva' in srcenvid's address space // at 'dstva' in dstenvid's address space with permission 'perm'. // Perm has the same restrictions as in sys_page_alloc, except // that it also must not grant write access to a read-only // page. // // Return 0 on success, < 0 on error. Errors are: // -E_BAD_ENV if srcenvid and/or dstenvid doesn't currently exist, // or the caller doesn't have permission to change one of them. // -E_INVAL if srcva >= UTOP or srcva is not page-aligned, // or dstva >= UTOP or dstva is not page-aligned. // -E_INVAL is srcva is not mapped in srcenvid's address space. // -E_INVAL if perm is inappropriate (see sys_page_alloc). // -E_INVAL if (perm & PTE_W), but srcva is read-only in srcenvid's // address space. // -E_NO_MEM if there's no memory to allocate any necessary page tables. static int sys_page_map(envid_t srcenvid, void *srcva, envid_t dstenvid, void *dstva, int perm) { // Hint: This function is a wrapper around page_lookup() and // page_insert() from kern/pmap.c. // Again, most of the new code you write should be to check the // parameters for correctness. // Use the third argument to page_lookup() to // check the current permissions on the page. struct Env *srcenv, *dstenv; int r1 = envid2env(srcenvid, &srcenv, 1); int r2 = envid2env(dstenvid, &dstenv, 1); /* Proper envid check*/ if (r1 < 0 || r2 < 0) { cprintf("\n envid2env error %e, %e sys_page_map\n", r1, r2); return -E_BAD_ENV; } /*Address range check*/ if((uintptr_t)srcva >= UTOP || (uintptr_t)dstva >= UTOP || PGOFF(srcva) || PGOFF(dstva)) { cprintf("\n envid2env error %e sys_page_map\n", -E_INVAL); return -E_INVAL; } /*Correct page request check*/ struct PageInfo *map; pte_t *p_entry; map = page_lookup(srcenv->env_pml4e, srcva, &p_entry); if(!map) { cprintf("\n No page available or not mapped properly SYS_PAGE_ALLOC %e \n", -E_NO_MEM); return -E_NO_MEM; } /*Proper Permission check*/ int map_perm = PTE_P | PTE_U; if ((perm & map_perm) != map_perm || (perm & ~PTE_SYSCALL)) { cprintf("\n permission error %e sys_page_map\n", -E_INVAL); return -E_INVAL; } if((perm & PTE_W) && !(*p_entry & PTE_W)) { cprintf("\n permission error %e sys_page_map\n", -E_INVAL); return -E_INVAL; } /*Page insert check*/ if(page_insert(dstenv->env_pml4e, map, dstva, perm) < 0) { cprintf("\n No memory to allocate page SYS_PAGE_MAP %e \n", -E_NO_MEM); return -E_NO_MEM; } return 0; // LAB 4: Your code here. //panic("sys_page_map not implemented"); }
/** * boot_map_segment - setup&enable the paging mechanism * @param la linear address of this memory need to map (after x86 segment map) * @param size memory size * @param pa physical address of this memory * @param perm permission of this memory */ void boot_map_segment(pde_t * pgdir, uintptr_t la, size_t size, uintptr_t pa, uint32_t perm) { assert(PGOFF(la) == PGOFF(pa)); size_t n = ROUNDUP(size + PGOFF(la), PGSIZE) / PGSIZE; la = ROUNDDOWN(la, PGSIZE); pa = ROUNDDOWN(pa, PGSIZE); for (; n > 0; n--, la += PGSIZE, pa += PGSIZE) { pte_t *ptep = get_pte(pgdir, la, 1); assert(ptep != NULL); ptep_map(ptep, pa); ptep_set_perm(ptep, perm); } }
// Allocate a page of memory and map it at 'va' with permission // 'perm' in the address space of 'envid'. // The page's contents are set to 0. // If a page is already mapped at 'va', that page is unmapped as a // side effect. // // perm -- PTE_U | PTE_P must be set, PTE_AVAIL | PTE_W may or may not be set, // but no other bits may be set. See PTE_USER in inc/mmu.h. // // Return 0 on success, < 0 on error. Errors are: // -E_BAD_ENV if environment envid doesn't currently exist, // or the caller doesn't have permission to change envid. // -E_INVAL if va >= UTOP, or va is not page-aligned. // -E_INVAL if perm is inappropriate (see above). // -E_NO_MEM if there's no memory to allocate the new page, // or to allocate any necessary page tables. static int sys_page_alloc(envid_t envid, void *va, int perm) { // Hint: This function is a wrapper around page_alloc() and // page_insert() from kern/pmap.c. // Most of the new code you write should be to check the // parameters for correctness. // If page_insert() fails, remember to free the page you // allocated! // LAB 4: Your code here. //TODO: check if user has permission to change envid struct Page *new_page; struct Env* env; envid2env(envid,&env,1); if(env==NULL) return -E_BAD_ENV; if(va >= (void *)UTOP || PGOFF(va) != 0) return -E_INVAL; if((perm & PTE_U) == 0 || (perm & PTE_P) == 0 || (perm & ~PTE_U & ~PTE_P & ~PTE_AVAIL & ~PTE_W) != 0){ return -E_INVAL; } page_alloc(&new_page); if(new_page==NULL) return -E_NO_MEM; if(page_insert(env->env_pgdir, new_page, va, perm) < 0) { page_free(new_page); return -E_NO_MEM; } memset(page2kva(new_page), 0, PGSIZE); return 0; }
// Block until a value is ready. Record that you want to receive // using the env_ipc_recving and env_ipc_dstva fields of struct Env, // mark yourself not runnable, and then give up the CPU. // // If 'dstva' is < UTOP, then you are willing to receive a page of data. // 'dstva' is the virtual address at which the sent page should be mapped. // // This function only returns on error, but the system call will eventually // return 0 on success. // Return < 0 on error. Errors are: // -E_INVAL if dstva < UTOP but dstva is not page-aligned. static int sys_ipc_recv(void *dstva) { // LAB 4: Your code here. //panic("sys_ipc_recv not implemented"); if(curenv->env_ipc_recving) {//it is waiting for a ipc panic("curenv is waiting for a ipc already, something wrong\n"); }else { if((int)dstva == -1) curenv->env_ipc_dstva = dstva; else if((uint32_t)dstva<UTOP && PGOFF(dstva)) { cprintf("sys_ipc_recv : dstva is illegal.\n"); //panic("'"); return -E_INVAL; } curenv->env_ipc_recving = 1; curenv->env_ipc_dstva = dstva; curenv->env_status = ENV_NOT_RUNNABLE; curenv->env_tf.tf_regs.reg_eax = 0; //cprintf("begin wait recv,curenv is %08x\n",curenv->env_id); sched_yield(); } //*********************cj-code-end //panic("sys_ipc_recv not implemented"); return 0; }
// Allocate a page of memory and map it at 'va' with permission // 'perm' in the address space of 'envid'. // The page's contents are set to 0. // If a page is already mapped at 'va', that page is unmapped as a // side effect. // // perm -- PTE_U | PTE_P must be set, PTE_AVAIL | PTE_W may or may not be set, // but no other bits may be set. See PTE_SYSCALL in inc/mmu.h. // // Return 0 on success, < 0 on error. Errors are: // -E_BAD_ENV if environment envid doesn't currently exist, // or the caller doesn't have permission to change envid. // -E_INVAL if va >= UTOP, or va is not page-aligned. // -E_INVAL if perm is inappropriate (see above). // -E_NO_MEM if there's no memory to allocate the new page, // or to allocate any necessary page tables. static int sys_page_alloc(envid_t envid, void *va, int perm) { // Hint: This function is a wrapper around page_alloc() and // page_insert() from kern/pmap.c. // Most of the new code you write should be to check the // parameters for correctness. // If page_insert() fails, remember to free the page you // allocated! // LAB 4: Your code here. //panic("sys_page_alloc not implemented"); struct Env *e; struct Page *p; int r; //cprintf("[sys_page_alloc]:perm is %d\n",perm); if( va >=(void *)UTOP || (perm & 5) != 5 || PGOFF(va)!=0 || (perm & (~PTE_SYSCALL))!=0) return -E_INVAL; r = envid2env(envid, &e, 1); if(r < 0) return -E_BAD_ENV; p = page_alloc(ALLOC_ZERO); if(p == NULL) return -E_NO_MEM; r = page_insert(e->env_pgdir, p, va, perm); if(r < 0){ page_free(p); return -E_NO_MEM; } memset(page2kva(p), 0, PGSIZE); return 0; }
//boot_map_segment - setup&enable the paging mechanism // parameters // la: linear address of this memory need to map (after x86 segment map) // size: memory size // pa: physical address of this memory // perm: permission of this memory void boot_map_segment(pde_t *pgdir, uintptr_t la, size_t size, uintptr_t pa, uint32_t perm) { assert(PGOFF(la) == PGOFF(pa)); size_t n = ROUNDUP(size + PGOFF(la), PGSIZE) / PGSIZE; // cprintf("n=%08x\n",n); la = ROUNDDOWN(la, PGSIZE); pa = ROUNDDOWN(pa, PGSIZE); //cprintf("la=%08x,pa=%08x\n",la,pa); for (; n > 0; n --, la += PGSIZE, pa += PGSIZE) { pte_t *ptep = get_pte(pgdir, la, 1); assert(ptep != NULL); //if(ptep==0x714020) //cprintf("%08x,%08x\n",ptep,pa); *ptep = pa | PTE_V | perm; } }
// Try to send 'value' to the target env 'envid'. // If va != 0, then also send page currently mapped at 'va', // so that receiver gets a duplicate mapping of the same page. // // The send fails with a return value of -E_IPC_NOT_RECV if the // target has not requested IPC with sys_ipc_recv. // // Otherwise, the send succeeds, and the target's ipc fields are // updated as follows: // env_ipc_recving is set to 0 to block future sends; // env_ipc_from is set to the sending envid; // env_ipc_value is set to the 'value' parameter; // env_ipc_perm is set to 'perm' if a page was transferred, 0 otherwise. // The target environment is marked runnable again, returning 0 // from the paused ipc_recv system call. // // If the sender sends a page but the receiver isn't asking for one, // then no page mapping is transferred, but no error occurs. // The ipc doesn't happen unless no errors occur. // // Returns 0 on success where no page mapping occurs, // 1 on success where a page mapping occurs, and < 0 on error. // Errors are: // -E_BAD_ENV if environment envid doesn't currently exist. // (No need to check permissions.) // -E_IPC_NOT_RECV if envid is not currently blocked in sys_ipc_recv, // or another environment managed to send first. // -E_INVAL if srcva < UTOP but srcva is not page-aligned. // -E_INVAL if srcva < UTOP and perm is inappropriate // (see sys_page_alloc). // -E_INVAL if srcva < UTOP but srcva is not mapped in the caller's // address space. // -E_NO_MEM if there's not enough memory to map srcva in envid's // address space. static int sys_ipc_try_send(envid_t envid, uint32_t value, void *srcva, unsigned perm) { // LAB 4: Your code here. ///* struct Env *recv; if(envid2env(envid, &recv, 0)<0) { cprintf("sys_ipc_try_send : environment envid doesn't currently exist.\n"); return -E_BAD_ENV; } if(recv->env_ipc_recving == 0) { //cprintf("sys_ipc_try_send : recv env[%08x] is not prepared.\n", envid); return -E_IPC_NOT_RECV; } //cprintf("------------------------------------------------\n"); if((uint32_t)srcva < UTOP) { if(PGOFF(srcva)) { cprintf("sys_ipc_try_send : srcva is not page-aligned.\n"); return -E_INVAL; } if(!(perm & (PTE_U|PTE_P))){ cprintf("sys_ipc_try_send : perm[%08x] wrong--PTE_U|PTE_P not be set\n", perm); return -E_INVAL; }else if(perm & (0x1F8)){// cprintf("sys_ipc_try_send : perm wrong--unknown perm bit.\n"); return -E_INVAL; } //if(!page_lookup(curenv->env_pgdir, srcva, 0)) { //cprintf("sys_ipc_try_send : srcva is not mapped.\n"); //return -E_INVAL; //} int ret; if((int)recv->env_ipc_dstva!=-1) ret = sys_page_map(curenv->env_id, srcva, recv->env_id, recv->env_ipc_dstva, perm); else ;//ret = sys_page_map(curenv->env_id, srcva, recv->env_id, srcva, perm); if(ret < 0) return ret; else recv->env_ipc_perm = perm; }else recv->env_ipc_perm = 0; recv->env_ipc_recving = 0; recv->env_ipc_value = value; recv->env_ipc_from = curenv->env_id; recv->env_status = ENV_RUNNABLE; return 0; //*/ //panic("sys_ipc_try_send not implemented"); }
/* Initializes a ucq. You pass in addresses of mmaped pages for the main page * (prod_idx) and the spare page. I recommend mmaping a big chunk and breaking * it up over a bunch of ucqs, instead of doing a lot of little mmap() calls. */ void ucq_init_raw(struct ucq *ucq, uintptr_t pg1, uintptr_t pg2) { printd("[user] initializing ucq %08p for proc %d\n", ucq, getpid()); assert(!PGOFF(pg1)); assert(!PGOFF(pg2)); /* Prod and cons both start on the first page, slot 0. When they are * equal, the ucq is empty. */ atomic_set(&ucq->prod_idx, pg1); atomic_set(&ucq->cons_idx, pg1); ucq->prod_overflow = FALSE; atomic_set(&ucq->nr_extra_pgs, 0); atomic_set(&ucq->spare_pg, pg2); parlib_static_assert(sizeof(struct spin_pdr_lock) <= sizeof(ucq->u_lock)); spin_pdr_init((struct spin_pdr_lock*)(&ucq->u_lock)); ucq->ucq_ready = TRUE; }
// // Map our virtual page pn (address pn*PGSIZE) into the target envid // at the same virtual address. If the page is writable or copy-on-write, // the new mapping must be created copy-on-write, and then our mapping must be // marked copy-on-write as well. (Exercise: Why do we need to mark ours // copy-on-write again if it was already copy-on-write at the beginning of // this function? // // Returns: 0 on success, < 0 on error. // It is also OK to panic on error. // static int duppage(envid_t envid, unsigned pn) { int r; // LAB 4: Your code here. uint64_t vaddr = pn << PTXSHIFT; if(!(vpt[pn] & PTE_P)) return -E_INVAL; if (vaddr > UTOP ){ panic("Invalid Address\n"); } //LAB 7 -- add pte share... if ( PGOFF(vpt[pn]) & PTE_SHARE){ if( (r = sys_page_map (0, (void*)vaddr, envid, (void*)vaddr, (PGOFF(vpt[pn]) & PTE_USER) | PTE_SHARE)) < 0) panic("error from sys_page_map: %e\n",r); return 0; } if((vpt[pn] & PTE_W) || (vpt[pn] & PTE_COW)){ if((r = sys_page_map(0, (void*)vaddr, envid, (void*)vaddr, PTE_U | PTE_P | PTE_COW )) <0) panic("sys_page_map error : %e\n",r); if((r=sys_page_map(0,(void*)vaddr,0, (void*)vaddr, PTE_U | PTE_P | PTE_COW)) <0) panic("sys_page_map error in : %e\n",r); }else { if ((r = sys_page_map(0, (void *)vaddr, envid, (void*)vaddr, (PGOFF(vpt[pn]) & PTE_USER) | PTE_U | PTE_P )) < 0){ panic("sys page map error: %e\n",r ); } } return 0; }
static void check_pgdir(void) { assert(npage <= KMEMSIZE / PGSIZE); assert(boot_pgdir != NULL && (uint32_t)PGOFF(boot_pgdir) == 0); assert(get_page(boot_pgdir, 0x0, NULL) == NULL); struct Page *p1, *p2; p1 = alloc_page(); // cprintf("insert begin\n"); assert(page_insert(boot_pgdir, p1, 0x0, 0) == 0); pte_t *ptep; // cprintf("%08x\n",boot_pgdir); assert((ptep = get_pte(boot_pgdir, 0x0, 0)) != NULL); assert(pte2page(*ptep) == p1); assert(page_ref(p1) == 1); ptep = &((pte_t *)KADDR(PDE_ADDR(boot_pgdir[0])))[1]; assert(get_pte(boot_pgdir, PGSIZE, 0) == ptep); p2 = alloc_page(); assert(page_insert(boot_pgdir, p2, PGSIZE, PTE_TYPE_URW_SRW) == 0); assert((ptep = get_pte(boot_pgdir, PGSIZE, 0)) != NULL); assert(*ptep & PTE_TYPE_URW_SRW); //assert(*ptep & PTE_W); assert(((boot_pgdir[0] & PTE_TYPE)==PTE_TYPE_TABLE)&&(boot_pgdir[0]&PTE_V)); assert(page_ref(p2) == 1); assert(page_insert(boot_pgdir, p1, PGSIZE, 0) == 0); assert(page_ref(p1) == 2); assert(page_ref(p2) == 0); assert((ptep = get_pte(boot_pgdir, PGSIZE, 0)) != NULL); assert(pte2page(*ptep) == p1); assert((*ptep & PTE_TYPE_URW_SRW) == 0); page_remove(boot_pgdir, 0x0); assert(page_ref(p1) == 1); assert(page_ref(p2) == 0); page_remove(boot_pgdir, PGSIZE); assert(page_ref(p1) == 0); assert(page_ref(p2) == 0); // cprintf("haha\n"); assert(page_ref(pde2page(boot_pgdir[0])) == 1); free_page(pde2page(boot_pgdir[0])); boot_pgdir[0] = 0; cprintf("check_pgdir() succeeded\n"); //cprintf("haha2\n"); }
// Map the page of memory at 'srcva' in srcenvid's address space // at 'dstva' in dstenvid's address space with permission 'perm'. // Perm has the same restrictions as in sys_page_alloc, except // that it also must not grant write access to a read-only // page. // // Return 0 on success, < 0 on error. Errors are: // -E_BAD_ENV if srcenvid and/or dstenvid doesn't currently exist, // or the caller doesn't have permission to change one of them. // -E_INVAL if srcva >= UTOP or srcva is not page-aligned, // or dstva >= UTOP or dstva is not page-aligned. // -E_INVAL is srcva is not mapped in srcenvid's address space. // -E_INVAL if perm is inappropriate (see sys_page_alloc). // -E_INVAL if (perm & PTE_W), but srcva is read-only in srcenvid's // address space. // -E_NO_MEM if there's no memory to allocate any necessary page tables. static int sys_page_map(envid_t srcenvid, void *srcva, envid_t dstenvid, void *dstva, int perm) { // Hint: This function is a wrapper around page_lookup() and // page_insert() from kern/pmap.c. // Again, most of the new code you write should be to check the // parameters for correctness. // Use the third argument to page_lookup() to // check the current permissions on the page. // LAB 4: Your code here. struct Env *srcenv, *dstenv; struct Page *pp = NULL; pte_t *pte = NULL; int err; if ((err = envid2env(srcenvid, &srcenv, 1)) < 0) return err; if ((err = envid2env(dstenvid, &dstenv, 1)) < 0) return err; if ((uintptr_t)srcva >= UTOP || PGOFF(srcva) != 0 || (uintptr_t)dstva >= UTOP || PGOFF(dstva) != 0) return -E_INVAL; if (((perm & (~PTE_USER)) != 0) || (((perm & (PTE_U | PTE_P))) != (PTE_U | PTE_P))) return -E_INVAL; pp = page_lookup(srcenv->env_pgdir, srcva, &pte); if (pp == NULL) return -E_INVAL; if (((*pte & PTE_W) == 0) && (perm & PTE_W)) return -E_INVAL; if ((err = page_insert(dstenv->env_pgdir, pp, dstva, perm)) < 0) { return err; } return 0; }
// Map the page of memory at 'srcva' in srcenvid's address space // at 'dstva' in dstenvid's address space with permission 'perm'. // Perm has the same restrictions as in sys_page_alloc, except // that it also must not grant write access to a read-only // page. // // Return 0 on success, < 0 on error. Errors are: // -E_BAD_ENV if srcenvid and/or dstenvid doesn't currently exist, // or the caller doesn't have permission to change one of them. // -E_INVAL if srcva >= UTOP or srcva is not page-aligned, // or dstva >= UTOP or dstva is not page-aligned. // -E_INVAL is srcva is not mapped in srcenvid's address space. // -E_INVAL if perm is inappropriate (see sys_page_alloc). // -E_INVAL if (perm & PTE_W), but srcva is read-only in srcenvid's // address space. // -E_NO_MEM if there's no memory to allocate any necessary page tables. static int sys_page_map(envid_t srcenvid, void *srcva, envid_t dstenvid, void *dstva, int perm) { // Hint: This function is a wrapper around page_lookup() and // page_insert() from kern/pmap.c. // Again, most of the new code you write should be to check the // parameters for correctness. // Use the third argument to page_lookup() to // check the current permissions on the page. // LAB 4: Your code here. struct Env *source_env, *dest_env; struct Page* page; pte_t *ppte; int ret; envid2env(srcenvid,&source_env,1); envid2env(dstenvid,&dest_env,1); if(source_env == NULL || dest_env == NULL) return -E_BAD_ENV; if(srcva >= (void *)UTOP || PGOFF(srcva) != 0) return -E_INVAL; if(dstva >= (void *)UTOP || PGOFF(dstva) != 0) return -E_INVAL; if(((page = page_lookup(source_env->env_pgdir, srcva, &ppte)) == NULL) || (*ppte | PTE_P) == 0){ return -E_INVAL; } if((perm & PTE_U) == 0 || (perm & PTE_P) == 0 || (perm & ~PTE_U & ~PTE_P & ~PTE_AVAIL & ~PTE_W) != 0){ return -E_INVAL; } if((ret = page_insert(dest_env->env_pgdir, page, dstva, perm)) < 0){ return ret; } return 0; }
int // Only copies 1024 bytes! server and client call sys_copy_mem(envid_t env_id, void* addr, void* buf, int perm, bool frombuf) { void *pgva = (void *) ROUNDDOWN(addr, PGSIZE); if (sys_page_map(env_id, pgva, curenv->env_id, (void *) UTEMP, perm) < 0) return -E_INVAL; if (frombuf) { memmove((void *) (UTEMP + PGOFF(addr)), buf, 1024); } else { memmove(buf, (void *) (UTEMP + PGOFF(addr)), 1024); } if (sys_page_unmap(curenv->env_id, (void *) UTEMP) < 0) return -E_INVAL; return 0; }
// Try to send 'value' to the target env 'envid'. // If srcva < UTOP, then also send page currently mapped at 'srcva', // so that receiver gets a duplicate mapping of the same page. // // The send fails with a return value of -E_IPC_NOT_RECV if the // target is not blocked, waiting for an IPC. // // The send also can fail for the other reasons listed below. // // Otherwise, the send succeeds, and the target's ipc fields are // updated as follows: // env_ipc_recving is set to 0 to block future sends; // env_ipc_from is set to the sending envid; // env_ipc_value is set to the 'value' parameter; // env_ipc_perm is set to 'perm' if a page was transferred, 0 otherwise. // The target environment is marked runnable again, returning 0 // from the paused sys_ipc_recv system call. (Hint: does the // sys_ipc_recv function ever actually return?) // // If the sender wants to send a page but the receiver isn't asking for one, // then no page mapping is transferred, but no error occurs. // The ipc only happens when no errors occur. // // Returns 0 on success, < 0 on error. // Errors are: // -E_BAD_ENV if environment envid doesn't currently exist. // (No need to check permissions.) // -E_IPC_NOT_RECV if envid is not currently blocked in sys_ipc_recv, // or another environment managed to send first. // -E_INVAL if srcva < UTOP but srcva is not page-aligned. // -E_INVAL if srcva < UTOP and perm is inappropriate // (see sys_page_alloc). // -E_INVAL if srcva < UTOP but srcva is not mapped in the caller's // address space. // -E_INVAL if (perm & PTE_W), but srcva is read-only in the // current environment's address space. // -E_NO_MEM if there's not enough memory to map srcva in envid's // address space. static int sys_ipc_try_send(envid_t envid, uint32_t value, void *srcva, unsigned perm) { // LAB 4: Your code here. struct Env* dstenv; int ret; pte_t *pte; uintptr_t dstva; if((ret = envid2env(envid, &dstenv, 0)) < 0) return ret; if(dstenv->env_ipc_recving == 0) return -E_IPC_NOT_RECV; if((uintptr_t)srcva >= UTOP) return -E_INVAL; if(PGOFF((uintptr_t)srcva)) return -E_INVAL; if((uintptr_t)srcva != USTACKTOP) { if((perm & ~PTE_SYSCALL) || !(perm & PTE_P) || !(perm & PTE_U)) return -E_INVAL; if((pte = pgdir_walk(curenv->env_pgdir, srcva, 0)) == NULL) return -E_INVAL; if((perm & PTE_W) && !(*pte & PTE_W)) return -E_INVAL; dstva = (uintptr_t)dstenv->env_ipc_dstva; if(dstva != USTACKTOP) { /* if((ret = sys_page_map(0, srcva, envid, (void*)dstva, perm)) < 0) return ret;*/ /* i don't use sys_page_map. because filesystem env is neither current env nor * the child of current env. sys_page_map require envid is current env or the * child of current env; */ struct Page *page; if ((page = page_lookup(curenv->env_pgdir, srcva, NULL)) == NULL) return -E_INVAL; if (page_insert(dstenv->env_pgdir, page, (void*)dstva, perm) < 0) return -E_NO_MEM; dstenv->env_ipc_perm = perm; } else { dstenv->env_ipc_perm = 0; } } else { dstenv->env_ipc_perm = 0; } dstenv->env_ipc_from = curenv->env_id; dstenv->env_ipc_value = value; dstenv->env_tf.tf_regs.reg_eax = 0; dstenv->env_ipc_recving = 0; dstenv->env_status = ENV_RUNNABLE; return 0; //panic("sys_ipc_try_send not implemented"); }
static int sys_clear_block_access_bit(envid_t envid, void *va) { struct Env *env; pte_t *pte; envid2env(envid,&env,1); if(env == NULL) return -E_BAD_ENV; if((uintptr_t)va >= UTOP || PGOFF(va) != 0) return -E_INVAL; if(page_lookup(env->env_pgdir, va, &pte) == NULL) return -E_INVAL; *pte &= ~ PTE_A; return 0; }
/** * Check page table */ void check_pgdir(void) { assert(npage <= KMEMSIZE / PGSIZE); assert(boot_pgdir != NULL && (uint32_t) PGOFF(boot_pgdir) == 0); assert(get_page(boot_pgdir, TEST_PAGE, NULL) == NULL); struct Page *p1, *p2; p1 = alloc_page(); assert(page_insert(boot_pgdir, p1, TEST_PAGE, 0) == 0); pte_t *ptep, perm; assert((ptep = get_pte(boot_pgdir, TEST_PAGE, 0)) != NULL); assert(pa2page(*ptep) == p1); assert(page_ref(p1) == 1); ptep = &((pte_t *) KADDR(PTE_ADDR(boot_pgdir[PDX(TEST_PAGE)])))[1]; assert(get_pte(boot_pgdir, TEST_PAGE + PGSIZE, 0) == ptep); p2 = alloc_page(); ptep_unmap(&perm); ptep_set_u_read(&perm); ptep_set_u_write(&perm); assert(page_insert(boot_pgdir, p2, TEST_PAGE + PGSIZE, perm) == 0); assert((ptep = get_pte(boot_pgdir, TEST_PAGE + PGSIZE, 0)) != NULL); assert(ptep_u_read(ptep)); assert(ptep_u_write(ptep)); assert(ptep_u_read(&(boot_pgdir[PDX(TEST_PAGE)]))); assert(page_ref(p2) == 1); assert(page_insert(boot_pgdir, p1, TEST_PAGE + PGSIZE, 0) == 0); assert(page_ref(p1) == 2); assert(page_ref(p2) == 0); assert((ptep = get_pte(boot_pgdir, TEST_PAGE + PGSIZE, 0)) != NULL); assert(pa2page(*ptep) == p1); assert(!ptep_u_read(ptep)); page_remove(boot_pgdir, TEST_PAGE); assert(page_ref(p1) == 1); assert(page_ref(p2) == 0); page_remove(boot_pgdir, TEST_PAGE + PGSIZE); assert(page_ref(p1) == 0); assert(page_ref(p2) == 0); assert(page_ref(pa2page(boot_pgdir[PDX(TEST_PAGE)])) == 1); free_page(pa2page(boot_pgdir[PDX(TEST_PAGE)])); boot_pgdir[PDX(TEST_PAGE)] = 0; exit_range(boot_pgdir, TEST_PAGE, TEST_PAGE + PGSIZE); kprintf("check_pgdir() succeeded.\n"); }
// Block until a value is ready. Record that you want to receive // using the env_ipc_recving and env_ipc_dstva fields of struct Env, // mark yourself not runnable, and then give up the CPU. // // If 'dstva' is < UTOP, then you are willing to receive a page of data. // 'dstva' is the virtual address at which the sent page should be mapped. // // This function only returns on error, but the system call will eventually // return 0 on success. // Return < 0 on error. Errors are: // -E_INVAL if dstva < UTOP but dstva is not page-aligned. static int sys_ipc_recv(void *dstva) { // LAB 4: Your code here. //panic("sys_ipc_recv not implemented"); if(dstva < (void *) UTOP && PGOFF(dstva) != 0) return -E_INVAL; curenv->env_ipc_recving = 1; curenv->env_ipc_dstva = dstva; curenv->env_status = ENV_NOT_RUNNABLE; curenv->env_tf.tf_regs.reg_eax = 0; sched_yield(); return 0; }
static int map_segment(envid_t child, uintptr_t va, size_t memsz, int fd, size_t filesz, off_t fileoffset, int perm) { int i, r; void *blk; //cprintf("map_segment %x+%x\n", va, memsz); if ((i = PGOFF(va))) { va -= i; memsz += i; filesz += i; fileoffset -= i; } for (i = 0; i < memsz; i += PGSIZE) { if (i >= filesz) { // allocate a blank page if ((r = sys_page_alloc(0, UTEMP, perm)) < 0) { return r; } memset(UTEMP, 0, PGSIZE); sys_page_map(0, UTEMP, child, (void *)(va+i), perm); return r; } else { // from file if (perm & PTE_W) { // must make a copy so it can be writable if ((r = sys_page_alloc(0, UTEMP, PTE_P|PTE_U|PTE_W)) < 0) return r; if ((r = seek(fd, fileoffset + i)) < 0) return r; if ((r = read(fd, UTEMP, MIN(PGSIZE, filesz-i))) < 0) return r; memset(UTEMP+MIN(PGSIZE, filesz-i), 0, PGSIZE-MIN(PGSIZE, filesz-i)); if ((r = sys_page_map(0, UTEMP, child, (void*) (va + i), perm)) < 0) panic("spawn: sys_page_map data: %e", r); sys_page_unmap(0, UTEMP); } else { // can map buffer cache read only if ((r = read_map(fd, fileoffset + i, &blk)) < 0) return r; if ((r = sys_page_map(0, blk, child, (void*) (va + i), perm)) < 0) panic("spawn: sys_page_map text: %e", r); } } } return 0; }
// Try to send 'value' to the target env 'envid'. // If srcva < UTOP, then also send page currently mapped at 'srcva', // so that receiver gets a duplicate mapping of the same page. // // The send fails with a return value of -E_IPC_NOT_RECV if the // target is not blocked, waiting for an IPC. // // The send also can fail for the other reasons listed below. // // Otherwise, the send succeeds, and the target's ipc fields are // updated as follows: // env_ipc_recving is set to 0 to block future sends; // env_ipc_from is set to the sending envid; // env_ipc_value is set to the 'value' parameter; // env_ipc_perm is set to 'perm' if a page was transferred, 0 otherwise. // The target environment is marked runnable again, returning 0 // from the paused sys_ipc_recv system call. (Hint: does the // sys_ipc_recv function ever actually return?) // // If the sender wants to send a page but the receiver isn't asking for one, // then no page mapping is transferred, but no error occurs. // The ipc only happens when no errors occur. // // Returns 0 on success, < 0 on error. // Errors are: // -E_BAD_ENV if environment envid doesn't currently exist. // (No need to check permissions.) // -E_IPC_NOT_RECV if envid is not currently blocked in sys_ipc_recv, // or another environment managed to send first. // -E_INVAL if srcva < UTOP but srcva is not page-aligned. // -E_INVAL if srcva < UTOP and perm is inappropriate // (see sys_page_alloc). // -E_INVAL if srcva < UTOP but srcva is not mapped in the caller's // address space. // -E_INVAL if (perm & PTE_W), but srcva is read-only in the // current environment's address space. // -E_NO_MEM if there's not enough memory to map srcva in envid's // address space. static int sys_ipc_try_send(envid_t envid, uint32_t value, void *srcva, unsigned perm) { // LAB 4: Your code here. //panic("sys_ipc_try_send not implemented"); struct Env *e; // int ret; struct Page *p; //cprintf("%08x is Sending data to: %08x Value: %08x\n",curenv->env_id,envid,value); //cprintf("Calling send ipc...\n"); envid2env(envid,&e,0); if(e==NULL) return -E_BAD_ENV; //cprintf("Calling send ipc 2...\n"); if(e->env_ipc_recving == 0) return -E_IPC_NOT_RECV; //cprintf("Calling send ipc 3...\n"); if(srcva < (void *)UTOP) { if(PGOFF(srcva) != 0) return -E_INVAL; //cprintf("Calling send ipc 4...\n"); if((perm & PTE_U) == 0 || (perm & PTE_P) == 0 || (perm & ~PTE_U & ~PTE_P & ~PTE_AVAIL & ~PTE_W) != 0){ return -E_INVAL; } //cprintf("Calling send ipc 5..\n"); if((p=page_lookup(curenv->env_pgdir, srcva, NULL)) == NULL) return -E_INVAL; //if((perm & PTE_W) && !(*p & PTE_W)) // return -E_INVAL; //cprintf("Calling send ipc 6...\n"); e->env_ipc_perm = perm; if((page_insert(e->env_pgdir, p, e->env_ipc_dstva, perm)) < 0) return -E_NO_MEM; } else { e->env_ipc_perm = 0; } //cprintf("Calling send ipc 7...\n"); e->env_ipc_recving = 0; e->env_ipc_from = curenv->env_id; e->env_ipc_value = value; e->env_status = ENV_RUNNABLE; return 0; }
// Block until a value is ready. Record that you want to receive // using the env_ipc_recving and env_ipc_dstva fields of struct Env, // mark yourself not runnable, and then give up the CPU. // // If 'dstva' is < UTOP, then you are willing to receive a page of data. // 'dstva' is the virtual address at which the sent page should be mapped. // // This function only returns on error, but the system call will eventually // return 0 on success. // Return < 0 on error. Errors are: // -E_INVAL if dstva < UTOP but dstva is not page-aligned. static int sys_ipc_recv(void *dstva) { // LAB 4: Your code here. // panic("sys_ipc_recv not implemented"); if((uintptr_t)dstva >= UTOP) return -E_INVAL; if(PGOFF((uintptr_t)dstva)) return -E_INVAL; curenv->env_ipc_dstva = dstva; curenv->env_ipc_recving = 1; curenv->env_status = ENV_NOT_RUNNABLE; sched_yield(); return 0; }
// Unmap the page of memory at 'va' in the address space of 'envid'. // If no page is mapped, the function silently succeeds. // // Return 0 on success, < 0 on error. Errors are: // -E_BAD_ENV if environment envid doesn't currently exist, // or the caller doesn't have permission to change envid. // -E_INVAL if va >= UTOP, or va is not page-aligned. static int sys_page_unmap(envid_t envid, void *va) { // Hint: This function is a wrapper around page_remove(). struct Env* envnow; int r = envid2env(envid, &envnow, 1); if(r < 0) { cprintf("\n envid2env error %e sys_page_map\n", r); return r; } if ((uint64_t)va >= UTOP || PGOFF(va)) return -E_INVAL; page_remove(envnow->env_pml4e, va); return 0; // LAB 4: Your code here. //panic("sys_page_unmap not implemented"); }
int sys_env_set_thisenv(envid_t envid, void *thisenv) { void *pgva = (void *) ROUNDDOWN(thisenv, PGSIZE); if (sys_page_map(envid, pgva, curenv->env_id, (void *) UTEMP, PTE_P|PTE_U|PTE_W) < 0) return -E_INVAL; *((struct Env **)(UTEMP + PGOFF(thisenv))) = &((struct Env *)UENVS)[ENVX(envid)]; if (sys_page_unmap(curenv->env_id, (void *) UTEMP) < 0) return -E_INVAL; return 0; }
// Unmap the page of memory at 'va' in the address space of 'envid'. // If no page is mapped, the function silently succeeds. // // Return 0 on success, < 0 on error. Errors are: // -E_BAD_ENV if environment envid doesn't currently exist, // or the caller doesn't have permission to change envid. // -E_INVAL if va >= UTOP, or va is not page-aligned. static int sys_page_unmap(envid_t envid, void *va) { // Hint: This function is a wrapper around page_remove(). // LAB 4: Your code here. if (va >= (void *)UTOP || PGOFF(va)) return -E_INVAL; struct Env *env; if (envid2env(envid, &env, 1)) { return -E_BAD_ENV; } page_remove(env->env_pgdir, va); return 0; // panic("sys_page_unmap not implemented"); }
/* Given a proc and a user virtual address, gives us the KVA. Useful for * debugging. Returns 0 if the page is unmapped (page lookup fails). This * doesn't play nice with Jumbo pages. */ uintptr_t uva2kva(struct proc *p, void *uva, size_t len, int prot) { struct page *u_page; uintptr_t offset = PGOFF(uva); if (!p) return 0; if (prot & PROT_WRITE) { if (!is_user_rwaddr(uva, len)) return 0; } else { if (!is_user_raddr(uva, len)) return 0; } u_page = page_lookup(p->env_pgdir, uva, 0); if (!u_page) return 0; return (uintptr_t)page2kva(u_page) + offset; }
// Try to send 'value' to the target env 'envid'. // If srcva < UTOP, then also send page currently mapped at 'srcva', // so that receiver gets a duplicate mapping of the same page. // // The send fails with a return value of -E_IPC_NOT_RECV if the // target is not blocked, waiting for an IPC. // // The send also can fail for the other reasons listed below. // // Otherwise, the send succeeds, and the target's ipc fields are // updated as follows: // env_ipc_recving is set to 0 to block future sends; // env_ipc_from is set to the sending envid; // env_ipc_value is set to the 'value' parameter; // env_ipc_perm is set to 'perm' if a page was transferred, 0 otherwise. // The target environment is marked runnable again, returning 0 // from the paused sys_ipc_recv system call. (Hint: does the // sys_ipc_recv function ever actually return?) // // If the sender wants to send a page but the receiver isn't asking for one, // then no page mapping is transferred, but no error occurs. // The ipc only happens when no errors occur. // // Returns 0 on success, < 0 on error. // Errors are: // -E_BAD_ENV if environment envid doesn't currently exist. // (No need to check permissions.) // -E_IPC_NOT_RECV if envid is not currently blocked in sys_ipc_recv, // or another environment managed to send first. // -E_INVAL if srcva < UTOP but srcva is not page-aligned. // -E_INVAL if srcva < UTOP and perm is inappropriate // (see sys_page_alloc). // -E_INVAL if srcva < UTOP but srcva is not mapped in the caller's // address space. // -E_INVAL if (perm & PTE_W), but srcva is read-only in the // current environment's address space. // -E_NO_MEM if there's not enough memory to map srcva in envid's // address space. static int sys_ipc_try_send(envid_t envid, uint32_t value, void *srcva, unsigned perm) { struct Env *recvr; int r = envid2env(envid, &recvr, 0); if (r < 0) { cprintf("\n Bad ENV\n"); return r; } if (recvr->env_ipc_recving == 0) { return -E_IPC_NOT_RECV; } recvr->env_ipc_recving = 0; recvr->env_ipc_from = curenv->env_id; recvr->env_ipc_perm = 0; if((srcva && (srcva < (void*)UTOP)) && ((recvr->env_ipc_dstva) && (recvr->env_ipc_dstva < (void*)UTOP))){ if(PGOFF(srcva)) { cprintf("\n Not pageAligned\n"); return -E_INVAL; } int map_perm = PTE_U | PTE_P; if(((perm & map_perm) != map_perm) || (perm & ~PTE_SYSCALL)) { cprintf("\nPermission error\n"); return -E_INVAL; } pte_t* entry; struct PageInfo *map = page_lookup(curenv->env_pml4e, srcva, &entry); if(!(map) || ((perm & PTE_W) && !(*entry & PTE_W))) { cprintf("\n VA is not mapped in senders address space or Sending read only pages with write permissions not permissible\n"); return -E_INVAL; } if(page_insert(recvr->env_pml4e, map, recvr->env_ipc_dstva , perm) < 0) { cprintf("\n No memory to map the page to target env\n"); return -E_NO_MEM; } recvr->env_ipc_perm = perm; } recvr->env_ipc_value = value; recvr->env_status = ENV_RUNNABLE; return 0; // LAB 4: Your code here. // panic("sys_ipc_try_send not implemented"); }
// Unmap the page of memory at 'va' in the address space of 'envid'. // If no page is mapped, the function silently succeeds. // // Return 0 on success, < 0 on error. Errors are: // -E_BAD_ENV if environment envid doesn't currently exist, // or the caller doesn't have permission to change envid. // -E_INVAL if va >= UTOP, or va is not page-aligned. static int sys_page_unmap(envid_t envid, void *va) { // Hint: This function is a wrapper around page_remove(). // LAB 4: Your code here. struct Env *env; struct Page *pp = NULL; int err; if ((err = envid2env(envid, &env, 1)) < 0) return err; if ((uintptr_t)va >= UTOP || PGOFF(va) != 0) return -E_INVAL; page_remove(env->env_pgdir, va); return 0; }
// Allocate a page of memory and map it at 'va' with permission // 'perm' in the address space of 'envid'. // The page's contents are set to 0. // If a page is already mapped at 'va', that page is unmapped as a // side effect. // // perm -- PTE_U | PTE_P must be set, PTE_AVAIL | PTE_W may or may not be set, // but no other bits may be set. See PTE_USER in inc/mmu.h. // // Return 0 on success, < 0 on error. Errors are: // -E_BAD_ENV if environment envid doesn't currently exist, // or the caller doesn't have permission to change envid. // -E_INVAL if va >= UTOP, or va is not page-aligned. // -E_INVAL if perm is inappropriate (see above). // -E_NO_MEM if there's no memory to allocate the new page, // or to allocate any necessary page tables. static int sys_page_alloc(envid_t envid, void *va, int perm) { // Hint: This function is a wrapper around page_alloc() and // page_insert() from kern/pmap.c. // Most of the new code you write should be to check the // parameters for correctness. // If page_insert() fails, remember to free the page you // allocated! // LAB 4: Your code here. struct Env *env; struct Page *pp = NULL; int err; if ((err = envid2env(envid, &env, 1)) < 0) return err; if ((uintptr_t)va >= UTOP || PGOFF(va) != 0) return -E_INVAL; if (((perm & (~PTE_USER)) != 0) || ((perm & (PTE_U | PTE_P)) != (PTE_U | PTE_P))) return -E_INVAL; if (page_alloc(&pp) == -E_NO_MEM) return -E_NO_MEM; if (page_insert(env->env_pgdir, pp, va, perm) == -E_NO_MEM) { page_free(pp); return -E_NO_MEM; } /* memset(va, 0, PGSIZE) won't work. Kernel cannot use the other user's pgdir, * (NOT the Syscall Caller). When user uses syscall, it get into the kernel part * of its own page table. So, it is correct when accessing the virtual address * in its own page table. However, when this sys_page_alloc() allocated a page * for another env, the page is inserted into that env's page table. * Use va, the kernel will find the corresponding physical page in the current * page table which will not be the page we want*/ memset(page2kva(pp), 0, PGSIZE); return 0; }
static int init_smbios() { unsigned char *biosarea; int ofs, n; struct smbios_eps *s; unsigned char chksum; int pgofs; // Search the bios data area (0xf0000-0xffff0) for a valid SMBIOS structure. biosarea = iomap(0xF0000, 0x10000); if (!biosarea) return -EIO; for (ofs = 0x0000; ofs < 0xFFF0; ofs += 0x10) { s = (struct smbios_eps *) (biosarea + ofs); // Check _SM_ signature if (s->anchor[0] != '_' || s->anchor[1] != 'S' || s->anchor[2] != 'M' || s->anchor[3] != '_') continue; // Check structure checksum if (!s->length) continue; chksum = 0; for (n = 0; n < s->length; n++) chksum += biosarea[ofs + n]; if (chksum != 0) continue; //kprintf("smbios: SMBIOS %d.%d EPS found at 0x%08x\n", s->smbios_major, s->smbios_minor, 0xF0000 + ofs); //kprintf("smbios: table addr=0x%08x len=%d\n", s->structure_table_address, s->structure_table_length); // Make a copy of SMBIOS entry point structure eps = kmalloc(s->length); if (!eps) return -ENOMEM; memcpy(eps, s, s->length); // Map the SMBIOS structure table pgofs = PGOFF(eps->structure_table_address); smbios_table = iomap(eps->structure_table_address - pgofs, eps->structure_table_length + pgofs); if (!smbios_table) return -EIO; smbios_table += pgofs; break; } iounmap(biosarea, 0x10000); return 0; }