Chapter 15
Kernel Support Library: liboskit_kern.a

15.1 Introduction

The kernel support library, libkern.a, supplies a variety of functions and other definitions that are primarily of use in OS kernels. (In contrast, the other parts of the OSKit are more generic components useful in a variety of environments including, but not limited to, OS kernels.) The kernel support library contains all the code necessary to create a minimal working “kernel” that boots and sets up the machine for a generic “OS-friendly” environment. For example, on the x86, the kernel support library provides code to get into protected mode, set up default descriptor tables, etc. The library also includes a remote debugging stub, providing convenient source-level debugging of the kernel over a serial line using GDB’s serial-line remote debugging protocol. As always, all components of this library are optional and replaceable, so although some pieces may be unusable in some environments, others should still work fine.

15.1.1 Machine-dependence of code and interfaces

This library contains a much higher percentage of machine-dependent code than the other libraries in the toolkit, primarily because this library deals with heavily machine-dependent facilities such as page tables, interrupt vector tables, trap handling, etc. The library attempts to hide some machine-dependent details from the OS by providing generic, machine-independent interfaces to machine-dependent library code. For example, regardless of the architecture and boot loading mechanism in use, the kernel startup code included in the library always sets up a generic C-compatible execution environment and starts the kernel by calling the well-known main routine, just as in ordinary C programs. However, the library makes no attempt to provide a complete architecture-independence layer, since such a layer would have to make too many assumptions about the OS that is using it. For example, although the library provides page table management routines, these routines have fairly low-level, architecture-specific interfaces.

15.1.2 Generic versus Base Environment code

The functionality provided by the kernel support library is divided into two main classes: the generic support code, and the base environment. The generic support contains simple routines and definitions that are almost completely independent of the particular OS environment in which they are used: for example, the generic support includes symbolic definitions for bits in processor registers and page tables, C wrapper functions to access special-purpose processor registers, etc. The generic support code should be usable in any OS that needs it.

The base environment code, on the other hand, is somewhat less generic in that it is designed to create, and function in, a well-defined default or “base” kernel execution environment. Out of necessity, this code makes more assumptions about how it is used, and therefore it is more likely that parts of it will not be usable to a particular client OS. For example, on the x86 architecture, the base environment code sets up a default global descriptor table containing a “standard” set of basic, flat-model segment descriptors, as well as a few extra slots reserved for use by the client OS. This “base GDT” is likely to be sufficient for many kernels, but may not be usable to kernels that make more exotic uses of the processor’s GDT. In order to allow piecemeal replacement of the base environment as necessary, the assumptions made by the code and the intermodule dependencies are clearly documented in the sections covering the base environment code.

15.1.3 Road Map

Following is a brief summary of the main facilities provided by the library, indexed by the section numbers of the sections describing each facility:

15.2 Machine-independent Facilities

This section includes machine-independent types, constants, macros, and functions that every supported architecture provides. These are used extensively within the OSKit itself as well as by the applications built on the OSKit.

15.2.1 page.h: Page size definitions

SYNOPSIS

#include <oskit/machine/page.h>

DESCRIPTION

This file provides of following symbols, which define the architectural page size of the architecture for which the OSKit is configured:

PAGE_SIZE:
The number of bytes on each page. It can always be assumed to be a power of two.
PAGE_SHIFT:
The number of low address bits not translated by the MMU hardware. PAGE_SIZE is always 2PAGESHIFT.
PAGE_MASK:
A bit mask with the low-order PAGE_SHIFT address bits set. Always equal to PAGESIZE - 1. WARNING: Some systems (like linux) define this to be  ~ (PAGESIZE - 1), be careful that the definitions match what the code expects!

In addition, the following macros are provided for convenience in performing page-related manipulations of addresses:

atop(addr ):
Converts a byte address into a page frame number, by dividing by PAGE_SIZE.
ptoa(page ):
Converts a page frame number into an integer (oskit_addr_t) byte address, by multiplying by PAGE_SIZE.
round_page(addr ):
Returns addr rounded up to the next higher page boundary. If addr is already on a page boundary, it is returned unchanged.
trunc_page(addr ):
Returns addr rounded down to the next lower page boundary. If addr is already on a page boundary, it is returned unchanged.
page_aligned(addr ):
Evaluates to true (nonzero) if addr is page aligned, or false (zero) if it isn’t.

