int SetKernelBrk(void *addr) { int index; if(!virtual_mem_enabled) { if((long)addr>=VMEM_1_LIMIT) return -1; } else { while((long)addr>UP_TO_PAGE(mod_brk)) { mod_brk=(void *)UP_TO_PAGE(mod_brk); index=(long)mod_brk/PAGESIZE; if(index >=PAGE_TABLE_SIZE||pfn_head==-1) return -1; pt1[index].pfn = (long)pfn_head; pt1[index].uprot=0; pt1[index].kprot=PROT_READ|PROT_WRITE; pt1[index].valid=1; pfn_head=*(int *)(long)(mod_brk); } } mod_brk=addr; return 0; }
/* This function make perfect mapping from kernel virtual memory phyical memory */ void init_kernel_page_table() { kernel_page_table = (pte_t*) malloc(sizeof(pte_t) * GET_PAGE_NUMBER(VMEM_0_SIZE)); // For text segment mapping TracePrintf(0, "Text Start=%p, End=%p\n", kernel_memory.text_low, kernel_memory.data_low); write_kernel_pte(kernel_memory.text_low, kernel_memory.data_low , _VALID, PROT_READ | PROT_EXEC); // For data segment mapping TracePrintf(0, "Data Start=%p, End=%p\n", kernel_memory.data_low, kernel_memory.brk_low); write_kernel_pte(kernel_memory.data_low, kernel_memory.brk_low , _VALID, PROT_READ | PROT_WRITE); // For stack segment mapping, noted that stack space is reserved even if it is not used TracePrintf(0, "Stack Start=%p, End=%p\n", KERNEL_STACK_BASE, KERNEL_STACK_LIMIT); write_kernel_pte(KERNEL_STACK_BASE, KERNEL_STACK_LIMIT , _VALID, PROT_READ | PROT_WRITE); int i; // Add free pages between heap and stack int start_pfn = GET_PAGE_NUMBER(UP_TO_PAGE(kernel_memory.brk_high)); int end_pfn = GET_PAGE_NUMBER(DOWN_TO_PAGE(KERNEL_STACK_BASE)); for(i = start_pfn; i < end_pfn; i++) { add_tail_available_frame(i); } // Add free pages above kernel space start_pfn = GET_PAGE_NUMBER(UP_TO_PAGE(VMEM_0_LIMIT)); end_pfn = GET_PAGE_NUMBER(DOWN_TO_PAGE(PMEM_BASE)) + PAGE_SIZE; for(i = start_pfn; i < end_pfn; i++) { add_tail_available_frame(i); } }
extern int KernelBrk(void *addr, struct PCBNode *pcb) { TracePrintf(256, "Brk: addr(%d)\n", addr); //check if the addr is illegal if(addr < MEM_INVALID_SIZE) { TracePrintf(0, "Error in KernelBrk: Trying to set the brk below MEM_INVALID_SIZE.\n"); //Exit the program. } if(addr > DOWN_TO_PAGE(pcb -> stack_brk) - PAGESIZE) { TracePrintf(0, "Error in KernelBrk: Trying to set the brk inside or above the red zone.\n"); } unsigned int userTablePTE; //Only allocate for entire pages?? unsigned int gap = (UP_TO_PAGE(addr)-UP_TO_PAGE(pcb -> heap_brk)); if(gap>0) { TracePrintf(250, "Moving user brk up to address: %d (%d)\n", addr, (long)addr >> PAGESHIFT); for (userTablePTE = (UP_TO_PAGE(pcb -> heap_brk)); userTablePTE < (UP_TO_PAGE(addr)); userTablePTE += PAGESIZE) { unsigned int i = ((userTablePTE) >> PAGESHIFT) % PAGE_TABLE_LEN; UserPageTable[i].valid = 1; UserPageTable[i].uprot = PROT_NONE; UserPageTable[i].kprot = PROT_READ | PROT_WRITE; /* Need to change the pfn here */ UserPageTable[i].pfn = allocatePhysicalPage(); TracePrintf(250, "Allocate physical pages for user process: PID(%d), VPN(%d), PFN(%d).\n", pcb -> PID, i, UserPageTable[i].pfn); } }
/* * Not necessary in checkpoint 2 */ int SetKernelBrk (void *addr) { TracePrintf(0, "SetKernelBrk Start = %p, End = %p, New Addr = %p\n", kernel_memory.brk_low, kernel_memory.brk_high, addr); int page_cnt, rc; uint32 new_addr = (uint32)addr; uint32 new_page_bound = UP_TO_PAGE(new_addr); uint32 current_page_bound = UP_TO_PAGE(kernel_memory.brk_high); // Boudaries check if(new_addr > kernel_memory.stack_low) { TracePrintf(0, "Kernel Break Warning: Trying to Access Stack Addr = %p\n", new_addr); return _FAILURE; } // Check if trying to access below brk base line else if(new_addr < kernel_memory.brk_low) { TracePrintf(0, "Kernel Break Warning: Trying to Access Text Addr = %p\n", addr); return _FAILURE; } // Before the virual memory is enabled if(!ReadRegister(REG_VM_ENABLE)) { kernel_memory.brk_high = new_addr; return _SUCCESS; } TracePrintf(0, "SetKernelBrk CHECK DONE = %p, End = %p, New Addr = %p\n", kernel_memory.brk_low, kernel_memory.brk_high, addr); // Modify the brk if(new_addr > kernel_memory.brk_high) { TracePrintf(0, "SetKernelBrk ADD = %p, End = %p, New Addr = %p\n", kernel_memory.brk_low, kernel_memory.brk_high, addr); page_cnt = GET_PAGE_NUMBER(new_page_bound) - GET_PAGE_NUMBER(current_page_bound); rc = map_page_to_frame(kernel_page_table, current_page_bound, page_cnt, PROT_READ | PROT_WRITE); if(rc) { TracePrintf(0, "Kernel Break Warning: Not enough phycial memory\n"); return _FAILURE; } } else if (new_page_bound < kernel_memory.brk_high) { TracePrintf(0, "SetKernelBrk RM = %p, End = %p, New Addr = %p\n", kernel_memory.brk_low, kernel_memory.brk_high, addr); page_cnt = GET_PAGE_NUMBER(new_page_bound) - GET_PAGE_NUMBER(current_page_bound); rc = unmap_page_to_frame(kernel_page_table, new_page_bound, page_cnt); if(rc) { TracePrintf(0, "Kernel Break Warning: Not able to release pages\n"); return _FAILURE; } } TracePrintf(0, "SetKernelBrk DONE = %p, End = %p, New Addr = %p\n", kernel_memory.brk_low, kernel_memory.brk_high, addr); kernel_memory.brk_high = new_addr; return _SUCCESS; }
int SetKernelBrk(void *addr) { TracePrintf(2, "SetKernelBrk start\n"); u_int i; u_int address = (u_int) addr; u_int upperAddress = UP_TO_PAGE(address); int addressPage = Page(upperAddress); // check to make sure address is below the stack if (address < KernelDataEnd || address >= Address(INTERFACE)) { TracePrintf(0, "ERROR: SetKernelBrk Failure! Bad address!\n"); return FAILURE; } // If virtual memory has not been enabled if (0 == VM_ENABLED) { if (KernelBrk < address) KernelBrk = upperAddress; } else { if (FAILURE == disablePTE(addressPage, INTERFACE)) { TracePrintf(0, "SetKernelBrk Failure, disable pte fail!"); return FAILURE; } if (FAILURE == enablePTE(MIN_VPN, addressPage, PROT_READ | PROT_WRITE)) { TracePrintf(0, "SetKernelBrk Failure, enable pte fail!"); return FAILURE; } } TracePrintf(2, "NewBrkPage: %d, Free frames: %d\n", addressPage, FreeFrameCount); TracePrintf(2, "SetKernelBrk finished\n"); return SUCCESS; }
extern void trapMemory(ExceptionStackFrame *exceptionStackFrame) { TracePrintf(512, "trapMemory: vector(%d), code(%d), addr(%d), psr(%d), pc(%d), sp(%d), regs(%s)\n", exceptionStackFrame->vector, exceptionStackFrame->code, exceptionStackFrame->addr, exceptionStackFrame->psr, exceptionStackFrame->pc, exceptionStackFrame->sp, exceptionStackFrame->regs); if( exceptionStackFrame -> addr > current -> stack_brk ) { TracePrintf(0, "Trap Memory Error: addr: %d is large than stack_brk: %d\n", exceptionStackFrame -> addr, current -> stack_brk); //Exit; } if( exceptionStackFrame -> addr < UP_TO_PAGE(current -> heap_brk) + PAGESIZE ) { TracePrintf(0, "Trap Memory Error: addr: %d is smaller than heap_brk: %d\n", exceptionStackFrame -> addr, current -> heap_brk); //Exit; } long userTablePTE; TracePrintf(510, "Moving user stack down to address: %d (%d)\n", exceptionStackFrame -> addr, (long)exceptionStackFrame -> addr >> PAGESHIFT); for (userTablePTE = (DOWN_TO_PAGE(exceptionStackFrame -> addr)); userTablePTE < (DOWN_TO_PAGE(current -> stack_brk)); userTablePTE += PAGESIZE) { unsigned int i = ((userTablePTE) >> PAGESHIFT) % PAGE_TABLE_LEN; UserPageTable[i].valid = 1; UserPageTable[i].uprot = PROT_NONE; UserPageTable[i].kprot = PROT_READ | PROT_WRITE; /* Need to change the pfn here */ UserPageTable[i].pfn = allocatePhysicalPage(); TracePrintf(250, "Allocate physical pages for user process: PID(%d), VPN(%d), PFN(%d).\n", current -> PID, i, UserPageTable[i].pfn); } }
int Y_Brk(uint32 addr){ int page_cnt, rc; uint32 new_addr = (uint32)addr; uint32 new_page_bound = UP_TO_PAGE(new_addr); uint32 current_page_bound = UP_TO_PAGE(user_memory.brk_high); // Boudaries check if(new_addr > user_memory.stack_low - PAGESIZE) { log_err("New addr trepass the red zone below stack!"); return _FAILURE; } // Check if trying to access below brk base line else if(new_addr < user_memory.brk_low) { log_err("New addr trepass the data area!"); return _FAILURE; } // Modify the brk if(new_addr > user_memory.brk_high) { page_cnt = GET_PAGE_NUMBER(new_page_bound) - GET_PAGE_NUMBER(current_page_bound); rc = map_page_to_frame(user_page_table, current_page_bound, page_cnt, PROT_READ | PROT_WRITE); if(rc) { log_err("User Break Warning: Not enough phycial memory\n"); return _FAILURE; } log_info("Y_Brk ADD DONE = %p, End = %p, New Addr = %p", user_memory.brk_low, user_memory.brk_high, addr); } else if (new_page_bound < user_memory.brk_high) { page_cnt = GET_PAGE_NUMBER(new_page_bound) - GET_PAGE_NUMBER(current_page_bound); rc = unmap_page_to_frame(user_page_table, new_page_bound, page_cnt); if(rc) { log_err("User Break Warning: Not able to release pages\n"); return _FAILURE; } } user_memory.brk_high = new_addr; log_info("PID %d user brk done, new addr at %p", running_proc->pid, new_addr); return _SUCCESS; }
/* * Load a program into an existing address space. The program comes from * the Linux file named "name", and its arguments come from the array at * "args", which is in standard argv format. The argument "proc" points * to the process or PCB structure for the process into which the program * is to be loaded. */ int LoadProgram(char *name, char *args[], PCB *proc) //==>> Declare the argument "proc" to be a pointer to your PCB or //==>> process descriptor data structure. We assume you have a member //==>> of this structure used to hold the cpu context //==>> for the process holding the new program. { int fd; int (*entry)(); struct load_info li; int i; char *cp; char **cpp; char *cp2; int argcount; int size; int text_pg1; int data_pg1; int data_npg; int stack_npg; long segment_size; char *argbuf; /* * Open the executable file */ if ((fd = open(name, O_RDONLY)) < 0) { TracePrintf(0, "LoadProgram: can't open file '%s'\n", name); return FAILURE; } if (LoadInfo(fd, &li) != LI_NO_ERROR) { TracePrintf(0, "LoadProgram: '%s' not in Yalnix format\n", name); close(fd); return FAILURE; } if (li.entry < VMEM_1_BASE) { TracePrintf(0, "LoadProgram: '%s' not linked for Yalnix\n", name); close(fd); return FAILURE; } /* * Figure out in what region 1 page the different program sections * start and end */ text_pg1 = (li.t_vaddr - VMEM_1_BASE) >> PAGESHIFT; data_pg1 = (li.id_vaddr - VMEM_1_BASE) >> PAGESHIFT; data_npg = li.id_npg + li.ud_npg; /* * Store end of bss as UserDataEnd and as user brk */ proc->userDataEnd = (void *) UP_TO_PAGE(li.ud_end); proc->brk = proc->userDataEnd + 1; /* * Figure out how many bytes are needed to hold the arguments on * the new stack that we are building. Also count the number of * arguments, to become the argc that the new "main" gets called with. */ size = 0; for (i = 0; args[i] != NULL; i++) { TracePrintf(3, "counting arg %d = '%s'\n", i, args[i]); size += strlen(args[i]) + 1; } argcount = i; TracePrintf(2, "LoadProgram: argsize %d, argcount %d\n", size, argcount); /* * The arguments will get copied starting at "cp", and the argv * pointers to the arguments (and the argc value) will get built * starting at "cpp". The value for "cpp" is computed by subtracting * off space for the number of arguments (plus 3, for the argc value, * a NULL pointer terminating the argv pointers, and a NULL pointer * terminating the envp pointers) times the size of each, * and then rounding the value *down* to a double-word boundary. */ cp = ((char *)VMEM_1_LIMIT) - size; cpp = (char **) (((int)cp - ((argcount + 3 + POST_ARGV_NULL_SPACE) *sizeof (void *))) & ~7); /* * Compute the new stack pointer, leaving INITIAL_STACK_FRAME_SIZE bytes * reserved above the stack pointer, before the arguments. */ cp2 = (caddr_t)cpp - INITIAL_STACK_FRAME_SIZE; TracePrintf(1, "prog_size %d, text %d data %d bss %d pages\n", li.t_npg + data_npg, li.t_npg, li.id_npg, li.ud_npg); /* * Compute how many pages we need for the stack */ stack_npg = (VMEM_1_LIMIT - DOWN_TO_PAGE(cp2)) >> PAGESHIFT; TracePrintf(11, "LoadProgram: heap_size %d, stack_size %d\n", li.t_npg + data_npg, stack_npg); /* * Store stack base in pcb */ proc->stackBase = (void *) DOWN_TO_PAGE(cp2); /* * If free frames are not enough, return FAILURE */ int page_in_use = (Page(proc->brk) - MAX_PT_LEN) + (NUM_VPN - Page(proc->stackBase)); if (page_in_use > FreeFrameCount) { return FAILURE; } /* leave at least one page between heap and stack */ if (stack_npg + data_pg1 + data_npg >= MAX_PT_LEN) { close(fd); return FAILURE; } /* * This completes all the checks before we proceed to actually load * the new program. From this point on, we are committed to either * loading succesfully or killing the process. */ /* * Set the new stack pointer value in the process's exception frame. */ //==>> Here you replace your data structure proc //==>> proc->context.sp = cp2; proc->uctxt.sp = cp2; /* * Now save the arguments in a separate buffer in region 0, since * we are about to blow away all of region 1. */ cp2 = argbuf = (char *)malloc(size); //==>> You should perhaps check that malloc returned valid space if (NULL == cp2) { TracePrintf(0, "Malloc returned NULL when saving arguments in a separate buffer in region 0.\n"); close(fd); return FAILURE; } for (i = 0; args[i] != NULL; i++) { TracePrintf(3, "saving arg %d = '%s'\n", i, args[i]); strcpy(cp2, args[i]); cp2 += strlen(cp2) + 1; } /* * Set up the page tables for the process so that we can read the * program into memory. Get the right number of physical pages * allocated, and set them all to writable. */ //==>> Throw away the old region 1 virtual address space of the //==>> curent process by freeing //==>> all physical pages currently mapped to region 1, and setting all //==>> region 1 PTEs to invalid. //==>> Since the currently active address space will be overwritten //==>> by the new program, it is just as easy to free all the physical //==>> pages currently mapped to region 1 and allocate afresh all the //==>> pages the new program needs, than to keep track of //==>> how many pages the new process needs and allocate or //==>> deallocate a few pages to fit the size of memory to the requirements //==>> of the new process. disablePTE(MAX_PT_LEN, NUM_VPN); //==>> Allocate "li.t_npg" physical pages and map them starting at //==>> the "text_pg1" page in region 1 address space. //==>> These pages should be marked valid, with a protection of //==>> (PROT_READ | PROT_WRITE). enablePTE(MAX_PT_LEN + text_pg1, MAX_PT_LEN + text_pg1 + li.t_npg, PROT_READ | PROT_WRITE); //==>> Allocate "data_npg" physical pages and map them starting at //==>> the "data_pg1" in region 1 address space. //==>> These pages should be marked valid, with a protection of //==>> (PROT_READ | PROT_WRITE). enablePTE(MAX_PT_LEN + data_pg1, MAX_PT_LEN + data_pg1 + data_npg, PROT_READ | PROT_WRITE); /* * Allocate memory for the user stack too. */ //==>> Allocate "stack_npg" physical pages and map them to the top //==>> of the region 1 virtual address space. //==>> These pages should be marked valid, with a //==>> protection of (PROT_READ | PROT_WRITE). enablePTE(NUM_VPN - stack_npg, NUM_VPN, PROT_READ | PROT_WRITE); /* * All pages for the new address space are now in the page table. * But they are not yet in the TLB, remember! */ /* * Read the text from the file into memory. */ lseek(fd, li.t_faddr, SEEK_SET); segment_size = li.t_npg << PAGESHIFT; if (read(fd, (void *) li.t_vaddr, segment_size) != segment_size) { close(fd); //==>> KILL is not defined anywhere: it is an error code distinct //==>> from ERROR because it requires different action in the caller. //==>> Since this error code is internal to your kernel, you get to define it. return KILL; } /* * Read the data from the file into memory. */ lseek(fd, li.id_faddr, 0); segment_size = li.id_npg << PAGESHIFT; if (read(fd, (void *) li.id_vaddr, segment_size) != segment_size) { close(fd); return KILL; } /* * Now set the page table entries for the program text to be readable * and executable, but not writable. */ //==>> Change the protection on the "li.t_npg" pages starting at //==>> virtual address VMEM_1_BASE + (text_pg1 << PAGESHIFT). Note //==>> that these pages will have indices starting at text_pg1 in //==>> the page table for region 1. //==>> The new protection should be (PROT_READ | PROT_EXEC). //==>> If any of these page table entries is also in the TLB, either //==>> invalidate their entries in the TLB or write the updated entries //==>> into the TLB. It's nice for the TLB and the page tables to remain //==>> consistent. for (i = 0; i < li.t_npg; i++) { changePTE(MAX_PT_LEN + text_pg1 + i, PROT_READ | PROT_EXEC, -1); } close(fd); /* we've read it all now */ /* * Zero out the uninitialized data area */ bzero(li.id_end, li.ud_end - li.id_end); /* * Set the entry point in the exception frame. */ //==>> Here you should put your data structure (PCB or process) //==>> proc->context.pc = (caddr_t) li.entry; proc->uctxt.pc = (caddr_t) li.entry; /* * Now, finally, build the argument list on the new stack. */ #ifdef LINUX memset(cpp, 0x00, VMEM_1_LIMIT - ((int) cpp)); #endif *cpp++ = (char *)argcount; /* the first value at cpp is argc */ cp2 = argbuf; for (i = 0; i < argcount; i++) { /* copy each argument and set argv */ *cpp++ = cp; strcpy(cp, cp2); cp += strlen(cp) + 1; cp2 += strlen(cp2) + 1; } free(argbuf); *cpp++ = NULL; /* the last argv is a NULL pointer */ *cpp++ = NULL; /* a NULL pointer for an empty envp */ return SUCCESS; }