//--------------------------------------------------------------------- // MemoryPageFaultHandler is called in traps.c whenever a page fault // (better known as a "seg fault" occurs. If the address that was // being accessed is on the stack, we need to allocate a new page // for the stack. If it is not on the stack, then this is a legitimate // seg fault and we should kill the process. Returns MEM_SUCCESS // on success, and kills the current process on failure. Note that // fault_address is the beginning of the page of the virtual address that // caused the page fault, i.e. it is the vaddr with the offset zero-ed // out. // // Note: The existing code is incomplete and only for reference. // Feel free to edit. //--------------------------------------------------------------------- int MemoryPageFaultHandler(PCB *pcb) { int newPage; printf("debug into pagefault\n"); if (pcb->currentSavedFrame[PROCESS_STACK_USER_STACKPOINTER]>= pcb->currentSavedFrame[PROCESS_STACK_FAULT]) { newPage = MemoryAllocPage (); if (newPage == MEM_FAIL) { printf("FATAL:couldn't allocate memory - no free pages!\n"); ProcessKill(); } pcb->npages +=1; printf("(int)pcb->currentSavedFrame[PROCESS_STACK_FAULT]=0x%x\n",(int)pcb->currentSavedFrame[PROCESS_STACK_FAULT]); printf("pcb->currentSavedFrame[PROCESS_STACK_USER_STACKPOINTER]=0x%x\n",pcb->currentSavedFrame[PROCESS_STACK_USER_STACKPOINTER]); pcb->pagetable[(int)pcb->currentSavedFrame[PROCESS_STACK_FAULT]/(MEM_PAGESIZE)] = MemorySetupPte(newPage); printf("Allocate a newpage, the page number is %d\n",newPage); return MEM_SUCCESS; } else{ printf("FATAL: page fault!\n"); ProcessKill(); return MEM_FAIL; } }
//---------------------------------------------------------------------- // // ProcessFork // // Create a new process and make it runnable. This involves the // following steps: // * Allocate resources for the process (PCB, memory, etc.) // * Initialize the resources // * Place the PCB on the runnable queue // // NOTE: This code has been tested for system processes, but not // for user processes. // //---------------------------------------------------------------------- int ProcessFork (VoidFunc func, uint32 param, char *name, int isUser) { int i; // Loop index variable int fd, n; // Used for reading code from files. int start, codeS, codeL; // Used for reading code from files. int dataS, dataL; // Used for reading code from files. int addr = 0; // Used for reading code from files. unsigned char buf[100]; // Used for reading code from files. uint32 *stackframe; // Stores address of current stack frame. PCB *pcb; // Holds pcb while we build it for this process. int intrs; // Stores previous interrupt settings. uint32 initial_user_params[MAX_ARGS+2]; // Initial memory for user parameters (argc, argv) // initial_user_params[0] = argc // initial_user_params[1] = argv, points to initial_user_params[2] // initial_user_params[2] = address of string for argv[0] // initial_user_params[3] = address of string for argv[1] // ... uint32 argc=0; // running counter for number of arguments uint32 offset; // Used in parsing command line argument strings, holds offset (in bytes) from // beginning of the string to the current argument. uint32 initial_user_params_bytes; // total number of bytes in initial user parameters array int newPage; intrs = DisableIntrs (); dbprintf ('I', "Old interrupt value was 0x%x.\n", intrs); dbprintf ('p', "Entering ProcessFork args=0x%x 0x%x %s %d\n", (int)func, param, name, isUser); // Get a free PCB for the new process if (AQueueEmpty(&freepcbs)) { printf ("FATAL error: no free processes!\n"); exitsim (); // NEVER RETURNS! } pcb = (PCB *)AQueueObject(AQueueFirst (&freepcbs)); dbprintf ('p', "Got a link @ 0x%x\n", (int)(pcb->l)); if (AQueueRemove (&(pcb->l)) != QUEUE_SUCCESS) { printf("FATAL ERROR: could not remove link from freepcbsQueue in ProcessFork!\n"); exitsim(); } // This prevents someone else from grabbing this process ProcessSetStatus (pcb, PROCESS_STATUS_RUNNABLE); // At this point, the PCB is allocated and nobody else can get it. // However, it's not in the run queue, so it won't be run. Thus, we // can turn on interrupts here. RestoreIntrs (intrs); // Copy the process name into the PCB. dstrcpy(pcb->name, name); //---------------------------------------------------------------------- // This section initializes the memory for this process //---------------------------------------------------------------------- // Allocate 1 page for system stack, 1 page for user stack (at top of // virtual address space), and 4 pages for user code and global data. //--------------------------------------------------------- // STUDENT: allocate pages for a new process here. The // code below assumes that you set the "stackframe" variable // equal to the last 4-byte-aligned address in physical page // for the system stack. //--------------------------------------------------------- // Pages for code and global data and Heap pcb->npages = 5; for(i = 0; i < pcb->npages; i++) { newPage = MemoryAllocPage(); if(newPage == MEM_FAIL) { printf ("FATAL: couldn't allocate memory - no free pages!\n"); ProcessFreeResources (pcb); return PROCESS_FORK_FAIL; } pcb->pagetable[i] = MemorySetupPte (newPage); } // Initialize nodes in pool for (i = 1; i <= MEM_HEAP_MAX_NODES; i++) { pcb->htree_array[i].parent = NULL; pcb->htree_array[i].cleft = NULL; pcb->htree_array[i].crght = NULL; pcb->htree_array[i].index = i; pcb->htree_array[i].size = -1; pcb->htree_array[i].addr = -1; pcb->htree_array[i].inuse = 0; pcb->htree_array[i].order = -1; } // Initialize Heap tree pcb->htree_array[1].size = MEM_PAGESIZE; pcb->htree_array[1].addr = 0; pcb->htree_array[1].order = 7; // user stack pcb->npages += 1; newPage = MemoryAllocPage(); if(newPage == MEM_FAIL) { printf ("FATAL: couldn't allocate user stack - no free pages!\n"); ProcessFreeResources (pcb); return PROCESS_FORK_FAIL; } pcb->pagetable[MEM_ADDRESS_TO_PAGE(MEM_MAX_VIRTUAL_ADDRESS)] = MemorySetupPte (newPage); // for system stack newPage = MemoryAllocPage (); if(newPage == MEM_FAIL) { printf ("FATAL: couldn't allocate system stack - no free pages!\n"); ProcessFreeResources (pcb); return PROCESS_FORK_FAIL; } pcb->sysStackArea = newPage * MEM_PAGESIZE; //---------------------------------------------------------------------- // Stacks grow down from the top. The current system stack pointer has // to be set to the bottom of the interrupt stack frame, which is at the // high end (address-wise) of the system stack. stackframe = (uint32 *)(pcb->sysStackArea + MEM_PAGESIZE - 4); dbprintf('p', "ProcessFork: SystemStack page=%d sysstackarea=0x%x\n", newPage, pcb->sysStackArea); // Now that the stack frame points at the bottom of the system stack memory area, we need to // move it up (decrement it) by one stack frame size because we're about to fill in the // initial stack frame that will be loaded for this PCB when it gets switched in by // ProcessSchedule the first time. stackframe -= PROCESS_STACK_FRAME_SIZE; // The system stack pointer is set to the base of the current interrupt stack frame. pcb->sysStackPtr = stackframe; // The current stack frame pointer is set to the same thing. pcb->currentSavedFrame = stackframe; dbprintf ('p', "Setting up PCB @ 0x%x (sys stack=0x%x, mem=0x%x, size=0x%x)\n", (int)pcb, pcb->sysStackArea, pcb->pagetable[0], pcb->npages * MEM_PAGESIZE); //---------------------------------------------------------------------- // This section sets up the stack frame for the process. This is done // so that the frame looks to the interrupt handler like the process // was "suspended" right before it began execution. The standard // mechanism of swapping in the registers and returning to the place // where it was "interrupted" will then work. //---------------------------------------------------------------------- // The previous stack frame pointer is set to 0, meaning there is no // previous frame. dbprintf('p', "ProcessFork: stackframe = 0x%x\n", (int)stackframe); stackframe[PROCESS_STACK_PREV_FRAME] = 0; //---------------------------------------------------------------------- // STUDENT: setup the PTBASE, PTBITS, and PTSIZE here on the current // stack frame. //---------------------------------------------------------------------- // Set the base of the level 1 page table. If there's only one page // table level, this is it. For 2-level page tables, put the address // of the level 1 page table here. For 2-level page tables, we'll also // have to build up the necessary tables.... stackframe[PROCESS_STACK_PTBASE] = (uint32)(&(pcb->pagetable[0])); // Set the size (maximum number of entries) of the level 1 page table. // In our case, it's just one page, but it could be larger. stackframe[PROCESS_STACK_PTSIZE] = MEM_PAGE_TBL_SIZE; // Set the number of bits for both the level 1 and level 2 page tables. // This can be changed on a per-process basis if desired. For now, // though, it's fixed. stackframe[PROCESS_STACK_PTBITS] = (MEM_L1FIELD_FIRST_BITNUM << 16) + MEM_L1FIELD_FIRST_BITNUM; if (isUser) { dbprintf ('p', "About to load %s\n", name); fd = ProcessGetCodeInfo (name, &start, &codeS, &codeL, &dataS, &dataL); if (fd < 0) { // Free newPage and pcb so we don't run out... ProcessFreeResources (pcb); return (-1); } dbprintf ('p', "File %s -> start=0x%08x\n", name, start); dbprintf ('p', "File %s -> code @ 0x%08x (size=0x%08x)\n", name, codeS, codeL); dbprintf ('p', "File %s -> data @ 0x%08x (size=0x%08x)\n", name, dataS, dataL); while ((n = ProcessGetFromFile (fd, buf, &addr, sizeof (buf))) > 0) { dbprintf ('i', "Placing %d bytes at vaddr %08x.\n", n, addr - n); // Copy the data to user memory. Note that the user memory needs to // have enough space so that this copy will succeed! MemoryCopySystemToUser (pcb, buf, (char *)(addr - n), n); } FsClose (fd); stackframe[PROCESS_STACK_ISR] = PROCESS_INIT_ISR_USER; //---------------------------------------------------------------------- // STUDENT: setup the initial user stack pointer here as the top // of the process's virtual address space (4-byte aligned). //---------------------------------------------------------------------- stackframe[PROCESS_STACK_USER_STACKPOINTER] = MEM_MAX_VIRTUAL_ADDRESS - 3; dbprintf('p', "ProcessFork: UserStack usrsp=0x%x\n", stackframe[PROCESS_STACK_USER_STACKPOINTER]); //-------------------------------------------------------------------- // This part is setting up the initial user stack with argc and argv. //-------------------------------------------------------------------- // Copy the entire set of strings of command line parameters onto the user stack. // The "param" variable is a pointer to the start of a sequenial set of strings, // each ending with its own '\0' character. The final "string" of the sequence // must be an empty string to indicate that the sequence is done. Since we // can't figure out how long the set of strings actually is in this scenario, // we have to copy the maximum possible string length and parse things manually. stackframe[PROCESS_STACK_USER_STACKPOINTER] -= SIZE_ARG_BUFF; MemoryCopySystemToUser (pcb, (char *)param, (char *)stackframe[PROCESS_STACK_USER_STACKPOINTER], SIZE_ARG_BUFF); // Now that the main string is copied into the user space, we need to setup // argv as an array of pointers into that string, and argc as the total // number of arguments found in the string. The first call to get_argument // should return 0 as the offset of the first string. offset = get_argument((char *)param); // Compute the addresses in user space of where each string for the command line arguments // begins. These addresses make up the argv array. for(argc=0; argc < MAX_ARGS; argc++) { // The "+2" is because initial_user_params[0] is argc, and initial_user_params[1] is argv. // The address can be found as the current stack pointer (which points to the start of // the params list) plus the byte offset of the parameter from the beginning of // the list of parameters. initial_user_params[argc+2] = stackframe[PROCESS_STACK_USER_STACKPOINTER] + offset; offset = get_argument(NULL); if (offset == 0) { initial_user_params[argc+2+1] = 0; // last entry should be a null value break; } } // argc is currently the index of the last command line argument. We need it to instead // be the number of command line arguments, so we increment it by 1. argc++; // Now argc can be stored properly initial_user_params[0] = argc; // Compute where initial_user_params[3] will be copied in user space as the // base of the array of string addresses. The entire initial_user_params array // of uint32's will be copied onto the stack. We'll move the stack pointer by // the necessary amount, then start copying the array. Therefore, initial_user_params[3] // will reside at the current stack pointer value minus the number of command line // arguments (argc). initial_user_params[1] = stackframe[PROCESS_STACK_USER_STACKPOINTER] - (argc*sizeof(uint32)); // Now copy the actual memory. Remember that stacks grow down from the top of memory, so // we need to move the stack pointer first, then do the copy. The "+2", as before, is // because initial_user_params[0] is argc, and initial_user_params[1] is argv. initial_user_params_bytes = (argc + 2) * sizeof(uint32); stackframe[PROCESS_STACK_USER_STACKPOINTER] -= initial_user_params_bytes; MemoryCopySystemToUser (pcb, (char *)initial_user_params, (char *)(stackframe[PROCESS_STACK_USER_STACKPOINTER]), initial_user_params_bytes); // Set the correct address at which to execute a user process. stackframe[PROCESS_STACK_IAR] = (uint32)start; // Flag this as a user process pcb->flags |= PROCESS_TYPE_USER; } else { // Don't worry about messing with any code here for kernel processes because // there aren't any kernel processes in DLXOS. // Set r31 to ProcessExit(). This will only be called for a system // process; user processes do an exit() trap. stackframe[PROCESS_STACK_IREG+31] = (uint32)ProcessExit; // Set the stack register to the base of the system stack. //stackframe[PROCESS_STACK_IREG+29]=pcb->sysStackArea + MEM_PAGESIZE; // Set the initial parameter properly by placing it on the stack frame // at the location pointed to by the "saved" stack pointer (r29). *((uint32 *)(stackframe[PROCESS_STACK_IREG+29])) = param; // Set up the initial address at which to execute. This is done by // placing the address into the IAR slot of the stack frame. stackframe[PROCESS_STACK_IAR] = (uint32)func; // Set the initial value for the interrupt status register stackframe[PROCESS_STACK_ISR] = PROCESS_INIT_ISR_SYS; // Mark this as a system process. pcb->flags |= PROCESS_TYPE_SYSTEM; } // Place the PCB onto the run queue. intrs = DisableIntrs (); if ((pcb->l = AQueueAllocLink(pcb)) == NULL) { printf("FATAL ERROR: could not get link for forked PCB in ProcessFork!\n"); exitsim(); } if (AQueueInsertLast(&runQueue, pcb->l) != QUEUE_SUCCESS) { printf("FATAL ERROR: could not insert link into runQueue in ProcessFork!\n"); exitsim(); } RestoreIntrs (intrs); // If this is the first process, make it the current one if (currentPCB == NULL) { dbprintf ('p', "Setting currentPCB=0x%x, stackframe=0x%x\n", (int)pcb, (int)(pcb->currentSavedFrame)); currentPCB = pcb; } dbprintf ('p', "Leaving ProcessFork (%s)\n", name); // Return the process number (found by subtracting the PCB number // from the base of the PCB array). return (pcb - pcbs); }
int ProcessRealFork (PCB* ppcb) { PCB *cpcb; // Holds pcb while we build it for this process. int intrs; // Stores previous interrupt settings. int newPage; int i; uint32 *stackframe; intrs = DisableIntrs (); dbprintf ('I', "Old interrupt value was 0x%x.\n", intrs); dbprintf ('p', "Entering ProcessRealFork ppcb=%d\n", GetPidFromAddress(ppcb)); // Get a free PCB for the new process if (AQueueEmpty(&freepcbs)) { printf ("FATAL error: no free processes!\n"); exitsim (); // NEVER RETURNS! } cpcb = (PCB *)AQueueObject(AQueueFirst (&freepcbs)); dbprintf ('p', "Got a link @ 0x%x\n", (int)(cpcb->l)); if (AQueueRemove (&(cpcb->l)) != QUEUE_SUCCESS) { printf("FATAL ERROR: could not remove link from freepcbsQueue in ProcessFork!\n"); exitsim(); } // This prevents someone else from grabbing this process ProcessSetStatus (cpcb, PROCESS_STATUS_RUNNABLE); // cpcb shares code and global data with ppcb for(i = 0; i < 4; i++) { ppcb->pagetable[i] |= MEM_PTE_READONLY; MemorySharePage(ppcb->pagetable[i]); } // user stack is also shared at first ppcb->pagetable[MEM_ADDRESS_TO_PAGE(MEM_MAX_VIRTUAL_ADDRESS)] |= MEM_PTE_READONLY; MemorySharePage(ppcb->pagetable[MEM_ADDRESS_TO_PAGE(MEM_MAX_VIRTUAL_ADDRESS)]); // copy parent pcb to child pcb bcopy((char *)ppcb, (char *)cpcb, sizeof(PCB)); // At this point, the PCB is allocated and nobody else can get it. // However, it's not in the run queue, so it won't be run. Thus, we // can turn on interrupts here. RestoreIntrs (intrs); // for system stack newPage = MemoryAllocPage (); if(newPage == MEM_FAIL) { printf ("FATAL: couldn't allocate system stack - no free pages!\n"); ProcessFreeResources (cpcb); return PROCESS_FORK_FAIL; } cpcb->sysStackArea = newPage * MEM_PAGESIZE; // copy system stack from ppcb to cpcb bcopy((char *)(ppcb->sysStackArea), (char *)(cpcb->sysStackArea), MEM_PAGESIZE); dbprintf('p', "ProcessRealFork: SystemStack page=%d sysstackarea=0x%x\n", newPage, cpcb->sysStackArea); // printf("ProcessRealFork: parent_sysstackarea=0x%x child_sysstackarea=0x%x\n", ppcb->sysStackArea, cpcb->sysStackArea); //---------------------------------------------------------------------- // Stacks grow down from the top. The current system stack pointer has // to be set to the bottom of the interrupt stack frame, which is at the // high end (address-wise) of the system stack. stackframe = (uint32 *)(cpcb->sysStackArea + MEM_PAGESIZE - 4); // Now that the stack frame points at the bottom of the system stack memory area, we need to // move it up (decrement it) by one stack frame size because we're about to fill in the // initial stack frame that will be loaded for this PCB when it gets switched in by // ProcessSchedule the first time. stackframe -= PROCESS_STACK_FRAME_SIZE; // The system stack pointer is set to the base of the current interrupt stack frame. cpcb->sysStackPtr = stackframe; // The current stack frame pointer is set to the same thing. cpcb->currentSavedFrame = stackframe; dbprintf ('p', "Setting up PCB @ 0x%x (sys stack=0x%x, mem=0x%x, size=0x%x)\n", (int)cpcb, cpcb->sysStackArea, cpcb->pagetable[0], cpcb->npages * MEM_PAGESIZE); //---------------------------------------------------------------------- // STUDENT: setup the PTBASE, PTBITS, and PTSIZE here on the current // stack frame. //---------------------------------------------------------------------- // Set the base of the level 1 page table. If there's only one page // table level, this is it. For 2-level page tables, put the address // of the level 1 page table here. For 2-level page tables, we'll also // have to build up the necessary tables.... stackframe[PROCESS_STACK_PTBASE] = (uint32)(&(cpcb->pagetable[0])); // Place the PCB onto the run queue. intrs = DisableIntrs (); if ((cpcb->l = AQueueAllocLink(cpcb)) == NULL) { printf("FATAL ERROR: could not get link for forked PCB in ProcessFork!\n"); exitsim(); } if (AQueueInsertLast(&runQueue, cpcb->l) != QUEUE_SUCCESS) { printf("FATAL ERROR: could not insert link into runQueue in ProcessFork!\n"); exitsim(); } RestoreIntrs (intrs); ProcessSetResult(cpcb, 0); ProcessSetResult(ppcb, GetPidFromAddress(cpcb)); // TEST PRINTS printf("\nIn ProcessRealFork:\n"); printf("----- Page table of parent process PID:%d -----\n", GetPidFromAddress(ppcb)); ProcessForkTestPrints(ppcb); printf("\n----- Page table of child process PID: %d -----\n", GetPidFromAddress(cpcb)); ProcessForkTestPrints(cpcb); dbprintf ('p', "Leaving ProcessRealFork cpcbid=%d\n", GetPidFromAddress(cpcb)); return PROCESS_FORK_SUCCESS; }
//---------------------------------------------------------------------- // // ProcessFork // // Create a new process and make it runnable. This involves the // following steps: // * Allocate resources for the process (PCB, memory, etc.) // * Initialize the resources // * Place the PCB on the runnable queue // // NOTE: This code has been tested for system processes, but not // for user processes. // //---------------------------------------------------------------------- int ProcessFork (VoidFunc func, uint32 param, int p_nice, int p_info,char *name, int isUser) { int i, j, fd, n; Link *l; int start, codeS, codeL, dataS, dataL; uint32 *stackframe; int newPage; PCB *pcb; int addr = 0; int intrs; unsigned char buf[100]; uint32 dum[MAX_ARGS+8], count, offset; char *str; intrs = DisableIntrs (); dbprintf ('I', "Old interrupt value was 0x%x.\n", intrs); dbprintf ('p', "Entering ProcessFork args=0x%x 0x%x %s %d\n", func, param, name, isUser); // Get a free PCB for the new process if (QueueEmpty (&freepcbs)) { printf ("FATAL error: no free processes!\n"); exitsim (); // NEVER RETURNS! } l = QueueFirst (&freepcbs); dbprintf ('p', "Got a link @ 0x%x\n", l); QueueRemove (l); pcb = (PCB *)(l->object); // This prevents someone else from grabbing this process ProcessSetStatus (pcb, PROCESS_STATUS_RUNNABLE); // At this point, the PCB is allocated and nobody else can get it. // However, it's not in the run queue, so it won't be run. Thus, we // can turn on interrupts here. dbprintf ('I', "Before restore interrupt value is 0x%x.\n", CurrentIntrs()); RestoreIntrs (intrs); dbprintf ('I', "New interrupt value is 0x%x.\n", CurrentIntrs()); // Copy the process name into the PCB. dstrcpy (pcb->name, name); //---------------------------------------------------------------------- // This section initializes the memory for this process //---------------------------------------------------------------------- // For now, we'll use one user page and a page for the system stack. // For system processes, though, all pages must be contiguous. // Of course, system processes probably need just a single page for // their stack, and don't need any code or data pages allocated for them. pcb->npages = 1; newPage = MemoryAllocPage (); if (newPage == 0) { printf ("aFATAL: couldn't allocate memory - no free pages!\n"); exitsim (); // NEVER RETURNS! } pcb->pagetable[0] = MemorySetupPte (newPage); newPage = MemoryAllocPage (); if (newPage == 0) { printf ("bFATAL: couldn't allocate system stack - no free pages!\n"); exitsim (); // NEVER RETURNS! } pcb->sysStackArea = newPage * MEMORY_PAGE_SIZE; //--------------------------------------- // Lab3: initialized pcb member for your scheduling algorithm here //-------------------------------------- pcb->p_nice = p_nice < 0 ? 0 : p_nice; pcb->p_info = p_info; pcb->sleeptime = my_timer_get(); pcb->estcpu = 0; pcb->prio = PUSER; pcb->processed = 1 - processedFlag; pcb->estcputime = 0; //---------------------------------------------------------------------- // Stacks grow down from the top. The current system stack pointer has // to be set to the bottom of the interrupt stack frame, which is at the // high end (address-wise) of the system stack. stackframe = ((uint32 *)(pcb->sysStackArea + MEMORY_PAGE_SIZE)) - (PROCESS_STACK_FRAME_SIZE + 8); // The system stack pointer is set to the base of the current interrupt // stack frame. pcb->sysStackPtr = stackframe; // The current stack frame pointer is set to the same thing. pcb->currentSavedFrame = stackframe; dbprintf ('p', "Setting up PCB @ 0x%x (sys stack=0x%x, mem=0x%x, size=0x%x)\n", pcb, pcb->sysStackArea, pcb->pagetable[0], pcb->npages * MEMORY_PAGE_SIZE); //---------------------------------------------------------------------- // This section sets up the stack frame for the process. This is done // so that the frame looks to the interrupt handler like the process // was "suspended" right before it began execution. The standard // mechanism of swapping in the registers and returning to the place // where it was "interrupted" will then work. //---------------------------------------------------------------------- // The previous stack frame pointer is set to 0, meaning there is no // previous frame. stackframe[PROCESS_STACK_PREV_FRAME] = 0; // Set the base of the level 1 page table. If there's only one page // table level, this is it. For 2-level page tables, put the address // of the level 1 page table here. For 2-level page tables, we'll also // have to build up the necessary tables.... stackframe[PROCESS_STACK_PTBASE] = (uint32)&(pcb->pagetable[0]); // Set the size (maximum number of entries) of the level 1 page table. // In our case, it's just one page, but it could be larger. stackframe[PROCESS_STACK_PTSIZE] = pcb->npages; // Set the number of bits for both the level 1 and level 2 page tables. // This can be changed on a per-process basis if desired. For now, // though, it's fixed. stackframe[PROCESS_STACK_PTBITS] = (MEMORY_L1_PAGE_SIZE_BITS + (MEMORY_L2_PAGE_SIZE_BITS << 16)); if (isUser) { dbprintf ('p', "About to load %s\n", name); fd = ProcessGetCodeInfo (name, &start, &codeS, &codeL, &dataS, &dataL); if (fd < 0) { // Free newpage and pcb so we don't run out... ProcessFreeResources (pcb); return (-1); } dbprintf ('p', "File %s -> start=0x%08x\n", name, start); dbprintf ('p', "File %s -> code @ 0x%08x (size=0x%08x)\n", name, codeS, codeL); dbprintf ('p', "File %s -> data @ 0x%08x (size=0x%08x)\n", name, dataS, dataL); while ((n = ProcessGetFromFile (fd, buf, &addr, sizeof (buf))) > 0) { dbprintf ('p', "Placing %d bytes at vaddr %08x.\n", n, addr - n); // Copy the data to user memory. Note that the user memory needs to // have enough space so that this copy will succeed! MemoryCopySystemToUser (pcb, buf, addr - n, n); } FsClose (fd); stackframe[PROCESS_STACK_ISR] = PROCESS_INIT_ISR_USER; // Set the initial stack pointer correctly. Currently, it's just set // to the top of the (single) user address space allocated to this // process. str = (char *)param; stackframe[PROCESS_STACK_IREG+29] = MEMORY_PAGE_SIZE - SIZE_ARG_BUFF; // Copy the initial parameter to the top of stack MemoryCopySystemToUser (pcb, (char *)str, (char *)stackframe[PROCESS_STACK_IREG+29], SIZE_ARG_BUFF-32); offset = get_argument((char *)param); dum[2] = MEMORY_PAGE_SIZE - SIZE_ARG_BUFF + offset; for(count=3;;count++) { offset=get_argument(NULL); dum[count] = MEMORY_PAGE_SIZE - SIZE_ARG_BUFF + offset; if(offset==0) { break; } } dum[0] = count-2; dum[1] = MEMORY_PAGE_SIZE - SIZE_ARG_BUFF - (count-2)*4; MemoryCopySystemToUser (pcb, (char *)dum, (char *)(stackframe[PROCESS_STACK_IREG+29]-count*4), (count)*sizeof(uint32)); stackframe[PROCESS_STACK_IREG+29] -= 4*count; // Set the correct address at which to execute a user process. stackframe[PROCESS_STACK_IAR] = (uint32)start; pcb->flags |= PROCESS_TYPE_USER; } else { // Set r31 to ProcessExit(). This will only be called for a system // process; user processes do an exit() trap. stackframe[PROCESS_STACK_IREG+31] = (uint32)ProcessExit; // Set the stack register to the base of the system stack. stackframe[PROCESS_STACK_IREG+29]=pcb->sysStackArea + MEMORY_PAGE_SIZE-32; // Set the initial parameter properly by placing it on the stack frame // at the location pointed to by the "saved" stack pointer (r29). *((uint32 *)(stackframe[PROCESS_STACK_IREG+29])) = param; // Set up the initial address at which to execute. This is done by // placing the address into the IAR slot of the stack frame. stackframe[PROCESS_STACK_IAR] = (uint32)func; // Set the initial value for the interrupt status register stackframe[PROCESS_STACK_ISR] = PROCESS_INIT_ISR_SYS; // Mark this as a system process. pcb->flags |= PROCESS_TYPE_SYSTEM; } // Place the PCB onto the run queue. intrs = DisableIntrs (); QueueInsertLast (&runQueue[pcb->prio/4], l); RestoreIntrs (intrs); // If this is the first process, make it the current one if (currentPCB == NULL) { dbprintf ('p', "Setting currentPCB=0x%x, stackframe=0x%x\n", pcb, pcb->currentSavedFrame); currentPCB = pcb; } dbprintf ('p', "Leaving ProcessFork (%s)\n", name); // Return the process number (found by subtracting the PCB number // from the base of the PCB array). return (pcb - pcbs); }