Note that many modern architectures support multiple page sizes. On such architectures, the page size defined in this file is the minimum architectural page size, i.e., the finest granularity over which the MMU has control. Since there seems to be no sufficiently generic and useful way that this header file could provide symbols indicating which “other” page sizes the architecture supports, making good use of larger pages probably must be done in machine-dependent code.

Some operating systems on some architectures do not actually support the minimum architectural page size in software; instead, they aggregate multiple architectural pages together into larger “logical pages” managed by the OS software. On such operating systems, it would be inappropriate for general OS or application code to use the PAGE_SIZE value provided by oskit/page.h, since this value would be smaller (more fine-grained) than the OS software actually supports, and therefore inappropriate. However, this is purely a high-level OS issue; like other parts of the toolkit, no one is required to use this header file if it is inappropriate in a particular situation.

This file was originally derived from Mach’s vm_param.h.

15.2.2 spin_lock.h: Spin locks

SYNOPSIS

#include <oskit/machine/spin_lock.h>

DESCRIPTION

This file provides the architecture-dependent definition of the spin_lock_t “spin lock” data type and associated manipulation macros. This facility provides a basic locking mechanism which can be used with preemptive threading or on a multi-processor.

spin_lock_t:
Typedef for the spin lock data type.
spin_lock_init(s ):
Initialize a spin lock.
spin_lock_locked(s ):
Check if a spin lock is locked.
spin_unlock(s ):
Unlock a spin lock.
spin_try_lock(s ):
Attempt to lock a spin lock. Returns 0 if successful, nonzero if unsuccessful.
spin_lock(s ):
Busy wait until the lock is free. On return the lock has been acquired.

This header file is taken from CMU’s Mach kernel.

15.2.3 queue.h: Generic queues

SYNOPSIS

#include <oskit/queue.h>

struct queue_entry {

  struct queue_entry *next; /* next element */
  struct queue_entry *prev; /* previous element */

};

typedef struct queue_entry *queue_t;
typedef struct queue_entry queue_head_t;
typedef struct queue_entry queue_chain_t;
typedef struct queue_entry *queue_entry_t;

DESCRIPTION

Macros and structures for implementation of a “queue” data type. The implementation uses a doubly-linked list and supports operations to insert and delete anywhere in the list.

queue_init(q ):
Initialize the given queue.
queue_first(q ):
Returns the first entry in the queue.
queue_next(q ):
Returns the entry after an item in the queue.
queue_last(q ):
Returns the last entry in the queue.
queue_prev(q ):
Returns the entry before an item in the queue.
queue_end(q, qe ):
Tests whether a new entry is really the end of the queue.
queue_empty(q ):
Tests whether a queue is empty.
queue_enter(q, elt, type, field ):
Insert a new element at the tail of the queue.
queue_enter_first(head, elt, type, field ):
Insert a new element at the head of the queue.
queue_enter_before(head, nelt, elt, type, field ):
Insert a new element before the indicated element.
queue_enter_after(head, pelt, elt, type, field ):
Insert a new element after the indicated element.
queue_remove(head, elt, type, field ):
Remove an arbitrary item from the queue.
queue_remove_first(head, entry, type, field ):
Remove and return the entry at the head of the queue.
queue_remove_last(head, entry, type, field ):
Remove and return the entry at the tail of the queue.
queue_assign(to, from, type, field ):
Move an element in a queue to a new piece of memory.
queue_iterate(head, elt, type, field ):
Iterate over each item in the queue. Generates a ‘for’ loop, setting elt to each item in turn (by reference).

This header file is taken from CMU’s Mach kernel.

15.2.4 debug.h: debugging support facilities

SYNOPSIS

#include <oskit/debug.h>

DESCRIPTION

This file contains simple macros and functions to assist in debugging. Many of these facilities are intended to be used to “annotate” programs permanently or semi-permanently in ways that reflect the code’s proper or desired behavior. These facilities typically change their behavior depending on whether the preprocessor symbol DEBUG is defined: if it is defined, then extra code is introduced to check invariants and such; when DEBUG is not defined, all of this debugging code is “compiled out” so that it does not result in any size increase or efficiency loss in the resulting compiled code.

