Subversion Repositories shark

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

\documentclass[a4paper]{report}
\usepackage{epsfig}
\usepackage{latexsym}
\usepackage[english]{babel}
\selectlanguage{english}
\textheight=8.4 truein
\textwidth=6.1 truein
\oddsidemargin=0.2 truein

\newenvironment{proof}
        {\vspace{12pt} \noindent {\bf Proof.} \\
        \noindent}{$\Box$ \vspace{12pt}}

\title{The OSLib Manual}

\author{Luca Abeni
\and
        Gerardo Lamastra
}

\begin{document}
\setlength{\baselineskip}{1.5\baselineskip}
\maketitle
\begin{abstract}
This is the manual of OSLib, a collection of low level functions aimed
to help programmers in developing system software, ranging from small
programs for embedded systems to complex Operating System
kernels. Using OSLib, the system programmer can focus on the software
itself, without caring about the interaction with the hardware.  In
this sense, OSLib is similar to the Flux OS Toolkit.

From another point of view, OSLib provides an ``easy'' access to the
hardware, without introducing any useless abstraction, and can be seen
as a generic support layer for any operating system service. In this
sense, it is similar to the MIT ExoKernel .  Note that OSLib does not
force to use any particular OS structure, but can support any
conventional or innovative structure, such as the monolithic one, the
microkernel-based one, or the vertical structured one.
\end{abstract}

\chapter{Introduction
}
The OSLib is a collection of routines and data structures developed to
help system programmers in implementing an OS or an application that
directly accesses the hardware. In this sense it is
similar to the Windows NT Hardware Abstraction Layer (HAL), the
$\mu$Choices NanoKernel, or the HARTIK Virtual Machine (VM) Layer. On
the other hand, the aim of the OSLib is not to abstract the
hardware resources (like the cited works do).  In fact, hardware
resources abstraction can result in poor efficiency and flexibility,
as stated by Engler et others (ExoKernel). The OSLib code, instead of
abstracting hardware, provides a secure and easy access to it hiding
implementation details (for example, the PIC or PIT programming, or the
CPU tables management)
and leaving to the OS developers the hi-level and conceptual part of
the work.
%%%%% RIVEDERE!!!! CHIARIRE UN PO'
%%% Add reference to Flux and ExoKernels...
% These are the ideas: Flux is too complex (COM... What an orror!!!),
% ExoKernel exterminates too much abstractions :) ... And bound to a specific
% system structure (vertical structured system)!!!

\section{Library Structure}
OSLib is composed of some libraries, that can be compiled using
the standard GNU tools (gcc, GNU binutils and GNU make) either under
MSDOS or Linux (DJGPP for DOS and
gcc for Linux have been successfully tested; gcc for other Unix
systems or Cygnus gcc for Windows have not been tested yet, but
will probably work too). The resulting GNUCoff or ELF MultiBoot compliant
images can be loaded using a custom provided DOS eXtender (X) or
using the GNU Grand Unified Boot Loader (GRUB).

The code is organized in three parts: \begin{itemize}
\item the hardware support library ({\tt xlib}), used to access the PC
        hardware;
\item a subset of the OS independent part of the standard C library
        ({\tt libc1});
\item the Kernel support library ({\tt kl}), that is the component to
        use for writing OS code.
\end{itemize
}

The hardware support library contains the boot code for starting up
the system when a MultiBoot compliant loader is used, the code to
access hardware structures such as the GDT, IDT, the interrupt
controller, the code to detect the CPU, and some data structures
containing informations about the system.
%%%% ALLUNGARE LA DESCRIZIONE, SPIEGARE BENE... FARE RIFERIMENTO AI FILE .h

The OS independent part of the C library provides all the functions
from libc that can be implemented without invoking system calls
(typically the string management functions, the memory
copy/move/compare, the math functions and similar). An important
exception to this rule is represented by the {\tt cprintf} function:
it is similar to the standard {\tt printf} function (with the
difference that {\tt cprintf} directly writes to the screen), and,
since it needs to access the video memory, it depends by the OS (in
particular, {\tt cprintf} depends on how the OS remaps the video
memory). In any case, since the OS code needs to output some
informations for debugging or other purposes, this function is
provided by {\tt libc1}. {\tt libc1} does not provide any input
function.

The Kernel support library provides: \begin{itemize}
\item the code for interrupt/exception handling;
\item the code for thread management (thread creation/deletion,
        context switch...);
\item the code for address space management
\item the code for time management
\end{itemize}

\section{Compiling and Using}
The OSLib code is distributed as source code in a ZIP or tarball
archive. The tarball contains the source to be compiled in a Unix
system (only  {\tt chr(10)} at the end of each line), while the ZIP
archive can be decompressed in MSDOS ({\tt chr(13)+chr(10)}) or Unix
source using the -a option of UNZIP.

In order to decompress the source tree, use {\tt tar -xvzf llxxx.tgz}
or {\tt unzip -La llxxx.zip}; this command will create the tree shown in
Figure \ref{fig:tree}.

