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:
- Machine-independent Facilities: Types and constants describing machine-dependent information
such as word size and page size. For example, types are provided which, if used properly, allow
machine-independent code to compile easily on both 32-bit and 64-bit architectures. Also, functions are
provided for various generic operations such as primitive multiprocessor synchronization and efficient
bit field manipulation.
- (X86) Generic Low-level Definitions: Header files describing x86 processor data structures and registers,
as well as functions to access and manipulate them. Includes:
- Bit definitions of the contents of the flags, control, debug, and floating point registers.
- Inline functions and macros to read and write the flags, control, debug, segment registers, and
descriptor registers (IDTR, GDTR, LDTR, TR).
- Macros to read the Pentium timestamp counter (useful for fine-grained timing and benchmarking)
and the stack pointer.
- Structure definitions for architectural data structures such as far pointers, segment and gate
descriptors, task state structures, floating point save areas, and page tables, as well as generic
functions to set up these structures.
- Symbolic definitions of the processor trap vectors.
- Macros to access I/O ports using the x86’s in and out instructions.
- Assembly language support macros to smooth over the differences in target object formats, such
as ELF versus a.out.
- (X86 PC) Generic Low-level Definitions: Generic definitions for standard parts of the PC
architecture, such as IRQ assignments, the programmable interrupt controller (PIC), and the keyboard
controller.
- (X86) Processor Identification and Management: Functions to identify the CPU and available features,
to enter and leave protected mode, and to enable and disable paging.
- (X86) Base Environment Setup: Functions that can be used individually or as a unit to set up a basic,
minimal kernel execution environment on x86 processors: e.g., a minimal GDT, IDT, TSS, and kernel page
tables.
- (X86 PC) Base Environment Setup: Functions to set up a PC’s programmable interrupt controller (PIC)
and standard IRQ vectors, to manage a PC’s low (1MB), middle (16MB) and upper memory, and to provide
simple non-interrupt-driven console support.
- (X86 PC) MultiBoot Startup: Complete startup code to allow the kernel to be booted from
any MultiBoot-compliant boot loader easily. Includes code to parse options and environment
variables passed to the kernel by the boot loader, and to find and use boot modules loaded with the
kernel.
- (X86 PC) Raw BIOS Startup: Complete startup code for boot loaders and other programs that need to
be loaded directly by the BIOS at boot time. This startup code takes care of all aspects of switching
from real to protected mode and setting up a 32-bit environment, and provides mechanisms
to call back to 16-bit BIOS code by running the BIOS in either real mode or v86 mode (your
choice).
- (X86 PC) DOS Startup: This startup code is similar to the BIOS startup code, but it expects to be loaded
in a 16-bit DOS environment: useful for DOS-based boot loaders, DOS extenders, or prototype kernels that
run under DOS. Again, this code fully handles mode switching and provides DOS/BIOS callback
mechanisms.
- Kernel Debugging Facilities: A generic, machine-independent remote GDB stub is provided which
supports the standard serial-line GDB protocol. In addition, machine-dependent default trap handling and
fault-safe memory access code is provided to allow the debugging stub to be used “out of the box” on x86
PCs.
- Kernel Annotation Facility: Macros and functions to associate additional information with ranges of kernel
text or data. Annotations allow, for example, a kernel to mark a range of kernel text so that a special function
is invoked whenever an exception or interrupt occurs within that range. This facility is useful for implementing
rollback routines.
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
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