The following macros and functions are intended to be used as permanent- or semi-permanent annotations to be sprinkled throughout ordinary code to increase its robustness and clarify its invariants and assumptions to human readers:

assert(cond ):
This is a standard assert macro, like (and compatible with) the one provided in oskit/c/assert.h. If DEBUG is defined, this macro produces code that evaluates cond and calls panic (see Section 14.8.3) if the result is false (zero). When an assertion fails and causes a panic, the resulting message includes the source file name and line number of the assertion that failed, as well as the text of the cond expression used in the assertion. If DEBUG is not defined, this macro evaluates to nothing (an empty statement), generating no code.

Assertions are typically used to codify assumptions made by a code sequence, e.g., about the parameters to a function or the conditions on entry to or exit from a loop. By placing explicit assert statements in well-chosen locations to verify that the code’s invariants indeed hold, a thicker “safety net” is woven into the code, which tends to make bugs manifest themselves earlier and in much more obvious ways, rather than allowing incorrect results to “trickle” through the program’s execution for a long time, sometimes resulting in completely baffling behavior. Assertions can also act as a form of documentation, clearly describing to human readers the exact requirements and assumptions in a piece of code.

otsan():
If DEBUG is defined, this macro unconditionally causes a panic with the message “off the straight and narrow!,” along with the source file name and line number, if it is ever executed. It is intended to be placed at code locations that should never be reached if the code is functioning properly; e.g., as the default case of a switch statement for which the result of the conditional expression should always match one of the explicit case values. If DEBUG is not defined, this macro evaluates to nothing.
do_debug(stmt ):
If DEBUG is defined, this macro evaluates to stmt; otherwise it evaluates to nothing. This macro is useful in situations where an #ifdef DEBUG ... #endif block would otherwise be used over just a few lines of code or a single statement: it produces the same effect, but is smaller and less visually intrusive.

The following macros and functions are primarily intended to be used as temporary scaffolding during debugging, and removed from production code:

void dump_stack_trace(void):
This function dumps a human-readable backtrace of the current function call stack to the console, using printf. The exact content and format of the printed data is architecture-specific; however, the output is typically a list of instruction pointer or program counter values, each pointing into a function on the call stack, presumably to the return point after the function call to the next level. You can find out what function these addresses reside in by running the Unix nm utility on the appropriate executable file image, sorting the resulting symbol list if necessary, and looking up the address in the sorted list. Alternatively, for more precise details, you can look up the exact instruction addresses in a disassembly of the executable file, e.g., by using GNU objdump with the ‘-d’ option.
here():
This macro generates code that simply prints the source file name and line number at which the macro was used. This macro can be extremely useful when trying to nail down the precise time or code location at which a particular bug manifests itself, or to determine the sequence of events leading up to it. By sprinkling around calls to the here macro in appropriate places, the program will dump regular status reports of its location every time it hits one of these macros, effectively producing a log of “interesting” events (“interesting” being defined according to the placement of the here macro invocations). Using the here macro this way is equivalent to the common practice of sprinkling printf’s around and watching the output, except it is easier because the here invocation in each place does not have to be “tailored” to make it distinguishable from the other locations: each use of the here macro is self-identifying.

If DEBUG is not defined, the here macro is not defined at all; this makes it obvious when you’ve accidentally left invocations of this macro in a piece of code after it has been debugged.

debugmsg(printfargs ):
This macro is similar to here, except it allows a formatted message to be printed along with the source file name and line number. printfargs is a complete set of arguments to be passed to the printf function, including parentheses: for example, ‘debugmsg(("foo is %d", foo));’. A newline is automatically appended to the end of the message. This macro is generally useful as a wrapper for printf for printing temporary run-time status messages during execution of a program being debugged.

As with here, if DEBUG is not defined, the debugmsg macro is not defined at all, in order to make it obvious if any invocations are accidentally left in production code.

Note that only panic and dump_stack_trace are real functions; the others are simply macros.

