=== Boot Process === This is an overview of the booting process for VSTa. It covers the booting process from the DOS executable boot.exe through the login: prompt. There is also a set of LILO image tools for booting a VSTa image via LILO. The boot process for this is very much the same after LILO "does its thing". ==== Load kernel and server images ==== VSTa is booted from DOS using a large-model DOS program called boot.exe. boot.exe takes a single argument which is the kernel image to load. It opens this file using the usual DOS calls, and reads the image into free memory just after the end of boot.exe's memory. It also zeroes out the BSS portion. boot.exe now opens boot.lst, and reads in a list of files, one per line. For each file, boot.exe opens the file and reads its a.out image into successive memory just after the end of the kernel memory image. After this process, low memory looks like this: +-----+----------+--------+------+-------+----+-----+---- | DOS | boot.exe | kernel | cons | keybd | wd | dos | ... +-------------------------------------------------------- 0 -> ==== Build bootstrap 386 32-bit protected mode data structures ==== The i386 defines numerous arcane data structures which need to be present to switch from 16-bit real mode to 32-bit protected mode. These data structures must remain intact throughout the gyrations needed to get VSTa running, until VSTa is sufficiently "up" to take over with its own data structures. If these structures were tacked onto the end of low memory (from our picture, after "dos"), there would be a danger that they would be overwritten by VSTa data structures during bootup. Therefore, these structures are allocated at the top of low memory, from 640K coming down. ---+--------+-----+-------+-------+-------------+-------------+--------+-----+ ...| Copy() | GDT | TSS16 | TSS32 | Data-L2PTEs | Text-L2PTEs | L1PTEs |Stack| ---+--------+-----+-------+-------+-------------+-------------+--------+-----+ <- 640K "Stack" is a single page of memory used as a stack while switching modes. The top couple of words contain parameters which DOS passes to the VSTA kernel. These are the size of the kernel plus all boot servers, size of extended memory, size of base memory, and the address at which the kernel was loaded. L1PTEs is the root of the page tables (CR3), and Text and Data PTEs are the the second level PTEs. Text maps memory 1:1, and Data maps the a.out data virtual address (4 Mb) onto the data memory for the kernel image loaded in low memory. GDT, TSS16, and TSS32 are the data structures for describing the 32-bit text and data segments, as well as the 32-bit task (used to enter 32-bit mode). Copy() is some position-independent 16-bit code which is the last 16-bit code executed during bootup of VSTa. The VSTa kernel image was loaded above boot.exe's memory, but the VSTa kernel expects to run from a 0 base. Copy() disables interrupts, copies the VSTa kernel down to physical memory starting at 0, switches into protected mode, and jumps through the 32-bit TSS to becomes a 32-bit task running in the VSTa kernel's locore.s at _start. ==== Initial VSTa instructions ==== VSTa first pops the four DOS parameters off its stack. It switches to its own stack, and calls main(). We are now running in C code. main() will call a number of init routines, discussed below. init_machdep() handles the grotty intial setup of memory. It uses the DOS-passed parameters, and sets up machine-independent descriptions of the memory available. It also scans the images of the boot servers which were concatentated onto the end of the kernel image, and builds a machine- independent description of them. This description will be used soon to create the initial processes which allow VSTa to boot. Finally, init_machdep() sets up its own level 1 and 2 page tables, and switches to them. At this point, VSTa is now able to manage its own virtual memory. It calls init_trap() to complete machine-dependent initialization with the initialization of the interrupt system. ==== Further initialization ==== init_trap() creates a GDT (and null LDT, as only the GDT is used). It sets the PC interrupt controller to a mode compatible with protected mode, and puts together an IDT (interrupt descriptor table). By default, each interrupt slot is hooked to a routine which will push the trap ID, and call the common interrupt code. init_trap() then uses a table to override this default action with calls to more appropriate routines. For instance, the page fault vector is rewired to call the page fault handler. The system call vector is hooked to the system call handler. Next, init_page() and init_malloc() are called to set up the hardware-independent parts of the VM system. After these, the general- purpose memory routines can be used to allocate, free, and map memory. init_qio() and init_sched() set up the queued I/O facility and the scheduler. init_proc() now takes the list of boot servers found in init_machdep(), and creates a new process for each server, with its state set to RUN. After this routine, VSTa has a process for each boot server, with each server flagged as waiting to run. init_msg(), init_swap(), init_wire() all initialize various other machine-independent parts of the system. start_clock() enables the PC clock and also enables interrupts. From here, the clock will tick and system time will advance. main() finishes by calling swtch(), which will never return. ==== Running ==== swtch() enters the scheduler. The current "process" (which isn't really, but it's close enough for the scheduler) releases the CPU, and swtch() hunts for a new process to run. It finds the first of the boot servers, switches to its context, and "returns" to user mode (which will be at the first instruction in the a.out). VSTa is now "up", and all further system activity will be handled by VSTa through the usual services of memory management, message passing, scheduling, and so forth.