\begin{figure
}
%\begin{minipage}[t]{10cm}
\begin{tt}
\begin{tabbing}
oslib--\=------ll--\=------i386         \\
\>      | \>            |                       \\
\>      | \>            |                       \\
\>      |---lib \>      |----sys----ll          \\
\>      |                                       \\
\>      |---xlib \>                             \\
\>      | \>                                    \\
\>      |---libc \>                             \\
\>      | \>                                    \\
\>      |---libm \>                             \\
\>      |                                       \\
\>      |---kl                                  \\
\>      |                                       \\
\>      |---examples                            \\
\>      |                                       \\
\>      |---mk                                  \\
\end{tabbing}
\end{tt
}
%\end{minipage}
\caption{The OSLib source tree.}
\label{fig:tree}
\end{figure}

The {\tt ll} directory contains the header files with the
definitions of the OSLib structures and the prototypes for the OSLib calls.
It is organized in two subdirectories: the {\tt i386} directory,
containing the include files for the hardware support library, and
the {\tt sys/ll} directory, containing the headers for the OS support
library.

The {\tt lib} directory is the place where all the libraries
will be put once compiled. Depending on the
distribution, the {\tt lib} directory is in the archive or will be
created at compilation time by the {\tt make} command.

The {\tt xlib} directory contains the sources for the hardware
support  library; the {\tt libm} directory is used to compile a
modified version of the FreeBSD math library provided with OSLib; the
{\tt libc} directory contains the sources for the minimal C library,
while the {\tt kl} directory is the place where the Kernel support library
source code resides.

The {\tt examples} directory contains some examples
showing how to use all the functionalities provided by OSLib.

The {\tt mk} library contains some configuration files, used to
compile the libraries in different host OSs: currently, the files
to compile under MDSOS (using DJGPP V1 \& V2) and the file to compile
under Linux are provided. Moreover, a file to compile the H4 (S.Ha.R.K.)
kernel is provided.