15.2.5 base_critical: simple critical section support

SYNOPSIS

#include <oskit/base_critical.h>

void base_critical_enter(void);

void base_critical_leave(void);

DESCRIPTION

Functions to implements a simple “global critical region.” These functions are used throughout the OSKit to ensure proper serialization for various “touchy” but non-performance critical activities such as panicing, rebooting, debugging, etc. This critical region can safely be entered recursively; the only requirement is that enters match exactly with leaves.

The implementation of this module is machine-dependent, and generally disables interrupts and, on multiprocessors, grabs a recursive spin lock.

15.3 (X86) Generic Low-level Definitions

This section covers useful macros, definitions, and routines that are specific to the Intel x86 processor architecture but that are independent of the interrupt control, bus structure, and other ancillary functions traditionally associated with a “PC.” Those facilities are covered in section 15.4.

15.3.1 asm.h: assembly language support macros

SYNOPSIS

#include <oskit/x86/asm.h>

DESCRIPTION

This file contains convenience macros useful when writing x86 assembly language code in AT&T/GAS syntax. This header file is directly derived from Mach, and similar headers are used in various BSD kernels.

Symbol name extension: The following macros allow assembly language code to be written that coexists with C code compiled for either ELF or a.out format. In a.out format, by convention an underscore (_) is prefixed to each public symbol referenced or defined by the C compiler; however, the underscore prefix is not used in ELF format.

EXT(name ):
Evaluates to _name in a.out format, or just name in ELF. This macro is typically used when referring to public symbols defined in C code.
LEXT(name ):
Evaluates to _name : in a.out format, or name : in ELF. This macro is generally used when defining labels to be exported to C code.
SEXT(name ):
Evaluates to the string literal "_name " in a.out format, or "name " in ELF. This macro can be used in GCC inline assembly code, where the code is contained in a string constant; for example: asm("...; call "SEXT(foo)"; ...");

Alignment: The following macros relate to alignment of code and data:

TEXT_ALIGN:
Evaluates to the preferred alignment of instruction entrypoints (e.g., functions or branch targets), as a power of two. Currently evaluates to 4 (16-byte alignment) if the symbol i486 is defined, or 2 (4-byte alignment) otherwise.
ALIGN:
A synonym for TEXT_ALIGN.
DATA_ALIGN:
Evaluates to the preferred minimum alignment of data structures. Currently it is always defined as 2, although in some cases a larger value may be preferable, such as the processor’s cache line size.
P2ALIGN(alignment ):
Assembly language code can use this macro to work around the fact that the .align directive works differently in different x86 environments: sometimes .align takes a byte count, whereas other times it takes a power of two (bit count). The P2ALIGN macro always takes a power of two: for example, P2ALIGN(2) means 4-byte alignment. By default, the P2ALIGN macro uses the .p2align directive supported by GAS; if a different assembler is being used, then P2ALIGN should be redefined as either .align alignment or .align 1<<(alignment ), depending on the assembler’s interpretation of .align.

XXX S_ARG, B_ARG, frame stuff, . . .

XXX need to make the macros more easily overridable, using ifdefs.

XXX need to clean out old trash still in the header file

XXX IODELAY macro

15.3.2 eflags.h: Processor flags register definitions

SYNOPSIS

#include <oskit/x86/eflags.h>

DESCRIPTION

XXX

This header file can be used in assembly language code as well as C. The flags defined here correspond the the ones in the processor databooks.

EFL_CF:
carry
EFL_PF:
parity of low 8 bits
EFL_AF:
carry out of bit 3
EFL_ZF:
zero
EFL_SF:
sign
EFL_TF:
trace trap
EFL_IF:
interrupt enable
EFL_DF:
direction
EFL_OF:
overflow
EFL_IOPL:
IO privilege level mask. All 0’s is the same as EFL_IOPL_KERNEL, while all 1’s (or just EFL_IOPL) is the same as EFL_IOPL_USER.
EFL_NT:
nested task
EFL_RF:
resume without tracing
EFL_VM:
virtual 8086 mode
EFL_AC:
alignment check
EFL_VIF:
virtual interrupt flag
EFL_VIP:
virtual interrupt pending
EFL_ID:
CPUID instruction support

