void input(envid_t ns_envid) { binaryname = "ns_input"; // LAB 6: Your code here: // - read a packet from the device driver // - send it to the network server // Hint: When you IPC a page to the network server, it will be // reading from it for a while, so don't immediately receive // another packet in to the same physical page. int i, r; int32_t length; struct jif_pkt *cpkt = pkt; for(i = 0; i < 10; i++) if ((r = sys_page_alloc(0, (void*)((uintptr_t)pkt + i * PGSIZE), PTE_P | PTE_U | PTE_W)) < 0) panic("sys_page_alloc: %e", r); i = 0; while(1) { while((length = sys_netpacket_recv((void*)((uintptr_t)cpkt + sizeof(cpkt->jp_len)), PGSIZE - sizeof(cpkt->jp_len))) < 0) { // cprintf("len: %d\n", length); sys_yield(); } cpkt->jp_len = length; ipc_send(ns_envid, NSREQ_INPUT, cpkt, PTE_P | PTE_U); i = (i + 1) % 10; cpkt = (struct jif_pkt*)((uintptr_t)pkt + i * PGSIZE); sys_yield(); } }
static void announce(void) { // We need to pre-announce our IP so we don't have to deal // with ARP requests. Ideally, we would use gratuitous ARP // for this, but QEMU's ARP implementation is dumb and only // listens for very specific ARP requests, such as requests // for the gateway IP. uint8_t mac[6] = {0x52, 0x54, 0x00, 0x12, 0x34, 0x56}; uint32_t myip = inet_addr(IP); uint32_t gwip = inet_addr(DEFAULT); int r; if ((r = sys_page_alloc(0, pkt, PTE_P|PTE_U|PTE_W)) < 0) panic("sys_page_map: %e", r); struct etharp_hdr *arp = (struct etharp_hdr*)pkt->jp_data; pkt->jp_len = sizeof(*arp); memset(arp->ethhdr.dest.addr, 0xff, ETHARP_HWADDR_LEN); memcpy(arp->ethhdr.src.addr, mac, ETHARP_HWADDR_LEN); arp->ethhdr.type = htons(ETHTYPE_ARP); arp->hwtype = htons(1); // Ethernet arp->proto = htons(ETHTYPE_IP); arp->_hwlen_protolen = htons((ETHARP_HWADDR_LEN << 8) | 4); arp->opcode = htons(ARP_REQUEST); memcpy(arp->shwaddr.addr, mac, ETHARP_HWADDR_LEN); memcpy(arp->sipaddr.addrw, &myip, 4); memset(arp->dhwaddr.addr, 0x00, ETHARP_HWADDR_LEN); memcpy(arp->dipaddr.addrw, &gwip, 4); ipc_send(output_envid, NSREQ_OUTPUT, pkt, PTE_P|PTE_W|PTE_U); sys_page_unmap(0, pkt); }
// // Custom page fault handler - if faulting page is copy-on-write, // map in our own private writable copy. // static void pgfault(struct UTrapframe *utf) { void *addr = (void *) utf->utf_fault_va; uint32_t err = utf->utf_err; int r; // Check that the faulting access was (1) a write, and (2) to a // copy-on-write page. If not, panic. // Hint: // Use the read-only page table mappings at uvpt // (see <inc/memlayout.h>). // LAB 4: Your code here. if ((err & FEC_WR) == 0 || (vpd[VPD(addr)] & PTE_P) == 0 || (vpt[VPN(addr)] & PTE_COW) == 0) panic ("pgfault: not a write or attempting to access a non-COW page"); // Allocate a new page, map it at a temporary location (PFTEMP), // copy the data from the old page to the new page, then move the new // page to the old page's address. // Hint: // You should make three system calls. // No need to explicitly delete the old page's mapping. // LAB 4: Your code here. if ((r = sys_page_alloc (0, (void *)PFTEMP, PTE_U|PTE_P|PTE_W)) < 0) panic ("pgfault: page allocation failed : %e", r); addr = ROUNDDOWN (addr, PGSIZE); memmove (PFTEMP, addr, PGSIZE); if ((r = sys_page_map (0, PFTEMP, 0, addr, PTE_U|PTE_P|PTE_W)) < 0) panic ("pgfault: page mapping failed : %e", r); //panic("pgfault not implemented"); }
// // User-level fork with copy-on-write. // Set up our page fault handler appropriately. // Create a child. // Copy our address space and page fault handler setup to the child. // Then mark the child as runnable and return. // // Returns: child's envid to the parent, 0 to the child, < 0 on error. // It is also OK to panic on error. // // Hint: // Use uvpd, uvpt, and duppage. // Remember to fix "thisenv" in the child process. // Neither user exception stack should ever be marked copy-on-write, // so you must allocate a new page for the child's user exception stack. // envid_t fork(void) { // LAB 4: Your code here. set_pgfault_handler (pgfault); envid_t envid; uint32_t addr; int r; envid = sys_exofork(); if (envid < 0) panic("sys_exofork: %e", envid); // We’re the child if (envid == 0) { env = &envs[ENVX(sys_getenvid())]; return 0; } // We’re the parent. for (addr = UTEXT; addr < UXSTACKTOP - PGSIZE; addr += PGSIZE) { if ((vpd[VPD(addr)] & PTE_P) > 0 && (vpt[VPN(addr)] & PTE_P) > 0 && (vpt[ VPN(addr)] & PTE_U) > 0) duppage (envid, VPN(addr)); } if ((r = sys_page_alloc (envid, (void *)(UXSTACKTOP - PGSIZE), PTE_U|PTE_W|PTE_P)) < 0) panic ("fork: page allocation failed : %e", r); extern void _pgfault_upcall (void); sys_env_set_pgfault_upcall (envid, _pgfault_upcall); // Start the child environment running if ((r = sys_env_set_status(envid, ENV_RUNNABLE)) < 0) panic("fork: set child env status failed : %e", r); return envid; //panic("fork not implemented"); }
void umain(int argc, char **argv) { envid_t who; if ((who = fork()) == 0) { // Child ipc_recv(&who, TEMP_ADDR_CHILD, 0); cprintf("%x got message: %s\n", who, TEMP_ADDR_CHILD); if (strncmp(TEMP_ADDR_CHILD, str1, strlen(str1)) == 0) cprintf("child received correct message\n"); memcpy(TEMP_ADDR_CHILD, str2, strlen(str2) + 1); ipc_send(who, 0, TEMP_ADDR_CHILD, PTE_P | PTE_W | PTE_U); return; } // Parent sys_page_alloc(thisenv->env_id, TEMP_ADDR, PTE_P | PTE_W | PTE_U); memcpy(TEMP_ADDR, str1, strlen(str1) + 1); ipc_send(who, 0, TEMP_ADDR, PTE_P | PTE_W | PTE_U); ipc_recv(&who, TEMP_ADDR, 0); cprintf("%x got message: %s\n", who, TEMP_ADDR); if (strncmp(TEMP_ADDR, str2, strlen(str2)) == 0) cprintf("parent received correct message\n"); return; }
// // Set the page fault handler function. // If there isn't one yet, _pgfault_handler will be 0. // The first time we register a handler, we need to // allocate an exception stack (one page of memory with its top // at UXSTACKTOP), and tell the kernel to call the assembly-language // _pgfault_upcall routine when a page fault occurs. // void set_pgfault_handler(void (*handler)(struct UTrapframe *utf)) { int r; int re; envid_t envid = sys_getenvid(); if (_pgfault_handler == 0) { // First time through! // LAB 4: Your code here. sys_env_set_pgfault_upcall(envid, _pgfault_upcall); re = sys_page_alloc(envid, (void *)(UXSTACKTOP - PGSIZE),PTE_U | PTE_P |PTE_W); if( re < 0){ cprintf("process %x,sys_page_alloc fail\n",envid); sys_env_destroy(envid); } //panic("set_pgfault_handler not implemented"); } // Save handler pointer for assembly to call. _pgfault_handler = handler; //cprintf("_pgfault_handler = %x\n",(uint32_t)_pgfault_handler); }
// Dispatches to the correct kernel function, passing the arguments. int64_t syscall(uint64_t syscallno, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) { // Call the function corresponding to the 'syscallno' parameter. // Return any appropriate return value. // LAB 3: Your code here. // panic("syscall not implemented"); switch (syscallno) { case SYS_cputs: sys_cputs((const char *)a1, (size_t)a2); return 0; case SYS_cgetc: return sys_cgetc(); case SYS_getenvid: return sys_getenvid(); case SYS_env_destroy: return sys_env_destroy(a1); case SYS_yield: sys_yield(); return 0; case SYS_page_alloc: return sys_page_alloc((envid_t)a1, (void *)a2,(int)a3); case SYS_page_map: return sys_page_map((envid_t)a1, (void *)a2, (envid_t)a3, (void *)a4, (int)a5); case SYS_page_unmap: return sys_page_unmap((envid_t)a1, (void *)a2); case SYS_exofork: return sys_exofork(); case SYS_env_set_status: return sys_env_set_status((envid_t)a1, a2); case SYS_env_set_pgfault_upcall: return sys_env_set_pgfault_upcall((envid_t)a1,(void *)a2); case SYS_ipc_try_send: return sys_ipc_try_send((envid_t)a1, (uint32_t)a2, (void *)a3, a4); case SYS_ipc_recv: return sys_ipc_recv((void *)a1); case SYS_env_set_trapframe: return sys_env_set_trapframe((envid_t)a1, (struct Trapframe *)a2); case SYS_time_msec: return sys_time_msec(); case SYS_packet_transmit: return sys_packet_transmit((char*)a1,(size_t)a2); case SYS_packet_receive: return sys_packet_receive((char *)a1); //lab 7 code from here case SYS_insmod: return sys_insmod((char *)a1, (char *)a2,(char *)a3); case SYS_rmmod: return sys_rmmod((char *)a1); case SYS_lsmod: return sys_lsmod(); case SYS_depmod: return sys_depmod((char *)a1); //lab7 code ends here default: return -E_NO_SYS; } }
/* * low_level_output(): * * Should do the actual transmission of the packet. The packet is * contained in the pbuf that is passed to the function. This pbuf * might be chained. * */ static err_t low_level_output(struct netif *netif, struct pbuf *p) { int r = sys_page_alloc(0, (void *)PKTMAP, PTE_U|PTE_W|PTE_P); if (r < 0) panic("jif: could not allocate page of memory"); struct jif_pkt *pkt = (struct jif_pkt *)PKTMAP; struct jif *jif; jif = netif->state; char *txbuf = pkt->jp_data; int txsize = 0; struct pbuf *q; for (q = p; q != NULL; q = q->next) { /* Send the data from the pbuf to the interface, one pbuf at a time. The size of the data in each pbuf is kept in the ->len variable. */ if (txsize + q->len > 2000) panic("oversized packet, fragment %d txsize %d\n", q->len, txsize); memcpy(&txbuf[txsize], q->payload, q->len); txsize += q->len; } pkt->jp_len = txsize; ipc_send(jif->envid, NSREQ_OUTPUT, (void *)pkt, PTE_P|PTE_W|PTE_U); sys_page_unmap(0, (void *)pkt); return ERR_OK; }
void fs_test(void) { struct File *f; int r; char *blk; uint32_t *bits; // back up bitmap if ((r = sys_page_alloc(0, (void*) PGSIZE, PTE_P|PTE_U|PTE_W)) < 0) panic("sys_page_alloc: %e", r); bits = (uint32_t*) PGSIZE; memmove(bits, bitmap, PGSIZE); // allocate block if ((r = alloc_block()) < 0) panic("alloc_block: %e", r); // check that block was free assert(bits[r/32] & (1 << (r%32))); // and is not free any more assert(!(bitmap[r/32] & (1 << (r%32)))); cprintf("alloc_block is good\n"); if ((r = file_open("/not-found", &f)) < 0 && r != -E_NOT_FOUND) panic("file_open /not-found: %e", r); else if (r == 0) panic("file_open /not-found succeeded!"); if ((r = file_open("/newmotd", &f)) < 0) panic("file_open /newmotd: %e", r); cprintf("file_open is good\n"); if ((r = file_get_block(f, 0, &blk)) < 0) panic("file_get_block: %e", r); if (strcmp(blk, msg) != 0) panic("file_get_block returned wrong data"); cprintf("file_get_block is good\n"); *(volatile char*)blk = *(volatile char*)blk; assert((vpt[PPN(blk)] & PTE_D)); file_flush(f); assert(!(vpt[PPN(blk)] & PTE_D)); cprintf("file_flush is good\n"); if ((r = file_set_size(f, 0)) < 0) panic("file_set_size: %e", r); assert(f->f_direct[0] == 0); assert(!(vpt[PPN(f)] & PTE_D)); cprintf("file_truncate is good\n"); if ((r = file_set_size(f, strlen(msg))) < 0) panic("file_set_size 2: %e", r); assert(!(vpt[PPN(f)] & PTE_D)); if ((r = file_get_block(f, 0, &blk)) < 0) panic("file_get_block 2: %e", r); strcpy(blk, msg); assert((vpt[PPN(blk)] & PTE_D)); file_flush(f); assert(!(vpt[PPN(blk)] & PTE_D)); assert(!(vpt[PPN(f)] & PTE_D)); cprintf("file rewrite is good\n"); }
// // User-level fork with copy-on-write. // Set up our page fault handler appropriately. // Create a child. // Copy our address space and page fault handler setup to the child. // Then mark the child as runnable and return. // // Returns: child's envid to the parent, 0 to the child, < 0 on error. // It is also OK to panic on error. // // Hint: // Use uvpd, uvpt, and duppage. // Remember to fix "thisenv" in the child process. // Neither user exception stack should ever be marked copy-on-write, // so you must allocate a new page for the child's user exception stack. // envid_t fork(void) { extern void _pgfault_upcall(); // LAB 4: Your code here. set_pgfault_handler(pgfault); envid_t envid = sys_exofork(); if (envid == 0) { thisenv = &envs[ENVX(sys_getenvid())]; return 0; } if (envid < 0) { panic("sys_exofork failed: %e", envid); } uint32_t addr; for (addr = 0; addr < USTACKTOP; addr += PGSIZE) { if ((uvpd[PDX(addr)] & PTE_P) && (uvpt[PGNUM(addr)] & PTE_P) && (uvpt[PGNUM(addr)] & PTE_U)) { duppage(envid, PGNUM(addr)); } } int alloc_err = sys_page_alloc(envid, (void *)(UXSTACKTOP-PGSIZE), PTE_U|PTE_W|PTE_P); if (alloc_err) { panic("sys_page_alloc failed with error: %e", alloc_err); } sys_env_set_pgfault_upcall(envid, _pgfault_upcall); int set_status_err = sys_env_set_status(envid, ENV_RUNNABLE); if (set_status_err) { panic("sys_env_set_status"); } return envid; }
void umain(void) { sys_page_alloc(0, (void*) (UXSTACKTOP - PGSIZE), PTE_P|PTE_U|PTE_W); sys_env_set_pgfault_upcall(0, (void*) 0xDeadBeef); *(int*)0 = 0; }
// // User-level fork with copy-on-write. // Set up our page fault handler appropriately. // Create a child. // Copy our address space and page fault handler setup to the child. // Then mark the child as runnable and return. // // Returns: child's envid to the parent, 0 to the child, < 0 on error. // It is also OK to panic on error. // // Hint: // Use vpd, vpt, and duppage. // Remember to fix "thisenv" in the child process. // Neither user exception stack should ever be marked copy-on-write, // so you must allocate a new page for the child's user exception stack. // envid_t fork(void) { // LAB 4: Your code here. //panic("fork not implemented"); extern void _pgfault_upcall(void); set_pgfault_handler(pgfault); envid_t id = sys_exofork(); int r; if (id < 0) panic("exofork: child"); if (id == 0) { thisenv = envs + ENVX(sys_getenvid()); return 0; } // cprintf("fork id: %x",id); if ((r=sys_page_alloc(id, (void*)(UXSTACKTOP-PGSIZE), PTE_U | PTE_W | PTE_P))<0) return r; int i; for (i=0;i<UTOP-PGSIZE;i+=PGSIZE) if ((vpd[PDX(i)] & PTE_P) && (vpt[PGNUM(i)] & PTE_P)) { // cprintf("i:%x ",i); if ((r=duppage(id,PGNUM(i)))<0) return r; } if ((r=sys_env_set_pgfault_upcall(id,(void*)_pgfault_upcall))<0) return r; if ((r=sys_env_set_status(id, ENV_RUNNABLE))<0) return r; return id; }
void input(envid_t ns_envid) { binaryname = "ns_input"; // LAB 6: Your code here: // - read a packet from the device driver // - send it to the network server // Hint: When you IPC a page to the network server, it will be // reading from it for a while, so don't immediately receive // another packet in to the same physical page. uint8_t data[2048]; int length, r; for (;;) { while ((length = sys_pkt_receive(data)) < 0) sys_yield(); while (sys_page_alloc(0, &nsipcbuf, PTE_P | PTE_W | PTE_U) < 0) sys_yield(); nsipcbuf.pkt.jp_len = length; memmove(nsipcbuf.pkt.jp_data, data, length); while (sys_ipc_try_send(ns_envid, NSREQ_INPUT, &nsipcbuf, PTE_P | PTE_W | PTE_U) < 0) sys_yield(); } }
void input(envid_t ns_envid) { binaryname = "ns_input"; // LAB 6: Your code here: // - read a packet from the device driver // - send it to the network server // Hint: When you IPC a page to the network server, it will be // reading from it for a while, so don't immediately receive // another packet in to the same physical page. int RPKT_SIZE = 2048; int lengthOfPacket = RPKT_SIZE - 1; int r = 0; char buffer[RPKT_SIZE]; while(1){ while((r = sys_receive(buffer,&lengthOfPacket))<0){ sys_yield(); } while((r = sys_page_alloc(0, &nsipcbuf, PTE_P|PTE_U|PTE_W))<0); nsipcbuf.pkt.jp_len = lengthOfPacket; memmove(nsipcbuf.pkt.jp_data, buffer, lengthOfPacket); while((r = sys_ipc_try_send(ns_envid, NSREQ_INPUT, &nsipcbuf, PTE_P|PTE_U|PTE_W)) < 0); } }
// // Set the page fault handler function. // If there isn't one yet, _pgfault_handler will be 0. // The first time we register a handler, we need to // allocate an exception stack (one page of memory with its top // at UXSTACKTOP), and tell the kernel to call the assembly-language // _pgfault_upcall routine when a page fault occurs. // void set_pgfault_handler(void (*handler)(struct UTrapframe *utf)) { int r; if (_pgfault_handler == 0) { r = sys_page_alloc(sys_getenvid(), (void*)(UXSTACKTOP - PGSIZE), PTE_W | PTE_U | PTE_P); if(r) { panic("pgfault.c:40: Error %e when allocating User Exception Stack\n", r); } } // Save handler pointer for assembly to call. _pgfault_handler = handler; /* * We register with the kernel the assembly routine to return to execution. */ r = sys_env_set_pgfault_upcall(sys_getenvid(), _pgfault_upcall); if(r) { panic("pgfault.c:55: Error %e when setting the pgfault upcall\n", r); } }
// Allocate a page to hold the disk block int map_block(uint32_t blockno) { if (block_is_mapped(blockno)) return 0; return sys_page_alloc(0, diskaddr(blockno), PTE_U|PTE_P|PTE_W); }
void umain(int argc, char **argv) { int r; if (argc != 0) childofspawn(); if ((r = sys_page_alloc(0, VA, PTE_P|PTE_W|PTE_U|PTE_SHARE)) < 0) panic("sys_page_alloc: %e", r); // check fork if ((r = fork()) < 0) panic("fork: %e", r); if (r == 0) { strcpy(VA, msg); exit(); } wait(r); cprintf("fork handles PTE_SHARE %s\n", strcmp(VA, msg) == 0 ? "right" : "wrong"); // check spawn if ((r = spawnl("/testptelibrary", "testptelibrary", "arg", 0)) < 0) panic("spawn: %e", r); wait(r); cprintf("spawn handles PTE_SHARE %s\n", strcmp(VA, msg2) == 0 ? "right" : "wrong"); }
// Dispatches to the correct kernel function, passing the arguments. int32_t syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5) { // Call the function corresponding to the 'syscallno' parameter. // Return any appropriate return value. // LAB 3: Your code here. curenv->env_syscalls++; switch(syscallno){ case SYS_cputs: sys_cputs((char *)a1, (size_t)a2);break; case SYS_cgetc: sys_cgetc();break; case SYS_getenvid: return sys_getenvid(); case SYS_env_destroy: return sys_env_destroy((envid_t)a1); case SYS_dump_env: sys_dump_env();break; case SYS_page_alloc: return sys_page_alloc((envid_t)a1, (void *)a2, (int)a3); case SYS_page_map: { return sys_page_map((envid_t)a1, (void *)a2, (envid_t)a3, (void *)a4, (int)a5); } case SYS_page_unmap: return sys_page_unmap((envid_t)a1, (void *)a2); case SYS_exofork: return sys_exofork(); case SYS_env_set_status: return sys_env_set_status((envid_t)a1,(int)a2); case SYS_env_set_trapframe: return sys_env_set_trapframe((envid_t)a1, (struct Trapframe *)a2); case SYS_env_set_pgfault_upcall: return sys_env_set_pgfault_upcall((envid_t)a1, (void *)a2); case SYS_yield: sys_yield();break;//new add syscall for lab4; case SYS_ipc_try_send: return sys_ipc_try_send((envid_t)a1, (uint32_t)a2, (void *)a3, (unsigned)a4); case SYS_ipc_recv: return sys_ipc_recv((void *)a1); case SYS_ide_read: sys_ide_read((uint32_t)a1, (void *)a2, (size_t)a3); break; case SYS_ide_write: sys_ide_write((uint32_t)a1, (void *)a2, (size_t)a3); break; case SYS_time_msec: return sys_time_msec(); case NSYSCALLS: break; default: return -E_INVAL; } return 0; //panic("syscall not implemented"); }
// Fault any disk block that is read or written in to memory by // loading it from disk. // Hint: Use ide_read and BLKSECTS. static void bc_pgfault(struct UTrapframe *utf) { void *addr = (void *) utf->utf_fault_va; uint64_t blockno = ((uint64_t)addr - DISKMAP) / BLKSIZE; int r; // Check that the fault was within the block cache region if (addr < (void*)DISKMAP || addr >= (void*)(DISKMAP + DISKSIZE)) panic("page fault in FS: eip %08x, va %08x, err %04x", utf->utf_rip, addr, utf->utf_err); // Sanity check the block number. if (super && blockno >= super->s_nblocks) panic("reading non-existent block %08x\n", blockno); // Allocate a page in the disk map region, read the contents // of the block from the disk into that page, and mark the // page not-dirty (since reading the data from disk will mark // the page dirty). // // LAB 5: Your code here void *dst_addr = (void *) ROUNDDOWN(addr, BLKSIZE); r = sys_page_alloc(thisenv->env_id, dst_addr, PTE_U | PTE_P | PTE_W); // Project addition --> Transparent disk decryption (called only if disk block // is encrypted). Bitmap block has been left out of encryption, bitmap block data is tightly // bound in other routines, so can't encrypt. if(blockno == 2) ide_read(blockno * BLKSECTS, dst_addr, BLKSECTS); else if (blockno == 1) /* || (blockno == 2)) */ { if(!s_encrypted) ide_read(blockno * BLKSECTS, dst_addr, BLKSECTS); else { r = transparent_disk_decrypt(blockno, dst_addr); if(r) return; } } else { r = transparent_disk_decrypt(blockno, dst_addr); if(r) return; } // panic("bc_pgfault not implemented"); // Check that the block we read was allocated. (exercise for // the reader: why do we do this *after* reading the block // in?) if (bitmap && block_is_free(blockno)) panic("reading free block %08x\n", blockno); }
// Set up the initial stack page for the new child process with envid 'child' // using the arguments array pointed to by 'argv', // which is a null-terminated array of pointers to null-terminated strings. // // On success, returns 0 and sets *init_esp // to the initial stack pointer with which the child should start. // Returns < 0 on failure. static int init_stack(envid_t child, const char **argv, uintptr_t *init_esp) { size_t string_size; int argc, i, r; char *string_store; uintptr_t *argv_store; // Count the number of arguments (argc) // and the total amount of space needed for strings (string_size). string_size = 0; for (argc = 0; argv[argc] != 0; argc++) string_size += strlen(argv[argc]) + 1; // Determine where to place the strings and the argv array. // Set up pointers into the temporary page 'UTEMP'; we'll map a page // there later, then remap that page into the child environment // at (USTACKTOP - PGSIZE). // strings is the topmost thing on the stack. string_store = (char*) UTEMP + PGSIZE - string_size; // argv is below that. There's one argument pointer per argument, plus // a null pointer. argv_store = (uintptr_t*) (ROUNDDOWN(string_store, 4) - 4 * (argc + 1)); // Make sure that argv, strings, and the 2 words that hold 'argc' // and 'argv' themselves will all fit in a single stack page. if ((void*) (argv_store - 2) < (void*) UTEMP) return -E_NO_MEM; // Allocate the single stack page at UTEMP. if ((r = sys_page_alloc(0, (void*) UTEMP, PTE_P|PTE_U|PTE_W)) < 0) return r; for (i = 0; i < argc; i++) { argv_store[i] = UTEMP2USTACK(string_store); strcpy(string_store, argv[i]); string_store += strlen(argv[i]) + 1; } argv_store[argc] = 0; assert(string_store == (char*)UTEMP + PGSIZE); argv_store[-1] = UTEMP2USTACK(argv_store); argv_store[-2] = argc; *init_esp = UTEMP2USTACK(&argv_store[-2]); // After completing the stack, map it into the child's address space // and unmap it from ours! if ((r = sys_page_map(0, UTEMP, child, (void*) (USTACKTOP - PGSIZE), PTE_P | PTE_U | PTE_W)) < 0) goto error; if ((r = sys_page_unmap(0, UTEMP)) < 0) goto error; return 0; error: sys_page_unmap(0, UTEMP); return r; }
int pipe(int pfd[2]) { int r; struct Fd *fd0, *fd1; void *va; // allocate the file descriptor table entries if ((r = fd_alloc(&fd0)) < 0 || (r = sys_page_alloc(0, fd0, PTE_P|PTE_W|PTE_U|PTE_SHARE)) < 0) goto err; if ((r = fd_alloc(&fd1)) < 0 || (r = sys_page_alloc(0, fd1, PTE_P|PTE_W|PTE_U|PTE_SHARE)) < 0) goto err1; // allocate the pipe structure as first data page in both va = fd2data(fd0); if ((r = sys_page_alloc(0, va, PTE_P|PTE_W|PTE_U|PTE_SHARE)) < 0) goto err2; if ((r = sys_page_map(0, va, 0, fd2data(fd1), PTE_P|PTE_W|PTE_U|PTE_SHARE)) < 0) goto err3; // set up fd structures fd0->fd_dev_id = devpipe.dev_id; fd0->fd_omode = O_RDONLY; fd1->fd_dev_id = devpipe.dev_id; fd1->fd_omode = O_WRONLY; if (debug) cprintf("[%08x] pipecreate %08x\n", env->env_id, vpt[VPN(va)]); pfd[0] = fd2num(fd0); pfd[1] = fd2num(fd1); return 0; err3: sys_page_unmap(0, va); err2: sys_page_unmap(0, fd1); err1: sys_page_unmap(0, fd0); err: return r; }
// // Custom page fault handler - if faulting page is copy-on-write, // map in our own private writable copy. // static void pgfault(struct UTrapframe *utf) { void *addr = (void *) utf->utf_fault_va; uint32_t err = utf->utf_err; int r; // Check that the faulting access was (1) a write, and (2) to a // copy-on-write page. If not, panic. // Hint: // Use the read-only page table mappings at vpt // (see <inc/memlayout.h>). // LAB 4: Your code here. pde_t *pde; pte_t *pte; uint32_t *va,*srcva,*dstva; pde =(pde_t*) &vpd[VPD(addr)]; if(*pde&PTE_P) { pte=(pte_t*)&vpt[VPN(addr)]; } else{ cprintf("addr=%x err=%x *pde=%x utf_eip=%x\n",(uint32_t)addr,err,*pde,utf->utf_eip); panic("page table for fault va is not exist"); } //cprintf("addr=%x err=%x *pte=%x utf_eip=%x\n",(uint32_t)addr,err,*pte,utf->utf_eip); if(!(err&FEC_WR)||!(*pte&PTE_COW)) { cprintf("envid=%x addr=%x err=%x *pte=%x utf_eip=%x\n",env->env_id,(uint32_t)addr,err,*pte,utf->utf_eip); panic("faulting access is illegle"); } // Allocate a new page, map it at a temporary location (PFTEMP), // copy the data from the old page to the new page, then move the new // page to the old page's address. // Hint: // You should make three system calls. // No need to explicitly delete the old page's mapping. // LAB 4: Your code here. //cprintf("pgfault:env_id=%x\n",env->env_id); if((r=sys_page_alloc(0,PFTEMP,PTE_W|PTE_U|PTE_P))<0) //输入id=0表示当前环境id(curenv->env_id),这个时候不能用env->env-id,子环境中env的修改会缺页 panic("alloc a page for PFTEMP failed:%e",r); //cprintf("PFTEMP=%x add=%x\n",PFTEMP,(uint32_t)addr&0xfffff000); srcva = (uint32_t*)((uint32_t)addr&0xfffff000); dstva = (uint32_t*)PFTEMP; //strncpy((char*)PFTEMP,(char*)((uint32_t)addr&0xfffff000),PGSIZE); for(;srcva<(uint32_t*)(ROUNDUP(addr,PGSIZE));srcva++)//数据拷贝要注意,用strncpy出错了,原因还得分析 { *dstva=*srcva; dstva++; } if((r=sys_page_map(0,(void*)PFTEMP,0,(void*)((uint32_t)addr&0xfffff000),PTE_W|PTE_U|PTE_P))<0) //输入id=0表示当前环境id(curenv->env_id),这个时候不能用env->env-id,子环境中env的修改会缺页 panic("page mapping failed"); //panic("pgfault not implemented"); }
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(child, (void*) (va + i), perm)) < 0) 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; 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; }
// Dispatches to the correct kernel function, passing the arguments. int32_t syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5) { // Call the function corresponding to the 'syscallno' parameter. // Return any appropriate return value. // LAB 3: Your code here. int32_t ret = -E_INVAL; switch(syscallno) { case SYS_cputs: sys_cputs((char *)a1, a2); break; case SYS_cgetc: ret = sys_cgetc(); break; case SYS_getenvid: ret = sys_getenvid(); break; case SYS_env_destroy: ret = sys_env_destroy(a1); break; case SYS_yield: sys_yield(); ret = 0; break; case SYS_map_kernel_page: ret = sys_map_kernel_page((void *)a1, (void *)a2); break; case SYS_sbrk: ret = sys_sbrk(a1); break; case SYS_exofork: ret = sys_exofork(); break; case SYS_env_set_status: ret = sys_env_set_status(a1,a2); break; case SYS_page_alloc: ret = sys_page_alloc(a1,(void*)a2,a3); break; case SYS_page_map: ret = sys_page_map(a1,(void*)a2,a3,(void*)a4,a5); break; case SYS_page_unmap: ret = sys_page_unmap(a1,(void*)a2); break; case SYS_env_set_pgfault_upcall: ret = sys_env_set_pgfault_upcall(a1,(void*)a2); break; case SYS_ipc_try_send: ret = sys_ipc_try_send(a1,a2,(void*)a3,a4); break; case SYS_ipc_recv: ret = sys_ipc_recv((void*)a1); } return ret; // panic("syscall not implemented"); }
// Dispatches to the correct kernel function, passing the arguments. int32_t syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5) { // Call the function corresponding to the 'syscallno' parameter. // Return any appropriate return value. // LAB 3: Your code here. /* lj */ int ret = 0; switch(syscallno) { case SYS_cputs: sys_cputs((const char *)a1, a2); break; case SYS_cgetc: ret = sys_cgetc(); break; case SYS_getenvid: ret = sys_getenvid(); break; case SYS_env_destroy: ret = sys_env_destroy(a1); break; case SYS_yield: sys_yield(); break; case SYS_exofork: ret = sys_exofork(); break; case SYS_env_set_status: ret = sys_env_set_status((envid_t)a1, a2); break; case SYS_page_alloc: ret = sys_page_alloc((envid_t)a1, (void *)a2, a3); break; case SYS_page_map: ret = sys_page_map((envid_t)a1, (void *)a2, (envid_t)a3, (void *)a4, a5); break; case SYS_page_unmap: ret = sys_page_unmap((envid_t)a1, (void *)a2); break; case SYS_env_set_pgfault_upcall: ret = sys_env_set_pgfault_upcall((envid_t)a1, (void *)a2); break; case SYS_ipc_try_send: ret = sys_ipc_try_send((envid_t)a1, a2, (void *)a3, a4); break; case SYS_ipc_recv: ret = sys_ipc_recv((void *)a1); break; default: ret = -E_INVAL; break; } //panic("syscall not implemented"); //cprintf("%d return to user %d\n", syscallno, ret); return ret; }
void process_main(void) { while (1) { if (rand() % ALLOC_SLOWDOWN == 0) { if (sys_fork() == 0) { break; } } else { sys_yield(); } } pid_t p = sys_getpid(); srand(p); // The heap starts on the page right after the 'end' symbol, // whose address is the first address not allocated to process code // or data. heap_top = ROUNDUP((uint8_t*) end, PAGESIZE); // The bottom of the stack is the first address on the current // stack page (this process never needs more than one stack page). stack_bottom = ROUNDDOWN((uint8_t*) read_rsp() - 1, PAGESIZE); // Allocate heap pages until (1) hit the stack (out of address space) // or (2) allocation fails (out of physical memory). while (1) { int x = rand() % (8 * ALLOC_SLOWDOWN); if (x < 8 * p) { if (heap_top == stack_bottom || sys_page_alloc(heap_top) < 0) { break; } *heap_top = p; /* check we have write access to new page */ heap_top += PAGESIZE; if (console[CPOS(24, 0)]) { /* clear "Out of physical memory" msg */ console_printf(CPOS(24, 0), 0, "\n"); } } else if (x == 8 * p) { if (sys_fork() == 0) { p = sys_getpid(); } } else if (x == 8 * p + 1) { sys_exit(); } else { sys_yield(); } } // After running out of memory while (1) { if (rand() % (2 * ALLOC_SLOWDOWN) == 0) { sys_exit(); } else { sys_yield(); } } }
// // User-level fork with copy-on-write. // Set up our page fault handler appropriately. // Create a child. // Copy our address space and page fault handler setup to the child. // Then mark the child as runnable and return. // // Returns: child's envid to the parent, 0 to the child, < 0 on error. // It is also OK to panic on error. // // Hint: // Use vpd, vpt, and duppage. // Remember to fix "env" in the child process. // Neither user exception stack should ever be marked copy-on-write, // so you must allocate a new page for the child's user exception stack. // envid_t fork(void) { // LAB 4: Your code here. //cprintf("In environment %04x\n", sys_getenvid()); set_pgfault_handler(pgfault); envid_t envid; int r; envid = sys_exofork(); if(envid < 0) panic("sys_exofork: %e", envid); else if(envid == 0) { env = &envs[ENVX(sys_getenvid())]; //cprintf("I am the child %04x %04x\n", sys_getenvid(), env->env_id); return 0; } //cprintf("In the parent process %04x\n", sys_getenvid()); if((r = sys_page_alloc(envid, (void *)(UXSTACKTOP - PGSIZE), PTE_W | PTE_U | PTE_P))<0) panic("sys_page_alloc: %e", r); if((r = sys_env_set_pgfault_upcall(envid, env->env_pgfault_upcall))<0) panic("sys_env_set_pgfault_upcall: %e", r); uint32_t va; for(va = UTEXT; va < UTOP; va += PGSIZE) if(va == UXSTACKTOP - PGSIZE) { if((r = sys_page_alloc(envid, (void *)(UXSTACKTOP - PGSIZE), PTE_P | PTE_W | PTE_U))<0) panic("sys_page_alloc: %e", r); } else if((vpd[VPN(va)/NPTENTRIES] & PTE_P) && (vpt[VPN(va)] & PTE_P)) duppage(envid, va>>PGSHIFT); //cprintf("Done with the copying in environment %04x\n", env->env_id); if((r = sys_env_set_pgfault_upcall(envid, env->env_pgfault_upcall))<0) panic("sys_env_set_pgfault_upcall: %e", r); if((r = sys_env_set_status(envid, ENV_RUNNABLE))<0) panic("sys_env_status: %e", r); // Returning the child's envid to the parent return envid; }
// Page fault handler void handler(struct UTrapframe *utf) { int r; void *addr = (void*)utf->utf_fault_va; if ((r = sys_page_alloc(0, ROUNDDOWN(addr, PGSIZE), PTE_P|PTE_U|PTE_W)) < 0) panic("allocating at %x in page fault handler: %e", addr, r); }
// // User-level fork with copy-on-write. // Set up our page fault handler appropriately. // Create a child. // Copy our address space and page fault handler setup to the child. // Then mark the child as runnable and return. // // Returns: child's envid to the parent, 0 to the child, < 0 on error. // It is also OK to panic on error. // // Hint: // Use vpd, vpt, and duppage. // Remember to fix "env" and the user exception stack in the child process. // Neither user exception stack should ever be marked copy-on-write, // so you must allocate a new page for the child's user exception stack. // envid_t fork(void) { // LAB 4: Your code here. // Assembly language pgfault entrypoint defined in lib/pgfaultentry.S. extern void _pgfault_upcall(void); envid_t chenvid; void *addr; void *addr2; unsigned pn; pte_t pte, pde; int count, r, entries; set_pgfault_handler(pgfault); if ( (chenvid = sys_exofork()) < 0) panic("fork() - no free env!"); else if (chenvid == 0){ //we are in child - ren env = &envs[ENVX(sys_getenvid())]; return 0; } //we are in parent - ren //copy mappings - ren entries = NPDENTRIES; for(addr = 0; entries--; addr+=NPTENTRIES*PGSIZE){ //walk through page directory - ren pde = vpd[PDX(addr)]; if (pde & PTE_P) { //walk through page table - ren for(addr2 = addr, count = NPTENTRIES; ((uintptr_t)addr2 < (uintptr_t)UTOP - PGSIZE) && (count); addr2+=PGSIZE, count--){ pte = vpt[VPN(addr2)]; if (pte & PTE_P){ duppage(chenvid, (uintptr_t)addr2/PGSIZE); } } if (count) break; //reached exception stack - ren } } if( (r = sys_page_alloc(chenvid, (void *) UXSTACKTOP-PGSIZE, PTE_U | PTE_P | PTE_W)) < 0) panic("fork() - could not allocate exception stack for the child!"); if( (r = sys_env_set_pgfault_upcall(chenvid, (void *) _pgfault_upcall)) < 0) panic("fork() - could not set page fault entrypoint for the child!"); if( (r = sys_env_set_status(chenvid, ENV_RUNNABLE)) < 0) panic("fork() - could not set child ENV_RUNNABLE!"); return chenvid; }
envid_t fork(void) { // LAB 4: Your code here. // seanyliu int r; int pdidx = 0; int peidx = 0; envid_t childid; set_pgfault_handler(pgfault); // create child environment childid = sys_exofork(); if (childid < 0) { panic("fork: failed to create child %d", childid); } if (childid == 0) { env = &envs[ENVX(sys_getenvid())]; return 0; } // loop through pg dir, avoid user exception stack (which is immediately below UTOP for (pdidx = 0; pdidx < PDX(UTOP); pdidx++) { // check if the pg is present if (!(vpd[pdidx] & PTE_P)) continue; // loop through pg table entries for (peidx = 0; (peidx < NPTENTRIES) && (pdidx*NPDENTRIES+peidx < (UXSTACKTOP - PGSIZE)/PGSIZE); peidx++) { if (vpt[pdidx * NPTENTRIES + peidx] & PTE_P) { if ((r = duppage(childid, pdidx * NPTENTRIES + peidx)) < 0) { panic("fork: duppage failed: %d", r); } } } } // allocate fresh page in the child for exception stack. if ((r = sys_page_alloc(childid, (void *)(UXSTACKTOP - PGSIZE), PTE_U | PTE_P | PTE_W)) < 0) { panic("fork: %d", r); } // parent sets the user page fault entrypoint for the child to look like its own. if ((r = sys_env_set_pgfault_upcall(childid, env->env_pgfault_upcall)) < 0) { panic("fork: %d", r); } // parent marks child runnable if ((r = sys_env_set_status(childid, ENV_RUNNABLE)) < 0) { panic("fork: %d", r); } return childid; //panic("fork not implemented"); }