In order to compile the system, proceed as follows
\begin{itemize}
        \item Configure the compiling system, installing the
                correct configuration file: copy the correct {\tt mk/*.mk}
                file in {\tt config.mk}
        \item make all the libraries, from directories {\tt xlib},
                {\tt libc}, {\tt libm}, and {\tt kl}:
               
                \begin{minipage}[t]{10cm}
                \begin{tt}
                \begin{tabbing}
                cd xlib                                 \\
                make install                            \\
                cd ..                                   \\
                cd libc                                 \\
                make install                            \\
                cd ..                                   \\
                cd libm                                 \\
                $\ldots$
                \end{tabbing}
                \end{tt}
                \end{minipage}
        \item now the libraries are installed, and you are ready to
                use them. In order to test OSLib, you can compile
                the programs in the {\tt examples} directory:

                \begin{minipage}[t]{10cm}
                \begin{tt}
                \begin{tabbing}
                cd examples                             \\
                make
                \end{tabbing}
                \end{tt}
                \end{minipage}
\end{itemize}

A program compiled using the OSLib code can be run using the DOS
eXtender, or using GRUB. In order to use the extender, boot MSDOS (or
a 16 bit DOS compatible OS, such as FreeDOS), then copy {\tt X.EXE}
in the path, and finally use it: {\tt X <program name>} (for example,
try {\tt X schedtest.xtn}). Once the program execution is terminated,
it will nicely return to DOS.

In order to run a program through GRUB, put it in a GRUB accessible
partition, then boot GRUB and enter the command prompt pressing
{\tt `c'}. Now, specify the compiled program as a kernel: assuming
that you want to run {\tt schedtest.xtn}, residing in the
{\tt /oslib/examples} directory on the first partition of your first
hard drive, you have to type {\tt kernel=(hd0,0)/oslib/examples/schedtest.xtn}.
Finally, you can run the program typing {\tt boot}. Once the program
finishes, the system is halted and you must reboot it.

The OSLib code can be used including the adequate headers
from the {\tt include} directory, and linking the libraries from the
{\tt lib} directory. The compiler and linker options are set in the
{\tt config.mk} file: look at the makefile in the {\tt examples} directory
to see how to use it. The {\tt examples} directory contains
some simple programs to be browsed in order to learn how to use OSLib.

\chapter{The libraries}
As said, the OSLib code and data structures are organized in various
libraries, in order to increase the modularity and simplify the
structure. A description of those libraries and of the header files
that have to be included in order to use OSLib follows.

\section{The header files}
The {\tt ll} directory contains the header files to be included for
using OSLib. In particular, the directory structure tries to reflect
the standard POSIX include directory. Hence, the include files of the
minimal C library use the POSIX names and are distributed in {\tt ll}
and {\tt ll/sys}.

The {\tt xlib} headers are in the {\tt ll/i386} directory; in particular,
they are:
\begin{itemize}
        \item {\tt ll/i386/hw-data.h
}: this header defines the basic data
                types and constants, and has to be included in order to use
                them
                %do we need to cite all them?
        \item {\tt ll/i386/hw-instr.h}: this header defines the instruction
                needed to access hardware registers (or memory locations) as
                inlined functions
        \item {\tt ll/i386/hw-func.h}: this header has to be included in
                order to use the functions that permits to directly access
                hardware, such as {\tt halt()} and {\tt reboot()},
                {\tt IDT\_place()}, {\tt GDT\_place()}  and
                {\tt GDT\_read()}, {\tt addr2linear()}, and the
                {\tt ll\_context\_*()} functions. Moreover, it contains the
                prototypes of {\tt x\_init()}, {\tt x\_end()},
                {\tt x\_exc\_bind()}, and {\tt x\_irq\_bind()}
        \item {\tt ll/i386/hw-arch.h}: this header has to be included for
                using the CPU/FPU detection functions {\tt X86\_get\_CPU()},
                {\tt  X86\_get\_FPU()}, {\tt  X86\_is386()},
                {\tt X86\_isCyrix()}, and {\tt X86\_hasCPUID()}
        \item {\tt ll/i386/hw-io.h}: this header defines the I/O
                instructions as inline functions. It is included by
                {\tt ll/i386/hw-instr.h} and must never be directly
                included by user programs
        \item {\tt ll/i386/tss-ctx.h}: defines some macros for translating
                CONTEXTs in TSSs and vice-versa
        \item {\tt ll/i386/sel.h}: defines some symbolic names for the
                predefined selectors used by the {\tt xlib}
        \item {\tt ll/i386/int.h}: defines some macros that simplify
                writing interrupt or exception handlers
        \item {\tt ll/i386/pit.h}: this header has to be included in order
                to access the Programmable Interrupt Timer (PIT) through
                {\tt pit\_init()}, {\tt pit\_setconstant()}, and
                {\tt pit\_read()}
        \item {\tt ll/i386/pic.h}: this header has to be included to
                access the Programmable Interrupt Controller (PIC)
                through {\tt  PIC\_init()}, {\tt PIC\_end()},
                {\tt irq\_mask()}, and {\tt irq\_unmask()}
        \item {\tt ll/i386/linkage.h}: this header can be included by
                ASM files to generate correct C naming independently from
                the architecture/file format
        \item {\tt ll/i386/defs.h}: this header defines some macros to be
                used to start/end header and C files
        \item {\tt ll/i386/farptr.h}: this header contains the far pointer
                access from DJGPP
        \item {\tt ll/i386/x-bios.h}: this header has to be included to
                call real mode functions through {\tt X\_callBIOS()}, or
                {\tt vm86\_init()} and {\tt vm86\_callBIOS()}, or to
                directly communicate with the eXtender through
                {\tt x\_bios\_address()} and {\tt X\_meminfo()}
        \item {\tt ll/i386/x-dosmem.h}: this header has to be included in
                order to manage real-mode (DOS) memory through
                {\tt DOS\_mem\_init()}, {\tt DOS\_alloc()}, and
                {\tt DOS\_free()}
        \item {\tt ll/i386/x-dos.h}: this header has to be included in
                order to access a FAT file system through dos (using the
                X-BIOS calls) with {\tt DOS\_fopen()},
                {\tt DOS\_fclose()}, {\tt DOS\_fread()},
                {\tt DOS\_fwrite()}, and {\tt DOS\_error()}
        \item {\tt ll/i386/mb-hdr.h}: this header can be included by ASM
                files in order to easily generate a MultiBoot header
        \item {\tt ll/i386/mb-info.h}: this header contains the definition
                of the MultiBoot Information (MBI) structure.
\end{itemize
}

%Minimal libc includes...
%\item {\tt ll/time.h}:
%\item {\tt ll/string.h}:
%\item {\tt ll/stdio.h}:
%\item {\tt ll/math.h}:
%\item {\tt ll/limits.h}:
%\item {\tt ll/errno.h}:
%\item {\tt ll/assert.h}:
%\item {\tt ll/unistd.h}:
%\item {\tt ll/stdlib.h}:
%\item {\tt ll/stdarg.h}:
%\item {\tt ll/ctype.h}:
%\item {\tt ll/sys/types.h}:
%\item {\tt ll/sys/cdef.h}:
%\item {\tt ll/i386/mem.h}:
%\item {\tt ll/i386/stdio.h}:
%\item {\tt ll/i386/cons.h}:
%\item {\tt ll/i386/float.h}:
%\item {\tt ll/i386/stdlib.h}:
%\item {\tt ll/i386/string.h}:
%\item {\tt ll/i386/error.h}:
%\item {\tt ll/i386/limits.h}:

The {\tt kl} headers are in the {\tt ll/sys/ll} directory; in particular,
they are:
\begin{itemize}
        \item {\tt ll/sys/ll/ll-func.h}: this header has to be included for
                using the {\tt ll\_context\_create()},
                {\tt ll\_context\_setspace()}, and {\tt ll\_context\_delete()},
                {\tt ll\_init()}, {\tt ll\_end()}, and {\tt ll\_abort()}
                {\tt ll\_context\_save()}, {\tt ll\_context\_change()},
                {\tt ll\_context\_load()}, {\tt ll\_context\_from()}, and
                {\tt ll\_context\_to} functions
        \item {\tt ll/sys/ll/event.h}: this header has to be included for
                using the event related functions, that are
                {\tt event\_init()}, {\tt event\_post()},
                {\tt event\_delete()}, {\tt irq\_bind()}, and
                {\tt ll\_ActiveInt()}
        \item {\tt ll/sys/ll/time.h}: this header has to be included for
                using the {\tt gettime()} function. Moreover, it provides
                some macros for manipulating timespecs.
        \item {\tt ll/sys/ll/event.h}: this header has to be included for
                using Address Spaces. In particular, it provides prototypes
                and data definitions for the {\tt as\_init()},
                {\tt as\_create()}, and {\tt as\_bind()} functions.
\end{itemize}

\section{The Hardware Support Library}
The Hardware support library provides all the functions and data
structures needed to access the hardware. In particular, it provides
the code necessary to boot the OSLib application, to manage the CPU
and the PC hardware, and to switch to Real Mode calling BIOS functions.

The booting code and data permits to create MultiBoot compliant
executables and to interface the application with a MultiBoot compliant
boot loader.

The CPU handling code and data permits to identify the CPU type and to
manage the CPU tables (GDT and IDT), while the hardware managing code permits
to access some specific PC hardware (PIT and PIC).

First of all, some basic data types are defined (in {\tt ll/i386/hw-data.h}):
\begin{itemize}
        \item {\tt DWORD}: a 32 bit positive number;
        \item {\tt WORD}: a 16 bit positive number;
        \item {\tt BYTE}: an 8 bit positive number.
\end{itemize}
For each of these types, a {\tt MAX\_*} constant exists.

Based on the basic types, some important structures are defined:
\begin{itemize}
        \item {\tt struct gate}: the x86 gate structure;
        \item {\tt struct descriptor}: an x86 segment descriptor;
        \item {\tt union gdt\_entry}: an x86 GDT entry: can be a
                gate or a descriptor;
        \item {\tt struct tss}: an x86 Task descriptor.
\end{itemize}
All the constant that can be useful for initializing those structures are
also defined. See Intel manuals for some explanations.

These are the functions provided by the hardware library to manage
the CPU:

\noindent {\tt void int X86\_get\_CPU(struct ll\_cpuInfo *p)}

This function identifies the CPU present in the system and reports
its characteristics in the {\tt ll\_cpuInfo} structure whose pointer is
passed as an input. The {\tt ll\_cpuInfo} is described in Figure
\ref{fig:cpuinfo}.
\begin{figure
}
%\begin{minipage}[t]{15cm}
\begin{tt}
\begin{tabbing}
struct ll\_cpuInfo \= \{                                        \\
\>      DWORD X86\_cpu;                                         \\
\>      DWORD X86\_cpuIdFlag;                                   \\
\>      DWORD X86\_vendor\_1;                                   \\
\>      DWORD X86\_vendor\_2;                                   \\
\>      DWORD X86\_vendor\_3;                                   \\
\>      DWORD X86\_signature;                                   \\
\>      DWORD X86\_IntelFeature\_1;                             \\
\>      DWORD X86\_IntelFeature\_2;                             \\
\>      DWORD X86\_StandardFeature;                             \\
\}
\end{tabbing}
\end{tt
}
%\end{minipage}
\label{fig:cpuinfo}
\caption{The cpuInfo structure.}
\end{figure}

Basically, {\tt X86\_get\_CPU()} checks if the CPU is a 386, a 486, or
better; in the last case, it uses the {\tt cpuid} instruction to obtain
more information, otherwise it will use some custom code to determine the
manufacturer. In particular, it is based on the following functions:
\noindent {\tt void int X86\_is386(void)}                       \\
\noindent {\tt void int X86\_isCyrix(void)}                     \\
\noindent {\tt void int X86\_hasCPUID(void)}                    \\
It is recommended to invoke them through {\tt X86\_get\_CPU()
}, but
sometime it can be useful to call one of the previous functions directly.

Similar code exists for detecting and initializing the FPU, but it still
need some work.
%void X86_get_FPU(void); FIX THIS STORY!!! check_fpu() ???

Some other functions are provided to manage the most important CPU tables:

\noindent {\tt void
GDT\_place(WORD sel, DWORD base, DWORD lim, BYTE acc, BYTE gran)}

This function permits to insert a new entry in the GDT; {\tt sel}
is the selector (equal to the entry number multiplied by
{\em sizeof(struct gdt\_entry)}), {\tt base} and {\tt lim} are the
base and the limit of the segment described by the entry (and identified
by {\tt sel}), while {\tt acc} and {\tt gran} are the access and granularity
bytes. They can be set using the constants defined in
{\tt ll/i386/hw-data.h}.

\noindent {\tt DWORD GDT\_read(WORD sel, DWORD *lim, BYTE *acc, BYTE *gran)}

This function permits to read an entry from the GDT, returning the
base of the segment identified by the descriptor {\tt sel}. Moreover,
if {\tt lim}, {\tt acc}, and {\tt gran} are not null, the limit, the
access byte and the granularity of the segment are stored in
{\tt *lim}, {\tt *acc}, and {\tt *gran}.

{\tt LIN\_ADDR addr2linear(WORD selector, DWORD offset)}

This function can be used to translate an {\tt <selector>:<offset>} address
in a 32bit linear address. It uses {\tt GDT\_read} for obtaining the
base of the segment identified by {\tt selector}.

\noindent {\tt void IDT\_place(BYTE num,void (*handler)(void))}

This function permits to insert an entry in the IDT; {\tt num} is the
number of the interrupt, whereas {\tt handler
} is a pointer to the
code that has to be specified as an handler for that interrupt.

%multiprogramming --> These are described in the KL...
%CONTEXT ll_context_save(void);
%void    ll_context_change(CONTEXT c);
%void    ll_context_load(CONTEXT c);
%CONTEXT ll_context_from(void);
%void    ll_context_to(CONTEXT c);
%
%PIC Stuff...           --> TODO: documentation...
%void PIC_init(void);
%void PIC_end(void);
%void irq_mask(WORD irqno);
%void irq_unmask(WORD irqno);
After these very low level functionalities, the hardware support library
provides some other facilities:

\noindent{\tt void *x\_init(void)}

This function initializes a minimal programming environment, defining a
standard handler for all the CPU exceptions and hardware interrupts (through
{\tt IDT\_place()}), identifying the CPU type, initializing the FPU, setting
up a TSS for executing the code, and initializing the PIC.

Moreover, it returns a pointer to the MultiBoot Information structure (see
Figure \ref{fig:MBI}).

\noindent{\tt x\_exc\_bind(int i, void (*f)(int n))}
\noindent{\tt x\_irq\_bind(int i, void (*f)(int n))}

These two functions permit to associate a handler to a CPU exception or to
a hardware interrupt. Note that the handler is a C function, using the C
conventions for passing the parameters. Since these two functions are based
on the programming environment initialized by {\tt x\_init()}, they can be
used only {\em after} that {\tt x\_init()} has been called.

\noindent{\tt void x\_end(void)}
Restores the hardware settings to the DOS standards: it must be called on
shutdown if the execution is expected to return to a 16bit OS.

Some other functions can be used for accessing the PIT:

\noindent{\tt int pit\_init(BYTE c, BYTE m, WORD tconst)}

This function initializes the PIT channel {\tt c} (with {\tt c = 0},
{\tt 1}, or {\tt 2}) in mode {\tt m}, with the initial value of the
counter equal to {\tt tconst}. Returns a value {\tt < 0} on error (if
the requested channel does not exist).

\noindent{int pit\_setconstant(BYTE c, WORD val)}

This function sets the PIT channel {\tt c}'s counter to {\tt val}.
Returns a value {\tt < 0} on error (if the requested channel does not
exist).

\noindent{\tt WORD pit\_read(BYTE channel)}

This function reads the value of the counter of {\tt channel}.

Other functions permit to call real mode interrupts, either returning to
real mode or using the VM86 mode. Some of these functions are used to
access a FAT file system using DOS (they works only if the application is
invoked through the eXtender). See {\tt ll/i386/x-*.h
} for more details.

%BIOS
%X_CALLBIOS * x_bios_address(void);
%void X_meminfo(LIN_ADDR *b1,DWORD *s1,LIN_ADDR *b2,DWORD *s2);
%void X_callBIOS(int service,X_REGS16 *in,X_REGS16 *out,X_SREGS16 *s);
%void vm86_init();
%int vm86_callBIOS(int service,X_REGS16 *in,X_REGS16 *out,X_SREGS16 *s);

%DOS
%DOS_FILE *DOS_fopen(char *name, char *mode);
%void DOS_fclose(DOS_FILE *f);
%DWORD DOS_fread(void *buf,DWORD size,DWORD num,DOS_FILE *f);
%DWORD DOS_fwrite(void *buf,DWORD size,DWORD num,DOS_FILE *f);
%unsigned DOS_error(void);

%void DOS_dump_mem(void);
%void DOS_mem_init(void);
%LIN_ADDR DOS_alloc(DWORD s);
%int DOS_free(LIN_ADDR p,DWORD s);

Some other functions directly remap the corresponding ASM
instructions (these functions are implemented by the {\tt xlib}):
\begin{itemize}
\item {\tt cli()}
\item {\tt sti()}
\item {\tt halt()}
\item {\tt clts()}
\item {\tt BYTE inp(WORD port)}
\item {\tt WORD inw(WORD port)}
\item {\tt DWORD ind(WORD port)}
\item {\tt void outp(WORD port, BYTE data)}
\item {\tt void outw(WORD port, WORD data)}
\item {\tt void outd(WORD port, DWORD data)}
\end{itemize}
the following two functions can be used instead of {\tt cli()} and
{\tt sti()}:
\begin{itemize}
\item {\tt SYS\_FLAGS ll\_fsave(void)}: performs a {\tt cli}, and return the
                previous value of the flags register;
\item {\tt void ll\_frestore(SYS\_FLAGS f)}: restores the flags register to
                {\tt f}; can be used instead of a {\tt sti()}.
\end{itemize}

Moreover, the library provides some inline functions for reading and
writing some CPU registers' values:
\begin{itemize}
        \item {\tt get\_CS()}
        \item {\tt get\_DS()}
        \item {\tt get\_FS()}
        \item {\tt get\_SP()}
        \item {\tt get\_BP()}
        \item {\tt get\_TR()}: returns the Task Register value
        \item {\tt set\_TR()}: sets the Task Register value
        \item {\tt set\_LDTR()}: sets the LDT address
\end{itemize}

\section{The Kernel Support Library}
The Kernel support library allows an OS developer to write interrupt
handlers, binding them to hardware interrupts, to create threads and
perform context switches, to crate address spaces and assign them to
threads, and to manage the time.

Time management consists in reading time, and assigning execution
time to threads through an event-based model. Hence, time management
is performed using {\em events}: an event permits to execute some code
(the event handler) at a specified time (the event rising time).
When an event raises the event handler is called (with interrupt disabled).

Interrupts are managed in a similar way, allowing the programmer to
specify the event handler for a special event that will raise when the
hardware interrupt arrives.

Using the event mechanism it is easy to implement {\em temporal protection}
(enforcing that a thread will never require too much execution time), while
spatial protection is provided by OSLib through {\em Address Spaces}. An
address space is a very basic abstraction encapsulating user data and code.
Address Spaces are implemented using x86 segments: each Address Space is a
different segment. If the user code uses only the default data and code
selectors, the code running in an address space can not access other address
spaces. As a default, OSLib provides a ``flat'' address space, mapping
$1 \rightarrow 1$ all the physical memory.

Here is a list of the functions provided by {\tt kl}:

\noindent {\tt void *ll\_init(void)}

This library function is used to initialize the Kernel Library: it
detects the CPU, initializes the FPU, and sets the interrupt and
exception handlers to default values.

\begin{figure
}
%\begin{minipage}[t]{15cm}
\begin{tt}
\begin{tabbing}
struct multiboot\_info \= \{                                            \\
/* MultiBoot info version number */                                     \\
\>      unsigned long flags;                                            \\
                                                                        \\
/* Available memory from BIOS */                                        \\
\>      unsigned long mem\_lower;                                       \\
\>      unsigned long mem\_upper;                                       \\
/* "root" partition */                                                  \\
\>      unsigned long boot\_device;                                     \\
                                                                        \\
/* Kernel command line */                                               \\
\>      unsigned long cmdline;                                          \\
                                                                        \\
/* Boot-Module list */                                                  \\
\>      unsigned long mods\_count;                                      \\
\>      unsigned long mods\_addr;                                       \\
                                                                        \\
\>      union \= \{                                                     \\
\>      \>      struct \= \{                                            \\
/* (a.out) Kernel symbol table info */                                  \\
\>      \>      \>      unsigned long tabsize;                          \\
\>      \>      \>      unsigned long strsize;                          \\
\>      \>      \>      unsigned long addr;                             \\
\>      \>      \>      unsigned long pad;                              \\
\>      \>      \} a;                                                   \\
\>      \>      struct \{                                               \\
/* (ELF) Kernel section header table */                                 \\
\>      \>      \>      unsigned long num;                              \\
\>      \>      \>      unsigned long size;                             \\
\>      \>      \>      unsigned long addr;                             \\
\>      \>      \>      unsigned long shndx;                            \\
\>      \>      \} e;                                                   \\
\>      \} syms;                                                        \\
/* Memory Mapping buffer */                                             \\
\>      unsigned long mmap\_length;                                     \\
\>      unsigned long mmap\_addr;                                       \\
/*                                                                      \\
Gerardo: I need to add also the physical address base for               \\
both low ( < 1MB) \& upper ( > 1MB) memory, as X starts from DOS        \\
which could have preallocated some of this memory...                    \\
For example, GRUB assumes that mem\_lowbase = 0x0 \&                    \\
mem\_upbase = 0x100000                                                  \\
 */                                                                      \\
\>      unsigned long mem\_lowbase;                                     \\
\>      unsigned long mem\_upbase;                                      \\
\};                                                                     \\
\end{tabbing}
\end{tt
}
%\end{minipage}
\caption{The MultiBoot Information structure.}
\label{fig:MBI}
\end{figure}
As output, {\tt ll\_init} returns informations about the environment
through a modified version of the MultiBoot Information (MBI)
structure (the returned value is a pointer to such a structure). The
MultiBoot Info structure is defined as shown in Figure \ref{fig:MBI}.