15.3.3 proc_reg.h: Processor register definitions and accessor functions

SYNOPSIS

#include <oskit/x86/proc_reg.h>

DESCRIPTION

XXX

This header file contains the definitions for the processor’s control registers (CR0, CR4). It also contains macros for getting and setting the processor registers and flags. There is also a macro for reading the processor’s cycle counter (on Pentium and above processors).

This header file is taken from CMU’s Mach kernel.

15.3.4 debug_reg.h: Debug register definitions and accessor functions

SYNOPSIS

#include <oskit/x86/debug_reg.h>

DESCRIPTION

This provides the definitions for the processor’s built-in debug registers. There are also inline functions that allow the hardware-assisted breakpoints to be set.

DR0 through DR3 are the breakpoint address registers; DR6 is the status register, and DR7 is the control register.

get_dr0():
Returns the value in breakpoint address register 0.
get_dr1():
Returns the value in breakpoint address register 1.
get_dr2():
Returns the value in breakpoint address register 2.
get_dr3():
Returns the value in breakpoint address register 3.
get_dr6():
Returns the value in the debug status register.
get_dr7():
Returns the value in the debug control register.
set_dr0(val ):
Sets the value in breakpoint address register 0 to val.
set_dr1(val ):
Sets the value in breakpoint address register 1 to val.
set_dr2(val ):
Sets the value in breakpoint address register 2 to val.
set_dr3(val ):
Sets the value in breakpoint address register 3 to val.
set_dr6(val ):
Sets the value in the debug status register to val.
set_dr7(val ):
Sets the value in the debug control register to val.
set_b0(unsigned addr, unsigned len, unsigned rw ):
Enables breakpoint register 0. Sets dr0 to LINEAR address addr and updates dr7 to enable it. rw must be DR7_RW_INST, DR7_RW_WRITE, DR7_RW_IO, or DR7_RW_DATA indicating the condition to break on. len must be DR7_LEN_1, DR7_LEN_2, or DR7_LEN_4, indicating how many bytes are covered by the register.
set_b1(unsigned addr, unsigned len, unsigned rw ):
Enables breakpoint register 1.
set_b2(unsigned addr, unsigned len, unsigned rw ):
Enables breakpoint register 2.
set_b3(unsigned addr, unsigned len, unsigned rw ):
Enables breakpoint register 3.

15.3.5 fp_reg.h: Floating point register definitions and accessor functions

SYNOPSIS

#include <oskit/x86/fp_reg.h>

DESCRIPTION

XXX

This file contains the structure definition for saving the floating-point state and then restoring it. It also contains definitions for the x87 control and status registers.

This header file is taken from CMU’s Mach kernel.

15.3.6 far_ptr.h: Far (segment:offset) pointers

SYNOPSIS

#include <oskit/x86/far_ptr.h>

DESCRIPTION

This contains struct definitions for creating “far pointers.” Far pointers on the x86 are those that take an explicit segment in addition to the offset value.

struct far_pointer_16:
16-bit pointer structure which contains a 16-bit segment and a 16-bit offset. The address is computed as segment กก 4 + offset.
struct far_pointer_32:
48-bit pointer which contains a 32-bit offset and a 16-bit segment descriptor. Segmentation is used to determine what linear address is generated by these pointers.

15.3.7 pio.h: Programmed I/O functions

SYNOPSIS

#include <oskit/x86/pio.h>

DESCRIPTION

These are macros for accessing IO-space directly on the x86. These instructions will generate traps if executed in user-mode without permissions (either IOPL in the eflags register or access via the io-bitmap in the tss).

iodelay():
A macro used to delay the processor for a short period of time, generally to wait until programmed io can complete. The actual amount of time is indeterminate, since the delay is accomplished by doing an inb from a nonexistent port, which depends on the processor and chipset.1 The nominal delay value is 1uS for most machines.
inl(port ):
Returns 32-bit value from port
inw(port ):
Returns 16-bit value from port
inb(port ):
Returns 8-bit value from port
inl_p(port ):
inl followed immediately by iodelay
inw_p(port ):
inw followed immediately by iodelay
inb_p(port ):
inb followed immediately by iodelay
outl(port, val ):
Send 32-bit val out port.
outw(port, val ):
Send 16-bit val out port.
outb(port, val ):
Send 8-bit val out port.
outl_p(port ):
outl followed immediately by iodelay
outw_p(port ):
outw followed immediately by iodelay
outb_p(port ):
outb followed immediately by iodelay

