/* 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 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); } }
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); } }
#include "dlist.h" #include "proc.h" #include "load.h" #include "common.h" #include "tty.h" trap_handler interrupt_vector[TRAP_VECTOR_SIZE]; void SetKernelData _PARAMS((void *_Kernel_Data_Start, void *_Kernel_Data_End)) { // Set kernel vm boundaries, noted that most of the *_high is implicitly represented by other's *_low kernel_memory.text_low = (unsigned int)VMEM_BASE; kernel_memory.data_low = (unsigned int)_Kernel_Data_Start; kernel_memory.brk_low = (unsigned int)_Kernel_Data_End; kernel_memory.brk_high = (unsigned int)_Kernel_Data_End; kernel_memory.stack_low = (unsigned int)KERNEL_STACK_BASE; kernel_memory.swap_addr = (unsigned int)DOWN_TO_PAGE(KERNEL_STACK_BASE) - PAGESIZE; } /* * Initialize trap vector */ void init_trap_vector() { interrupt_vector[TRAP_KERNEL] = trap_kernel_handler; interrupt_vector[TRAP_CLOCK] = trap_clock_handler; interrupt_vector[TRAP_ILLEGAL] = trap_illegal_handler; interrupt_vector[TRAP_MEMORY] = trap_memory_handler; interrupt_vector[TRAP_MATH] = trap_math_handler; interrupt_vector[TRAP_TTY_RECEIVE] = trap_tty_receive_handler; interrupt_vector[TRAP_TTY_TRANSMIT] = trap_tty_transmit_handler; interrupt_vector[TRAP_DISK] = trap_disk_handler; }
/* * 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; }