Refer to the MultiBoot documentation for more informations about the
fields behaviour. The only difference with the standard MultiBoot
Info structure is that a new flag {\tt MB\_INFO\_USEGDT} in the
{\tt flags} field is provided for informing that the program has been
loaded through a DOS Extender, and two new fields are added. If the
{\tt MB\_INFO\_USEGDT} is set, the two new fields {\tt mem\_lowbase}
and {\tt mem\_upbase} indicates the low memory and high memory
starting addresses, otherwise the standard values (respectively
$0x00$ and $0x100000$) have to be assumed.

\noindent{\tt CONTEXT ll\_context\_create(void (*entrypoint)(void *p),
                                BYTE *stack, void *parm, void (*killer)(void),
                                WORD control)}

This library function is used to create a new thread, allocating a
CPU context for it. A thread is defined as an independent flow of
execution and is characterized by the register set values, a private
stack (used to initialize the {\tt SS} and {\tt ESP} registers), and
an address space in which it executes. The code executed by a thread
is defined by a function called {\em thread body} that takes as input
a void pointer (passed at thread creation time).

The {\tt entrypoint} parameter is a pointer to the thread body, the
{\tt stack} parameter is a pointer to a preallocated chunk of memory
to be used as a stack for the new thread, while the {\tt parm}
parameter is a void pointer passed as parameter to the thread body
when the thread is created. The {\tt killer} parameter is a pointer
to a function that will be called on thread correct termination (a
thread terminates correctly when the execution arrives to the end of
the body function). The {\tt control} parameters defines some control
flags associated to the thread.