The above macros have versions that begin with i16_, which are defined to be the same. It may be desirable to use the i16_ versions in 16-bit code in place of the normal macros for clarity.

This header file is taken from CMU’s Mach kernel.

15.3.8 seg.h: Segment descriptor data structure definitions and constants

SYNOPSIS

#include <oskit/x86/seg.h>

DESCRIPTION

XXX

struct x86_desc:
Normal segment descriptors.
struct x86_gate:
Trap, interrupt, and call gates.
struct pseudo_descriptor:
Used to load the IDT and GDT (and LDT).
sel_idx(sel):
Converts the selector into an index in the descriptor table.
ISPL(s):
Returns the selector’s privilege level.
USERMODE(s, f):
KERNELMODE(s, f):
fill_descriptor(struct x86_desc *desc, unsigned base, unsigned limit, unsigned char access, unsigned char sizebits):
Fill a segment descriptor.
fill_descriptor_base(struct x86_desc *desc, unsigned base):
Set the base address in a segment descriptor.
fill_descriptor_limit(struct x86_desc *desc, unsigned limit):
Set the limit in a segment descriptor.
fill_gate(struct x86_gate *gate, unsigned offset, unsigned short selector, unsigned char access, unsigned char word_count):
Fill an x86 gate descriptor.

This header file is based on a file in CMU’s Mach kernel.

15.3.9 gate_init.h: Gate descriptor initialization support

SYNOPSIS

#include <oskit/x86/gate_init.h>

DESCRIPTION

This file contains the C structures and assembly-language macro definitions used to build x86 gate descriptor tables suitable for use by gate_init (see Section 15.5.10).

struct gate_init_entry:
C structure describing a gate descriptor.
GATE_INITTAB_BEGIN(name):
Starts assembly-language definition of a gate descriptor table.
GATE_ENTRY(n, entry, type):
Initializes an element of a gate descriptor table.
GATE_INITTAB_END:
Defines the end of a gate descriptor table.

The assembly-language macros are designed to be used while writing trap entrypoint routines. See oskit/libkern/x86/base_trap_inittab.S for example code that uses this facility.

15.3.10 trap.h: Processor trap vectors

SYNOPSIS

#include <oskit/x86/trap.h>

DESCRIPTION

XXX

This contains the definitions for the trap numbers returned by the processor when something goes ‘wrong’. These can be used to determine the cause of the trap.

T_DIVIDE_ERROR:
T_DEBUG:
T_NMI:
non-maskable interrupt
T_INT3:
T_OVERFLOW:
overflow test
T_OUT_OF_BOUNDS:
bounds check
T_INVALID_OPCODE:
T_NO_FPU:
T_DOUBLE_FAULT:
T_FPU_FAULT:
T_INVALID_TSS:
T_SEGMENT_NOT_PRESENT:
T_STACK_FAULT:
T_GENERAL_PROTECTION:
T_PAGE_FAULT:
T_PF_PROT: protection violation; T_PF_WRITE: write access; T_PF_USER: from user state
T_FLOATING_POINT_ERROR:
T_ALIGNMENT_CHECK:
T_MACHINE_CHECK:

This header file is taken from CMU’s Mach kernel.

15.3.11 paging.h: Page translation data structures and constants

DESCRIPTION

XXX

This header file is derived from Mach’s intel/pmap.h.

15.3.12 tss.h: Processor task save state structure definition

SYNOPSIS

