void DoSpoon(UserContext *context) { // Get an available process id. int newPid = pidCount++; PCB *child = (PCB *)malloc(sizeof(PCB)); // If for any reason we can't fork, return ERROR to calling process. if (!newPid || !child) { context->regs[0] = ERROR; return; } PCB_Init(child); child->id = newPid; child->parent = current_process; queuePush(child->parent->children, child); if (queueIsEmpty(child->parent->children)) TracePrintf(3, "DoSpoon: parent queue empty.\n"); child->status = RUNNING; // Return 0 to child and arm the child for execution. child->user_context = *context; queuePush(ready_queue, child); queuePush(process_queue, child); KernelContextSwitch(SpoonKernel, current_process, child); // Return child's pid to parent and resume execution of the parent. if (current_process->id == newPid) { *context = current_process->user_context; context->regs[0] = 0; } else { context->regs[0] = newPid; } }
void LoadNextProc(UserContext *context, int block) { DelayPop(); if (!queueIsEmpty(ready_queue)) { if (current_process) { current_process->user_context = *context; if (block == NO_BLOCK) { queuePush(ready_queue, current_process); } } PCB *next = queuePop(ready_queue); TracePrintf(1, "LoadNextProc: Next Process Id: %d\n", next->id); if(next->id == 2) TracePrintf(3, "LoadNextProc: PCB has %p child\n", next->parent->children->head); WriteRegister(REG_PTBR1, (unsigned int) &(next->cow.pageTable)); WriteRegister(REG_TLB_FLUSH, TLB_FLUSH_1); KernelContextSwitch(MyKCS, current_process, next); TracePrintf(1, "LoadNextProc: Got past MyKCS\n"); *context = current_process->user_context; TracePrintf(3, "LoadNextProc: current user context pc is %p\n", context->pc); } }
// Begin executing the specified proc. // NOTE: place the current proc into the correct queue before calling void SwitchToProc(PCB *next_proc, UserContext *user_context) { TracePrintf(TRACE_LEVEL_FUNCTION_INFO, ">>> SwitchToProc()\n"); assert(user_context); assert(next_proc); // Save current user state current_proc->user_context = *user_context; TracePrintf(TRACE_LEVEL_DETAIL_INFO, "Loading next proc context into %p\n", user_context); TracePrintf(TRACE_LEVEL_DETAIL_INFO, "Loading next proc PID: %d\n", next_proc->pid); *user_context = next_proc->user_context; // Set the TLB registers for the region 1 page table. WriteRegister(REG_PTBR1, (unsigned int) next_proc->region_1_page_table); WriteRegister(REG_TLB_FLUSH, TLB_FLUSH_1); PCB *old_proc = current_proc; current_proc = next_proc; int rc = KernelContextSwitch(&SaveKernelContextAndSwitch, old_proc, next_proc); if (SUCCESS == rc) { TracePrintf(TRACE_LEVEL_DETAIL_INFO, "Succesfully switched kernel context!\n"); } else { TracePrintf(TRACE_LEVEL_NON_TERMINAL_PROBLEM, "Failed to save kernel context!\n"); char *err_str = calloc(TERMINAL_MAX_LINE, sizeof(char)); sprintf(err_str, "KernelContextSwitch failed!!! HALTING!!!\n", current_proc->pid); KernelTtyWriteInternal(0, err_str, strnlen(err_str, TERMINAL_MAX_LINE), user_context); free(err_str); Halt(); } // Restore user state of new current process *user_context = current_proc->user_context; TracePrintf(TRACE_LEVEL_FUNCTION_INFO, "<<< SwitchToProc()\n"); }
/* Wrapper of kernel context switch * * @param next_proc: the process to be switched in * @param user_context: current user context */ void context_switch_to(pcb_t *next_proc, UserContext *user_context) { int rc = 0; rc = KernelContextSwitch(&kernel_context_switch, running_proc, next_proc); if(rc) { log_err("Failed to execute magic function!"); Halt(); } }
/* Used for special processes that is not forked to init kernel context, * meaning this process is manually created by Yalnix */ void init_process_kernel(pcb_t *proc) { int rc = 0; rc = KernelContextSwitch(&init_newbie_kernel, proc, proc); if(rc) { log_err("Failed to execute magic function!"); Halt(); } //log_info("Init PID(%d) kernel stack done", proc->pid); }
void KernelStart(char *cmd_args[], unsigned int pmem_size, UserContext *uctxt) { //mark the initial KernelBrk KernelBrk = KernelDataEnd; //FreeFrameCount is only used page_table.c FreeFrameCount = pmem_size / PAGESIZE; ipc_table = (ObjectNode **) malloc(sizeof(ObjectNode *) * HASH_LEN); if (NULL == ipc_table) { TracePrintf(0, "ipc_table cannot be initialized!\n"); return; } memset(ipc_table, 0, sizeof(ObjectNode *) * HASH_LEN); ReadyQueue = (Queue *) malloc(sizeof(Queue)); DelayQueue = (Queue *) malloc(sizeof(Queue)); if (NULL == ReadyQueue) { TracePrintf(0, "ReadyQueue cannot be initialized!\n"); return; } if (NULL == DelayQueue) { TracePrintf(0, "DelayQueue cannot be initialized!\n"); return; } memset(ReadyQueue, 0, sizeof(Queue)); memset(DelayQueue, 0, sizeof(Queue)); u_int i; for (i = 0; i < NUM_TERMINALS; i++) { BufferQueue[i] = (Queue *) malloc(sizeof(Queue)); ReceiveQueue[i] = (Queue *) malloc(sizeof(Queue)); TransmitQueue[i] = (Queue *) malloc(sizeof(Queue)); if (NULL == BufferQueue[i]) { TracePrintf(0, "BufferQueue[%d] cannot be initialized!\n", i); return; } if (NULL == ReceiveQueue[i]) { TracePrintf(0, "ReceiveQueue[%d] cannot be initialized!\n", i); return; } if (NULL == TransmitQueue[i]) { TracePrintf(0, "TransmitQueue[%d] cannot be initialized!\n", i); return; } memset(BufferQueue[i], 0, sizeof(Queue)); memset(ReceiveQueue[i], 0, sizeof(Queue)); memset(TransmitQueue[i], 0, sizeof(Queue)); } //create interrupt vector table in kernel void **interruptVectorTable = (void **) malloc(sizeof(void *) * TRAP_VECTOR_SIZE); if (NULL == interruptVectorTable) { TracePrintf(0, "Interrupt Vector Table cannot be initialized!\n"); return; } //fill each entry in table with pointer to correct handler interruptVectorTable[TRAP_KERNEL] = &TrapKernelHandler; interruptVectorTable[TRAP_CLOCK] = &TrapClockHandler; interruptVectorTable[TRAP_ILLEGAL] = &TrapIllegalHandler; interruptVectorTable[TRAP_MEMORY] = &TrapMemoryHandler; interruptVectorTable[TRAP_MATH] = &TrapMathHandler; interruptVectorTable[TRAP_TTY_RECEIVE] = &TrapTtyReceiveHandler; interruptVectorTable[TRAP_TTY_TRANSMIT] = &TrapTtyTransmitHandler; interruptVectorTable[TRAP_DISK] = &TrapDiskHandler; for (i = 8; i < 16; i++) { interruptVectorTable[i] = &TrapErrorHandler; } //point REG_VECTOR_BASE to interrupt vector table WriteRegister(REG_VECTOR_BASE, (u_int) interruptVectorTable); TracePrintf(0, "Interrupt vector table initialization completed.\n"); //Build page table for Region 0 KernelPageTable = (PTE *) malloc(sizeof(PTE) * MAX_PT_LEN); if (NULL == KernelPageTable) { TracePrintf(0, "KernelPageTable cannot be initialized!\n"); return; } memset(KernelPageTable, 0, sizeof(PTE) * MAX_PT_LEN); TracePrintf(0, "Page tables built successfully.\n"); //create kernel stack union KS = (KernelStack *) KERNEL_STACK_BASE; TracePrintf(0, "Kernel stack initialization completed.\n"); //build input init process PCB KS->CurrentPCB = InitPCB = InitializePCB(); if (NULL == InitPCB) { TracePrintf(0, "InitPCB cannot be initialized!\n"); return; } // We enablePTE after using all the malloc in the kernel because // only in this way we can make sure the getFreeFrame will be called // and update the FreeFrameCount. enablePTE(0, Page(KernelDataStart), PROT_READ | PROT_EXEC); enablePTE(Page(KernelDataStart), Page(KernelBrk), PROT_READ | PROT_WRITE); enablePTE(INTERFACE, Page(KERNEL_STACK_LIMIT), PROT_READ | PROT_WRITE); // initialize virtual memory registers WriteRegister(REG_PTBR0, (u_int) KernelPageTable); WriteRegister(REG_PTLR0, MAX_PT_LEN); WriteRegister(REG_PTBR1, (u_int) KS->CurrentPCB->pagetable); WriteRegister(REG_PTLR1, MAX_PT_LEN); TracePrintf(0, "Table information loading to hardware completed.\n"); //set up the link list to keep track of free frames //from KernelBrk to KERNEL_STACK_BASE *FrameNumber(INTERFACE) = Page(KernelBrk); for (i = Page(KernelBrk); i < INTERFACE - 1; i++) { *FrameNumber(i) = i + 1; } *FrameNumber(INTERFACE - 1) = Page(VMEM_0_LIMIT); for (i = Page(VMEM_0_LIMIT); i < Page(pmem_size - 1); i++) { *FrameNumber(i) = i + 1; } TracePrintf(0, "Free frames' linked list has been set up.\n"); //enable virtual memory WriteRegister(REG_VM_ENABLE, 1); VM_ENABLED = 1; TracePrintf(0, "Virtual memory has been enabled.\n"); TracePrintf(0, "Load input program. (Default init)\n"); if (NULL != cmd_args[0]) { if (FAILURE == LoadProgram(cmd_args[0], cmd_args, InitPCB)) { TracePrintf(0, "Load input program failed!\n"); return; } } else { char *args[] = {"init", NULL}; if (FAILURE == LoadProgram(args[0], args, InitPCB)) { TracePrintf(0, "Load input program failed!\n"); return; } } TracePrintf(0, "Build the Idle process.\n"); //build kernel idle process PCB IdlePCB = InitializePCB(); if (NULL == IdlePCB) { TracePrintf(0, "IdlePCB cannot be initialized!\n"); return; } //sp = virtual address of top of stack IdlePCB->uctxt.sp = (void *) (VMEM_1_LIMIT - INITIAL_STACK_FRAME_SIZE - POST_ARGV_NULL_SPACE); //pc = virtual address of DoIdle function IdlePCB->uctxt.pc = &DoIdle; //enable user stack for idle process IdlePCB->pagetable[MAX_PT_LEN - 1].valid = 1; IdlePCB->pagetable[MAX_PT_LEN - 1].prot = PROT_READ | PROT_WRITE; if (0 == FreeFrameCount) { TracePrintf(0, "No frame for Idle process pagetable!\n"); return; } IdlePCB->pagetable[MAX_PT_LEN - 1].pfn = getFreeFrame(0); //use MyKCS initialize IdlePCB's KC and KS KernelContextSwitch(KCKSInit, (void *) IdlePCB, NULL); TracePrintf(0, "How many time will this line be printed?\n"); //copy current context into hardware provided context *uctxt = KS->CurrentPCB->uctxt; TracePrintf(0, "KernelStart finished.\n"); }