The function allocates a free CPU context and initializes the
register values using the passed parameters. The {\tt EIP} register
is initialized to {\tt entrypoint}, and {\tt ESP} is initialized
to {\tt stack}, whereas {\tt DS}, {\tt GS}, and {\tt FS} are
initialized to the default data segment and {\tt CS} is initialized to
the default code segment. As explained introducing Address Spaces, the
default code and data segments remap one-to-one all the system memory
(``flat'' Address Space). All the other registers are initialized to
standard values.

The return value is the  identifier of the initialized CPU context.

\noindent {\tt void ll\_context\_delete(CONTEXT c);}

This library function is used to free a CPU context when a thread is
terminated. The {\tt c} parameter is the identifier of the context to
be freed. Note that the stack memory has to be explicitly freed, since
{\tt ll\_context\_delete()} does not free it.

\noindent {\tt CONTEXT ll\_context\_save(void);}

This library function saves the CPU registers' values in the current
CPU context and returns its identifier. In other words, the context
associated to the thread executing when {\tt ll\_context\_save()} is
called is saved and its identifier is returned. It can be used to
implement context switches in OS primitives, as shown in the
following code:

\begin{minipage}[t]{15cm}
\begin{tt}
\begin{tabbing}
SYSCALL(mysyscall(...)) \=\                             \\
\{                                                      \\
\>      CONTEXT oldContext, newContext;                 \\
\>      $\ldots$                                        \\
                                                        \\
/* This must be the first primitive instruction */      \\
\>      oldContext = ll\_context\_save();               \\
\>      $\ldots$                                        \\
\>      OS primitive code                               \\
                                                        \\
/* This must be the last primitive instruction */       \\
\>      ll\_context\_load(newContext);                  \\
\};                                                     \\
\end{tabbing}
\end{tt}
\end{minipage}