#include <oskit/x86/tss.h>

         struct x86_tss {
                 int     back_link;      /* previous task's segment, if nested */
                 int     esp0;           /* initial stack pointer ... */
                 int     ss0;            /* and segment for ring 0 */
                 int     esp1;           /* initial stack pointer ... */
                 int     ss1;            /* and segment for ring 1 */
                 int     esp2;           /* initial stack pointer ... */
                 int     ss2;            /* and segment for ring 2 */
                 int     cr3;            /* CR3 - page table physical address */
                 int     eip;            /* eip */
                 int     eflags;         /* eflags */
                 int     eax;            /* eax */
                 int     ecx;            /* ecx */
                 int     edx;            /* edx */
                 int     ebx;            /* ebx */
                 int     esp;            /* current stack pointer (ring 3) */
                 int     ebp;            /* ebp */
                 int     esi;            /* esi */
                 int     edi;            /* edi */
                 int     es;             /* es */
                 int     cs;             /* cs */
                 int     ss;             /* current stack segment (ring 3) */
                 int     ds;             /* ds */
                 int     fs;             /* fs */
                 int     gs;             /* gs */
                 int     ldt;            /* local descriptor table segment */
                 unsigned short  trace_trap;     /* trap on switch to task */
                 unsigned short  io_bit_map_offset; /* offset to IO perm bitmap */
         };
         
DESCRIPTION

XXX

XXX only the 32-bit version

This contains the definition of a 32-bit tss. The tss used by the 80286 is incompatible with this.

This header file is taken from CMU’s Mach kernel.

15.4 (X86 PC) Generic Low-level Definitions

XXX

This section covers useful macros, definitions, and routines for “PC”-specific features.

15.4.1 irq_list.h: Standard hardware interrupt assignments

SYNOPSIS

#include <oskit/x86/pc/irq_list.h>

DESCRIPTION

XXX

Many of the interrupt vectors have pre-defined uses. The rest of them can be assigned to ISA, PCI, or other devices. This file contains the ‘defined’ interrupt usages.

15.4.2 pic.h: Programmable Interrupt Controller definitions

SYNOPSIS

#include <oskit/x86/pc/pic.h>

DESCRIPTION

XXX

This contains definitions for the 8259(A) Programmable Interrupt Controller (PIC). In addition to numerous constants, it also contains the prototypes for several functions and macros.

pic_init(unsigned char master_base, unsigned char slave_base):
MASTER_PIC_BASE and SLAVES_PIC_BASE are also defined, and may be passed in as parameters.
pic_disable_irq(unsigned char irq):
pic_enable_irq(unsigned char irq):
pic_test_irq(unsigned char irq):
pic_enable_all:
pic_disable_all:
pic_ack(irq):

15.4.3 keyboard.h: PC keyboard definitions

SYNOPSIS

#include <oskit/x86/pc/keyboard.h>

DESCRIPTION

XXX

This header file contains the register definitions for the PC keyboard. The port addresses are defined, along with the status and control bits. This would be used by a keyboard device driver, or someone manipulating they keyboard directly. (ie, to turn on and off the keyboard LEDs).

This header file is taken from CMU’s Mach kernel.

15.4.4 rtc.h: NVRAM Register locations

SYNOPSIS

#include <oskit/x86/pc/rtc.h>

DESCRIPTION

This file is taken from FreeBSD (XXX cite?) and contains definitions for the standard NVRAM, or Real Time Clock, register locations.

rtcin(unsigned char addr):
Returns the 8-bit value from location addr.
rtcout(unsigned char addr, unsigned char val):
Writes val to location addr.

15.5 (X86) Processor Identification and Management

15.5.1 cpu_info: CPU identification data structure

SYNOPSIS

#include <oskit/x86/cpuid.h>

struct cpu_info {

  unsigned stepping : 4; /* Stepping ID */
  unsigned model : 4; /* Model */
  unsigned family : 4; /* Family */
  unsigned type : 2; /* Processor type */
  unsigned feature_flags; /* Features supported */
  char vendor_id[12]; /* Vendor ID string */
  unsigned char cache_config[16]; /* Cache information */

};
DESCRIPTION

This structure is used to hold identification information about x86 processors, such as information returned by the CPUID instruction. The cpuid toolkit function, described below, fills in an instance of this structure with information about the current processor.

Note that it is expected that the cpu_info structure will continue to grow in the future as new x86-architecture processors are released, so client code should not depend on this structure in ways that will break if the structure’s size changes.

The family field describes the processor family:

CPU_FAMILY_386:
A 386-class processor.
CPU_FAMILY_486:
A 486-class processor.
CPU_FAMILY_PENTIUM:
A Pentium-class (“586”) processor.
CPU_FAMILY_PENTIUM_PRO:
A Pentium Pro-class (“686”) processor.

The type field is one of the following:

CPU_TYPE_ORIGINAL:
Original OEM processor.
CPU_TYPE_OVERDRIVE:
OverDrive upgrade processor.
CPU_TYPE_DUAL:
Dual processor.

The feature_flags field is a bit field containing the following bits:

CPUF_ON_CHIP_FPU:
Set if the CPU has a built-in floating point unit.
CPUF_VM86_EXT:
Set if the virtual 8086 mode extensions are supported, i.e., the VIF and VIP flags register bits, and the VME and PVI bits in CR4.
CPUF_IO_BKPTS:
Set if I/O breakpoints are supported, i.e., the DR7_RW_IO mode defined in x86/debug_reg.h.
CPUF_4MB_PAGES:
Set if 4MB superpages are supported, i.e., the INTEL_PDE_SUPERPAGE page directory entry bit defined in x86/paging.h.
CPUF_TS_COUNTER:
Set if the on-chip timestamp counter and the RDTSC instruction are available.
CPUF_PENTIUM_MSR:
Set if the Pentium model specific registers are available.
CPUF_PAGE_ADDR_EXT:
Set if the Pentium Pro’s page addressing extensions (36-bit physical addresses and 2MB pages) are available.
CPUF_MACHINE_CHECK_EXCP:
Set if the processor supports the Machine Check exception (vector 18, or T_MACHINE_CHECK in x86/trap.h).
CPUF_CMPXCHG8B:
Set if the processor supports the CMPXCHG8B instruction (also known as “double-compare-and-swap”).
CPUF_LOCAL_APIC:
Set if the processor has a built-in local APIC (Advanced Programmable Interrupt Controller), for symmetric multiprocessor support.
CPUF_MEM_RANGE_REGS:
Set if the processor supports the memory type range registers.
CPUF_PAGE_GLOBAL_EXT:
Set if the processor supports the global global paging extensions, i.e., the INTEL_PDE_GLOBAL page table entry bit defined in x86/paging.h.
CPUF_MACHINE_CHECK_ARCH:
Set if the processor supports Intel’s machine check architecture and the MCG_CAP model-specific register.
CPUF_CMOVCC:
Set if the processor supports the CMOVcc instructions.

The cpuid.h header file also contains symbolic definitions for other constants such as the cache configuration descriptor values; see the header file and the Intel documentation for details on these.

15.5.2 cpuid: identify the current CPU

SYNOPSIS

#include <oskit/x86/cpuid.h>

void cpuid([out] struct cpu_info *out_info);

DESCRIPTION

This function identifies the CPU on which it is running using Intel’s recommended CPU identification procedure, and fills in the supplied structure with the information found.

Note that since the cpuid function is 32-bit code, it wouldn’t run on anything less than an 80386 in the first place; therefore it doesn’t bother to check for earlier processors.

PARAMETERS
out_info:
The CPU information structure to fill in.

15.5.3 cpu_info_format: output a cpu_info structure in ASCII form

SYNOPSIS

#include <oskit/x86/cpuid.h>

void cpu_info_format(struct cpu_info *info, void (*formatter)(void *data, const char *fmt, ...), void *data);

DESCRIPTION

This function takes the information in a cpu_info structure and formats it as human-readable text. The formatter should be a pointer to a printf-like function to be called to format the output data. The formatter function may be called multiple times to output all the relevant information.

PARAMETERS
info:
The filled-in CPU information structure to output.
formatter:
The printf-style formatted output function to call. It will be called with the opaque data pointer provided, a standard C format string (fmt), and optionally a set of data items to format.
data:
An opaque pointer which is simply passed on to the formatter function.

15.5.4 cpu_info_min: return the minimum feature set of two CPU information structures

SYNOPSIS

#include <oskit/x86/cpuid.h>

void cpu_info_min(struct cpu_info *id1, struct cpu_info *id2);

DES