void _init( void ) { pcb_t *pcb; context_t *context; status_t stat; /* ** BOILERPLATE CODE - taken from basic framework ** ** Initialize interrupt stuff. */ __init_interrupts(); // IDT and PIC initialization /* ** Console I/O system. */ c_io_init(); c_setscroll( 0, 7, 99, 99 ); c_puts_at( 0, 6, "================================================================================" ); /* ** 20103-SPECIFIC CODE STARTS HERE */ /* ** Initialize various OS modules */ c_puts( "Starting module init: " ); _q_init(); // must be first _pcb_init(); _stack_init(); _sio_init(); _syscall_init(); _sched_init(); _clock_init(); //_pci_init(); build_lapic_info(); _paging_init(); c_puts( "\n" ); c_puts(" Ending init\n"); initSMP(); /* ** Create the initial system ESP ** ** This will be the address of the next-to-last ** longword in the system stack. */ _system_esp = ((uint32_t *) ( (&_system_stack) + 1)) - 2; /* ** Install the ISRs */ __install_isr( INT_VEC_TIMER, _isr_clock ); __install_isr( INT_VEC_SYSCALL, _isr_syscall ); __install_isr( INT_VEC_SERIAL_PORT_1, _isr_sio ); /* ** Create the initial process ** ** Code mostly stolen from _sys_fork() and _sys_exec(); ** if either of those routines change, SO MUST THIS!!! ** ** First, get a PCB and a stack */ stat = _pcb_alloc( &pcb ); if( stat != E_SUCCESS ) { _kpanic( "_init", "first pcb alloc status %s", stat ); } stat = _stack_alloc( &(pcb->stack) ); if( stat != E_SUCCESS ) { _kpanic( "_init", "first stack alloc status %s", stat ); } /* ** Next, set up various PCB fields */ pcb->pid = PID_INIT; pcb->ppid = PID_INIT; pcb->prio = PRIO_MAXIMUM; /* ** Set up the initial process context. */ context = _setup_stack( pcb->stack, (uint32_t) init ); // Finally, set up the process' ESP context->esp = (uint32_t) context; // Make it the "current" process _current = pcb; _current->context = context; /* ** Starting up the idle routine is the responsibility ** of the initial user process. */ /* ** Turn on the SIO receiver (the transmitter will be turned ** on/off as characters are being sent) */ _sio_enable( SIO_RX ); /* ** END OF 20103-SPECIFIC CODE ** ** Finally, report that we're all done. */ c_puts( "System initialization complete.\n" ); }
pcb_t *_create_process( pcb_t *curr ) { pcb_t *pcb; stack_t *stack; int offset; uint32_t *ptr; // allocate the new structures pcb = _pcb_alloc(); if( pcb == NULL ) { return( NULL ); } stack = _stack_alloc(); if( stack == NULL ) { _pcb_free(pcb); return( NULL ); } /* ** The PCB argument will be NULL if this function is called ** from the system initialization, and non-NULL if called ** from the fork() implementation. ** ** In the former case, we initialize the new data structures for ** a brand-new process. ** ** In the latter case, we replicate the information from the ** existing process whose PCB was passed in. */ if( curr != NULL ) { // called from fork() // duplicate the PCB and stack contents _kmemcpy( (void *) pcb, (void *) curr, sizeof(pcb_t) ); _kmemcpy( (void *) stack, (void *) curr->stack, sizeof(stack_t) ); // update the entries which should be changed in the PCB pcb->pid = _next_pid++; pcb->ppid = curr->pid; pcb->stack = stack; /* ** We duplicated the original stack contents, which ** means that the context pointer and ESP and EBP values ** in the new stack are still pointing into the original ** stack. We need to correct all of these. ** ** We have to change EBP because that's how the compiled ** code for the user process accesses its local variables. ** If we didn't change this, as soon as the new process ** was dispatched, it would start to stomp on the local ** variables in the original process' stack. We also ** have to fix the EBP chain in the child process. ** ** None of this would be an issue if we were doing "real" ** virtual memory, as we would be talking about virtual ** addresses here rather than physical addresses, and all ** processes would share the same virtual address space ** layout. ** ** First, determine the distance (in bytes) between the ** two stacks. This is the adjustment value we must add ** to the three pointers to correct them. Note that this ** distance may be positive or negative, depending on the ** relative placement of the two stacks in memory. */ offset = (void *) (pcb->stack) - (void *) (curr->stack); // modify the context pointer for the new process pcb->context = (context_t *)( (void *) (pcb->context) + offset ); // now, change ESP and EBP in the new process (easy to // do because they're just uint32_t values, not really // pointers) pcb->context->esp += offset; pcb->context->ebp += offset; /* ** Next, we must fix the EBP chain in the new stack. This ** is necessary in the situation where the fork() occurred ** in a nested function call sequence; we fixed EBP, but ** the "saved" EBP in the stack frame is pointing to the ** calling function's frame in the original stack, not the ** new stack. ** ** We are guaranteed that the chain of frames ends at the ** frame for the original process' main() routine, because ** exec() will initialize EBP for the process to 0, and the ** entry prologue code in main() routine will push EBP, ** ensuring a NULL pointer in the chain. */ // start at the current frame ptr = (uint32_t *) pcb->context->ebp; // follow the chain of frame pointers to its end while( *ptr != 0 ) { // update the link from this frame to the previous *ptr += offset; // follow the updated link ptr = (uint32_t *) *ptr; } } else { // called from init pcb->pid = pcb->ppid = PID_INIT; pcb->stack = stack; } // all done - return the new PCB return( pcb ); }