{\bf Warning:} if the virtual context switch mechanism is used, this
function cannot be used (use {\tt ll\_context\_from()} instead).

\noindent {\tt void ll\_context\_load(CONTEXT c);}

This library call is used to load a new CPU context in the CPU, for
performing context switches, as shown in the example above (see
{\tt ll\_context\_save()}). Note that {\tt ll\_context\_load()} must
be called {\bf at the end} of a system call (immediately before re-enabling
interrupts); if a system programmer needs to perform a context switch
with interrupt disabled (in an event handler or in the middle of a
system call), the {\em virtual context switch} mechanism have to be used.
When virtual context switch is used, the context switch function only
stores the new context ID in a temporary variable and performs the
real context switch only when interrupts are enabled (see {\tt
ll\_context\_from()} and {\tt ll\_context\_to())}.

\noindent {\tt CONTEXT ll\_context\_from(void);}

This library function is similar to {\tt ll\_context\_save()}, but
can be called when the virtual context switch mechanism is used. In
this case it returns the ID of the last context that have been
selected to be loaded in the CPU.

\noindent {\tt void ll\_context\_to(CONTEXT c);}

This library selects a thread to be dispatched: if interrupts are
disabled and the context switch cannot be performed immediately, the
real context switch will happen as soon as possible (when interrupts
will be re-enabled). This is the {\tt virtual context switch}
mechanism.

