The stage 3 / main kernel for My Operating System (MOS)
This module is responsible for managing and enabling the running of user applications
For software programming, check the System calls section
Marcel Sondaar
Educational purposes
stage3_ia32_pc.asm | The stage 3 / main kernel for My Operating System (MOS) |
AP detection and bootstrap | CALL InitializeAPIC ; todo CALL Multiprocessor ; needs fixing |
Parse memtable | |
fix debug stub | |
Kernel subfunctions | |
AddGate | allocates a gate and assigns a handler to it |
AddThread | adds a new thread to the scheduler’s list |
AddBlockedThread | adds a new thread to the blocked list |
Starvation in Route functions | add cli/sti blocks to prevent this |
AddRoute | adds a name to port lookup to the table |
AllocateInterrupt | attempts to allocate an IDT entry of choice |
AllocateGDTEntry | Allocates a free slot in the global descriptor table and loads it with the given parameters |
AllocateGDTEntry | Allocates and writes a TSS entry in the GDT |
AllocateMemoryLo | allocates memory from the table, preferrably from the beginning |
AllocateMemoryHi | allocates memory from the table, preferrably from the end |
AllocateMemoryUnit | Allocate one unit of free memory from a given table |
AllocatePortLo | Attempts to allocate a port in the lower 2k area |
AllocatePortHi | Attempts to allocate a port (4) in the higher area |
AllocateTaskset | allocates a task set (descriptor + kernel stack page) |
AllocateV8086Taskset | allocates a task set (descriptor + kernel stack page) |
BootstrapUserspace | load first userspace module and schedule it for execution |
subroutine for this | |
GetGateAddress | Looks up a gate’s values |
GetPageEntry | Loads a page table with a given entry |
GetPageEntry race condition | mutex on resolver use |
GetFPUInfo | Get FPU prescence, detect its type then configure the kernel accordingly |
GetProcessorInfo | Gets processor info and configures it accordingly |
expand detection for sse3, 3dnow, syscall | |
AC capability detection | |
Cyrix detection | |
GetRoute | Looks up a route in the route table and returns |
HardwareDelay | Delays for a short period of time. |
InitializeIDT | Sets up the IDT and loads it |
InitializeIO | Allocates and fills an IO handout table |
InitializeGDT | Sets up the GDT and loads it |
InitializeIPC | prepares the data structures needed for inter-process communication |
InitializePaging | constructs the page and allocation tables needed for a paged system |
InitializePIC | Brings the PIC into a known state |
InitializePIT | Brings the PIT into a known state |
needs the exact needed frequency calculated | none |
InitializeScheduler | prepares and starts the scheduler |
InitializeTSS | build the kernel tss from scratch |
InitializeTDT | Builds a Task Description Table for the kernel |
InsertPage | Loads a page table with a given entry |
InsertPageGlobal | loads a page table with a given entry, marking it global |
InsertPageUser | loads a page table with a given entry, marking it for userspace |
InsertPageDriver | loads a page table with a given entry, marking it both uncacheable and for userspace |
InvalidatePage | flushes a page from the TLB. |
InstallPatches | checks and runs the patches |
ProcessorCOMApatch | for fixing the cyrix 6x86 coma bug |
ProcessorF00Fpatch | for installing the workaround for the intel LOCK CMPXCHG8B bug |
ProcessorNoFPpatch | changes the #NM interrupt handler to fault (using self-modifying code) instead of switching fpu contexts |
write SIMD exception handler | |
ProcessorSYSEpatch | enables the use of SYSENTER/SYSEXIT instructions |
Test SYSENTER/ SYSEXIT - bochs has no clue | |
ProcessorSYSCpatch | for allowing SYSCALL/SYSRET |
ProcessorTLBpatch | replaces InvalidatePage with a more efficient version |
SetInterruptEntry | sets and IDT entry |
SetInterruptVector | sets the vector for interrupt handling |
SetPageEntry | Loads a page table with a given entry |
SetTaskRegisters | set GPRs for a given task |
Interrupt Handlers | |
write proper abort handlers | |
int_nm | handles #NM and switches context for FPU state |
fninit bug in 486 and Pentium I series | no-wait instructions can cause an exception in compatibility mode (NE=0) not sure wether we need this as native exceptions seem to be supported in 386+ (it requires mobo circuitry though, might need testing) |
thread status checking | |
Scheduler | |
scheduler_table | points to the schedule tables for each processor |
scheduler_offset | current offsets into the task table |
scheduler_curtask | contains the current threads for each processor in TDT/CR3 |
scheduler_curfpu | Contains the physical address for the TDT currently using the FPU state |
scheduler_fpusolv | contains the address of a page that is to contain the TDT when mapped into memory |
scheduler_sleeptask | Points to a page containing not-scheduled (blocked) tasks |
scheduler_ticks | contains the amount of ticks generated since system startup |
scheduler_cr0_ts | local copy of CR0 with TS set |
System calls | |
KernelVersion | Returns the kernel version |
MemMap | Maps a certain region in physical space to a region in kernel space |
BlockAlloc | Allocates memory and maps it to userspace |
race condition on address space allocation | pagetable r/w should be altered so that we can allocate pages for use. |
GetTimerTicks | Returns the amount of interrupts the kernel timer has generated |
PortAlloc | Requests access to a range of ports |
GetCpuInfo | Returns processor name, version and capabilities, independent of CPUID and known cpu bugs. |
CreateThread | Creates, Allocates and starts a new thread |
RouteAlloc | Requests a name to be bound to a port |
RouteFind | Requests to find a gate |
GateAlloc | Requests a new gate to be given |
GateLookup | Looks up the information of the handler behind a gate |
CreateV8086Thread | Creates, Allocates and starts a new thread |
sys_kernelswitch | Jump table for the kernel functions |
sys_checkprivileges | lookup table for required privileges |
sys_privilegeswitch | jump table for kernel calls |
Locks | |
semPageTable | Taken for write lock on page tables |
semPortLo | Taken for write lock on lower range port table |
semPortHi | Taken for write lock on upper range port table |
semBlockedTask | Taken for write access on the blocked task list |
semScheduler | Taken for write access to a scheduler’s list |
semTaskRoutes | taken for read or write access to the task route map |
ticTaskRoutes | ticket counter for the task route map |
trnTaskRoutes | turn counter for the task route map |
Globals | |
lpgRamdisk | Ramdisk address |
lpgTextPtr | Text video memory address |
lpgKernelEndPage | First page after the kernel section |
lpgMemtable | Location of memory allocation table in memory |
lpgLocalPagedir | Page Directory Mirror address |
lpgLocalPagemap | Physical to linear map of the page directory |
lpgPageResolver | Temporary location for unpositioned page directories |
lpgKernelpager | Location of kernel page table |
lpgKernelHole | location of free paging space after kernel stuff |
lpgKernelTDT | location of kernel TDT |
lpgTaskState | TSS Address |
lpgGdtLimit | Global Descriptor Table limit |
lpgGdtOffset | Global Descriptor Table offset |
lpgIdtLimit | Interrupt Descriptor Table limit |
lpgIdtOffset | Interrupt Descriptor Table offset |
lpgIdtAllocators | Location of interrupt owners |
lpgIOMapOffset | Location of port allocation tables |
lpgTSSDescriptor | Kernel thread TSS descriptor |
lpgTSSaddress | Kernel thread TSS address |
lpgSchedulerIndex | Scheduler index page |
lpgTickCount | Amount of milliseconds since kernel initialisation |
lpgLAPICBase | Base address for the onboard local APIC |
lpgProcessorcount | amount of Processors in the system |
lpgBootstrapApic | the APIC ID of the bootstrap processor |
lpgProcessorType | the installed processor type |
lpgProcessorVersion | the packed version word, 4 bits a number (x.x.x.x) |
lpgProcessorName | processor type |
lpgProcessorCaps | the installed processor’s capabilities |
lpgTaskRoutes | pointer to an name-to-port table |
lpgTaskHandlers | pointer to a task-lookup table |
allocates a gate and assigns a handler to it
EBX | CR3 of the gate’s handler |
EDI | the handler’s function |
EDX | Gate number allocated |
ECX
adds a new thread to the scheduler’s list
ESI | CR3 of the thread to be added |
EDI | TDT location |
EAX | processor to schedule to |
CF | clear on success, set on failure |
ECX
adds a new thread to the blocked list
ESI | CR3 of the thread to be added |
EDI | TDT location |
CF | clear on success, set on failure |
EAX ECX
adds a name to port lookup to the table
EAX | port number |
EDX | route name |
CF | clear on success, set on failure |
none
attempts to allocate an IDT entry of choice
EBX EDX
Allocates a free slot in the global descriptor table and loads it with the given parameters
EAX EBX ECX EDX
Allocates and writes a TSS entry in the GDT
EAX EBX ECX EDX
allocates memory from the table, preferrably from the beginning
none
none
allocates memory from the table, preferrably from the end
none
none
Allocate one unit of free memory from a given table
none
Attempts to allocate a port in the lower 2k area
EDX = requested port
CF = clear on success
none
Attempts to allocate a port (4) in the higher area
EDX = requested port
CF = clear on success
none
allocates a task set (descriptor + kernel stack page)
ECX, ESI
allocates a task set (descriptor + kernel stack page)
ECX, ESI
load first userspace module and schedule it for execution
the file is named ‘STAGE4’ and is considered a flat binary and is loaded and executed at the 4mb mark
none
none
all non-stack registers
Loads a page table with a given entry
EAX EBX EDX
Get FPU prescence, detect its type then configure the kernel accordingly
none
none
EAX
Gets processor info and configures it accordingly
none
none
all non-stack registers
Looks up a route in the route table and returns
EDX | route name |
EAX | route port, or 0 on failure |
none
Allocates and fills an IO handout table
Only 16k ports can be accessed. (Some ISA cards dont decode the top bits anyway)
The first 2k ports are fine grained to one address (0x0000-0x0800). The remaining 14k ports are handed out in doubleword accesses (4 consecutive addresses). A short integer is used to count the amount of allocations for the given port. This requires 1x 4k page for the low addresses and 2x 4k pages for the high addresses, adding to 12k of memory (with some redundancy)
PIT, PIC1 and PIC2 are claimed by default
none
none
all non-stack registers
prepares the data structures needed for inter-process communication
right now that only involves allocating two pages and zeroing them
none
none
all non-task registers
constructs the page and allocation tables needed for a paged system
memory
none
none
all non-stack registers
Brings the PIC into a known state
maps the irq’s 0-15 to the non-intel area
none
none
EAX, EBX
Brings the PIT into a known state
programs channel 0 to a ~100hz rate generator some bioses do not pre-load the pit to 18hz rate making this step necessary to get a regular interrupt issued on those systems
Builds a Task Description Table for the kernel
none
none
all non-task registers
Loads a page table with a given entry
none
EAX EBX EDX
loads a page table with a given entry, marking it global
none
EAX EBX EDX
loads a page table with a given entry, marking it for userspace
none
EAX EBX EDX
loads a page table with a given entry, marking it both uncacheable and for userspace
none
EAX EBX EDX
flushes a page from the TLB. On 386’s it has to flush the entire TLB
patched by <ProcessorTLBPatch>
none
none
for installing the workaround for the intel LOCK CMPXCHG8B bug
none
none
none
changes the #NM interrupt handler to fault (using self-modifying code) instead of switching fpu contexts
none
none
none
replaces InvalidatePage with a more efficient version
patch highly recommended on 486 or better patch breaks 386s
none
none
none
sets and IDT entry
none
none
sets the vector for interrupt handling
none
unknown
Loads a page table with a given entry
none
EAX EBX EDX
set GPRs for a given task
none
none
handles #NM and switches context for FPU state
Patched by <processorNoFPpatch>
interrupt handler
no-wait instructions can cause an exception in compatibility mode (NE=0) not sure wether we need this as native exceptions seem to be supported in 386+ (it requires mobo circuitry though, might need testing)
scheduler_curtask: TIMES 8 * 2 DD 0
contains the current threads for each processor in TDT/CR3
scheduler_curfpu: TIMES 8 DD 0
Contains the physical address for the TDT currently using the FPU state
scheduler_fpusolv: TIMES 8 DD 0
contains the address of a page that is to contain the TDT when mapped into memory
scheduler_sleeptask:DD 0
Points to a page containing not-scheduled (blocked) tasks
Returns the kernel version
Informational
none
Maps a certain region in physical space to a region in kernel space
Use with caution as this function does not perform any sanity checks
Kernel
EAX EDX
Allocates memory and maps it to userspace
Returns the amount of memory actually mapped
User
EAX EDX
pagetable r/w should be altered so that we can allocate pages for use. Right now its either checked inconsistently or blindly written
Returns the amount of interrupts the kernel timer has generated
User
EAX = 0x00030000
EAX = number of ticks occurred
none
Requests access to a range of ports
Driver
EAX | 0x00040000 |
EDI | First port to be opened |
EBX | Amount of ports to be opened |
EAX | Amount of ports allocated. Success if EAX >= EBX |
unknown
Returns processor name, version and capabilities, independent of CPUID and known cpu bugs.
Data is generated by GetProcessorInfo
Informational
EAX | 0x00050000 |
EBX | CPU number (zero based) |
EBX | name 0-3 |
EDX | name 4-7 |
ECX | name 8-11 |
EAX | first set of capabilities |
ESI | second set of capabilities |
EDI | packed processor version 24-31: model shortcut 15-12: type 11-08: family 07-04: model 03-00: revision |
CF | clear if present, set if not present if set, the rest of the output is not changed |
none
Creates, Allocates and starts a new thread
User level
EAX | 0x000 |
EBX | Address of the thread to be started |
EDI | Linear Address where the information is to be stored60000 |
EAX | amount of bytes allocated |
CF | clear on success, set on failure |
ECX ESI
Requests a name to be bound to a port
Driver
EAX | 0x00070000 |
EBX | Route to be allocated |
EDI | Port to point at |
CF | clear on success, set on failure |
EAX EDX
Requests to find a gate
User
EAX | 0x00080000 |
EBX | Name to check |
EAX | gate associated with the requested name, or 0 if it wasnt found |
EDX
Requests a new gate to be given
User
EAX | 0x00090000 |
EDI | Pointer to the handler function |
EAX | number of the acquired gate |
ECX EDX
Looks up the information of the handler behind a gate
User
EAX | 0x000A0000 |
EBX | gate number |
EAX | Address Space Handle of the handler |
EBX | Pointer of the handler function |
EDX
points to the schedule tables for each processor
scheduler_table: TIMES 8 DD 0
current offsets into the task table
scheduler_offset: TIMES 8 DD 0
contains the current threads for each processor in TDT/CR3
scheduler_curtask: TIMES 8 * 2 DD 0
Contains the physical address for the TDT currently using the FPU state
scheduler_curfpu: TIMES 8 DD 0
contains the address of a page that is to contain the TDT when mapped into memory
scheduler_fpusolv: TIMES 8 DD 0
Points to a page containing not-scheduled (blocked) tasks
scheduler_sleeptask:DD 0
contains the amount of ticks generated since system startup
scheduler_ticks: DD 0
local copy of CR0 with TS set
scheduler_cr0_ts: DD 0
Taken for write lock on page tables
semPageTable: DB 1
Taken for write lock on lower range port table
semPortLo: DB 1
Taken for write lock on upper range port table
semPortHi: DB 1
Taken for write access on the blocked task list
semBlockedTask: DB 1
Taken for write access to a scheduler’s list
semScheduler: DB 0xff
taken for read or write access to the task route map
semTaskRoutes: DB 1
ticket counter for the task route map
ticTaskRoutes: DW 0
turn counter for the task route map
trnTaskRoutes: DW 0
Ramdisk address
lpgRamdisk: DD 0
Text video memory address
lpgTextPtr: DD 0
First page after the kernel section
lpgKernelEndPage: DD 0
Temporary location for unpositioned page directories
lpgPageResolver: DD 0
location of free paging space after kernel stuff
lpgKernelHole: DD 0
location of kernel TDT
lpgKernelTDT: DD 0
TSS Address
lpgTaskState: DD 0
Global Descriptor Table limit
lpgGdtLimit: DW 0xffff
Global Descriptor Table offset
lpgGdtOffset: DD 0
Interrupt Descriptor Table limit
lpgIdtLimit: DW 0x7ff
Interrupt Descriptor Table offset
lpgIdtOffset: DD 0
Location of interrupt owners
lpgIdtAllocators: DD 0
Location of port allocation tables
lpgIOMapOffset: DD 0
Kernel thread TSS descriptor
lpgTSSDescriptor: DW 0
Kernel thread TSS address
lpgTSSaddress: DD 0
Scheduler index page
lpgSchedulerIndex: DD 0
Amount of milliseconds since kernel initialisation
lpgTickCount: DD 0, 0
Base address for the onboard local APIC
lpgLAPICBase: DD 0
amount of Processors in the system
lpgProcessorcount: DB 1
the APIC ID of the bootstrap processor
lpgBootstrapApic: DB 0
the installed processor type
lpgProcessorType: DB 0
the packed version word, 4 bits a number (x.x.x.x)
lpgProcessorVersion:DW 0
processor type
lpgProcessorName: DB "UndefinedCPU"
the installed processor’s capabilities
lpgProcessorCaps: DD 0
pointer to an name-to-port table
lpgTaskRoutes: DD 0
pointer to a task-lookup table
lpgTaskHandlers: DD 0