{\tt ll\_context\_to()} is similar to {\tt ll\_context\_load()}, but
uses the virtual context switch mechanism; if interrupts are enabled,
they behave in the same manner.

\noindent {\tt void ll\_end(void);}

This function can be used in the shutdown code: if the application
was started through the DOS Extender, {\tt ll\_end()} resets the PIT
(and the rest of the hardware) to the standard DOS settings and
prepares the system to return to MSDOS, otherwise it simply halts the
system.

\noindent {\tt void ll\_abort(int code);}

This functions acts as safety place to go when any error occurs and
the OS does not know which context is active. The function loads a
safe context (with a safe stack), displays an error identified by
the {\tt code} parameter, and exits the OS support code
(see also {\tt ll\_end}).

\noindent {\tt void event\_init(struct ll\_initparms *l)}

This function sets the time management services up, by initializing
the event queue and programming the Programmable Interval Timer (PIT)
in a proper way. The PIT can be programmed in two
different modes: the periodic mode and the one-shot mode. In periodic
mode, the PIT is programmed to generate a timer interrupt each {\tt tick}
of $T$ $\mu$seconds, specified by the user through the {\tt l}
parameter. In one shot mode, the PIT is dynamically programmed to
generate an interrupt only when it is necessary to raise a
programmed event. It is worth noting that the PIT mode only
influences the error with which an event raises, but is invisible to
the OS (the PIT mode can be changed simply changing the {\tt event\_init()}
parameter, without any modify to the OS code).

The function takes as input a pointer {\tt l} to a {\tt ll\_initparms}
structure defined as follows:

\begin{minipage}[t]{15cm}
\begin{tt}
\begin{tabbing}
struct ll\_initparms \= \{      \\
\>      DWORD mode;             \\
\>      TIME tick;              \\
\};
\end{tabbing}
\end{tt}
\end{minipage}

The {\tt mode} field indicates the PIT mode ({\tt LL\_PERIODIC} or
{\tt LL\_ONESHOT}), while the {\tt tick} field indicates the tick size
(for periodic mode only) in $\mu$seconds.

\noindent {\tt TIME gettime(int mode, struct timespec *val)}

This function can be used to read the current system time. The system
time can be read using different levels of precision (and different
levels of overhead): currently only the {\tt TIME\_PTICK} and
{\tt TIME\_EXACT} modes are implemented. The {\tt TIME\_PTICK} mode
works only if the system timer is programmed in periodic mode, and
returns the system time in ticks. It is characterized by a low
overhead (small execution time). The {\tt TIME\_EXACT} mode reads the
exact system time and returns it measured in $\mu$seconds.

The {\tt mode} parameter can be {\tt TIME\_PTICK} or {\tt TIME\_EXACT}
and specifies the time reading mode; the {\tt val} parameter can
point to a {\tt timespec} structure that will be filled
with the current time ( if {\tt val != NULL}) .

This function returns the read time in $\mu$seconds, or $0$ if the
reading fails.

\noindent {\tt int event\_post(struct timespec *time,
                        void (*handler)(void *p), void *par)}

This function is used to create a new event, selecting an handler to
be called at the specified time passing an user provided parameter to
it. The {\tt handler} parameter specifies the event handler (the
function to be called when the event will raise), the {\tt time}
parameter indicates the time at which the event will raise, while
{\tt par} is a void pointer that will be passed as parameter to
the event handler.

The function returns the identifier of the created event, or -1 if an
error occurs (it can be due to the lack of free event
descriptors,  or to some other internal error). The event identifier
is used to refer the event for modifying or deleting it (see
{\tt event\_delete()}).

The OS support code programs the interrupt controller in a periodic
or one-shot mode (see {\tt event\_init()}) so that an interrupt will be
generated near to time {\tt time} to call the event handler. The
event handler is called as a response to the timer interrupt, with
interrupts disabled, hence it {\bf must
} execute for not too much time
(otherwise, interrupts will be left disabled for a long time). The
timer mode can affect the error  with which the event handler is
called, but the code must be independent  from the timer mode.
% CHIARIRE IL CONCETTO!!!!
% -- GLI EVENT HANDLER NON DEVONO PRENDERE TEMPO!!!
% -- IL MODO DI FUNZIONAMENTO DEL TIMER E' ``TRASPARENTE''!!!

\noindent {\tt int event\_delete(int index)}

This library function is used to delete a posted event, identified by
the {\tt index} parameter. It returns 1 in case of success, -1 in
case of failure.

\noindent {\tt int irq\_bind(int irq, void (*handler)(void *p), DWORD flags)}

This function can be used to associate an handler to an hardware
interrupt; each interrupt is converted by the support code in an
event, so the interrupt handler is identical to an event handler. The
function checks if the requested interrupt is free, and in this case
allocates it and assigns the handler to it. If the interrupt is
already allocated (is not free), that is, a handler has been already
associated to it, {\tt irq\_bind} returns an error and does nothing.

The {\tt irq} parameter specifies the interrupt number, while {\tt
handler} is a pointer to the interrupt event handler, and the {\tt
flags} parameter defines some flags associated to the interrupt. In
particular, the {\tt FORCE} flag can be used to set a handler for an
already allocated interrupt, and the {\tt PREEMPTABLE} flag specifies
that the handler can be called with interrupts enabled (interruptible
handler).

FLAGS: \begin{itemize}
                \item the {\tt PREEMPTABLE} flag permits to specify
                                that the handler can be called with
                                interrupts enabled (interruptible handler).
                \item the {\tt FORCE} flag can be used to set a handler for
                                an already allocated interrupt.
\end{itemize}

Interruptible handlers are useful to enhance system responsiveness,
reducing the time in which interrupts are disabled and allowing to
develop a preemptable OS. On the other hand, they must be used with
caution, since mutual exclusion is not guaranteed in an interruptible
handler.

The {\tt FORCE} flag can be useful for removing an interrupt handler
(use this flag with the {\tt handler} parameter set to {\tt NULL}.

\noindent {\tt int ll\_ActiveInt(void)}

This function returns the number of pending interrupts or event handlers.

\noindent {\tt void as\_init(void)}

This function initializes the Address Space management code. It must be
called before using Address Spaces ({\tt as\_create()} or
{\tt as\_bind()}).

\noindent {\tt AS as\_create(void)}

This library function can be used to create a new Address Space: it
searches for a free Address Space descriptor and initializes it to an
empty Address Space returning its identifier. The return value is the
created Address Space ID in case of success, or 0 in case of failure.

\noindent {\tt int as\_bind(AS as, DWORD ph\_addr, DWORD l\_addr, DWORD size)}

This library function binds a chunk of physical memory to an Address
Space. The {\tt as} parameter is the Address Space identifier,
{\tt ph\_addr} is the physical chunk start address, {\tt l\_addr} is the
logical address in the {\tt as} address space where the memory chunk
has to be mapped, and {\tt size} indicate the size of the memory
chunk expressed in bytes.

{\bf Warning:} currently, this function has been only partially implemented.
In particular, since paging is not enabled, a single chunk of memory can be
bound to an Address Space, starting from logical address 0.

\noindent {\tt void ll\_context\_setspace(CONTEXT c, AS as)}

This library functions changes the Address Space in which a thread runs.
Basically, {ll\_context\_setspace()} sets all the context {\tt c} segment
registers to the segment of the {\tt as} address space.
This function can be useful to create a new task: \begin{enumerate}
\item Create a new context
\item Create a new Address Space
\item Set the context Address Space to the created one...
\end{enumerate}
{\em We need an example...} Look at {\tt examples/aspacedemo.c}.

\section{Miscellaneous}
Two functions {\tt void *ll\_alloc(DWORD size)} and
{\tt void ll\_free(void *base, DWORD size)} are provided to
allocate and free physical memory. They are provided only for
convenience, but they should not be used: the memory allocator
should be implemented in an upper level, using the informations
returned by {\tt ll\_init()}.

Two functions {\tt char *ll\_context\_sprintf(char *str, CONTEXT c)} and
{\tt void dump\_TSS(WORD sel)} are provided for debugging purpose.

\end{document
}