The system entry point, which is called by the OSKit after basic
hardware initialization is named main. Its arguments come from
the secondary boot loader.
The bootstrapping process consists on processing system options,
creating an instance of Node (to initialize system global data
structures and create instances for system objects to reflect the
available hardware and system services), issuing a hello
message3.3, and finally bringing any OS module loaded along with the
kernel into operation (before main gets called the multiboot
process may load other system modules).
Interrupts are cleared until every system server has been started.
<Off main entry point implementation. >= (U->)
// main system entry point.
//
int main(int argc, char *argv[])
{
extern void __do_global_ctors(void);
__do_global_ctors();
{
<Local variables of main. >
off_Processor::clri();
<Initialize the nd instance. >
<Say hello to users. >
<Start system servers. >
#if 0
off_Processor::seti();
<Bring OS modules up into operation. >
#endif
}
nd.halt();
}//main
Definesmain(links are to index).
To process system options we employ an Options object. It will do
the job and provide entry points to inspect option settings.
<Local variables of main. >= (<-U) [D->]
off_Options opts(argc,argv); // option settings at boot time.
<Off main entry point implementation dependencies. >= (U->) [D->] #include <node/Options.h> // for off_Options #include <hw/Processor.h> // for clri, seti et al.
Option processing will be described in section
, while
we say what options are available. For every option, the effect in the
behavior of the system is described along with the description of the
system objects involved.
The node instance, nd will receive the options settings by
set_options. Then, it will pass them in turn to every system object
it instantiates. Besides, the node protection will be set by
generating a new randomized Protection (and will be used later on
to protect remaining system servers).
<Initialize the nd instance. >= (<-U)
nd.set_options(opts);
p.randomize();
nd.protect(p,OFF_M_ALL);
<Local variables of main. >+= (<-U) [<-D]
off_Protection p;
<Off main entry point implementation dependencies. >+= (U->) [<-D->] #include <klib/prot.h> // for off_Protection, OFF_M_ALL et al. #include <node/Node.h> // for nd and off_Node
Once nd options have been set, we will say hello to any casual
user.
<Say hello to users. >= (<-U)
kcout << nl <<
"System Off" << nl;
kcout <<"Off++ "<<OFF_VERSION<<" "<<
OFF_COMPILE_DATE<<" "OFF_COMPILE_TIME<<" "<<
"("<<OFF_COMPILE_BY<<"@"<<OFF_COMPILE_HOST<<" "<<OFF_COMPILER<<")" <<
nl << nl;
The symbols used are taken from a source file generated every time the
kernel is built. Such file is named version.h and can be found in
the klib/Interfaces directory (see section
). We
must thus include that file as well as the stdio facilities we are
using.
<Off main entry point implementation dependencies. >+= (U->) [<-D->] #include <klib/version.h> // Compilation & version symbols. #include <stdio.h> // Basic I/O facilities.
To bring the system up, system servers are started and protected with the same protection created before for the node itself.
<Start system servers. >= (<-U) nd.srvstart(p);
The srvstart method does the job.
<Other public methods of off_Node. >+= (<-U) [<-D->]
// Starts system servers.
void srvstart(const off_Protection &prot);
Finally, after everything has been initialized and the system brought
up into operation, we will start any OS module loaded along with the
kernel image by the secondary boot loader. These modules will start in
turn OS services for system users. A new method in Node,
modstart, will do the job. Module startup code is thus contained
inside the Node singleton.
<Other public methods of off_Node. >+= (<-U) [<-D->]
// Starts any user module loaded along with the kernel image.
void modstart(void);
\Note{Add special module to melt frozen nodes.}
As the kernel will be no longer executing after starting up user
modules, a return from modstart will be considered to be system
failure. The otsan facility will halt the system in such a case.
<Bring OS modules up into operation. >= (<-U)
nd.modstart();
panic("modstat returned");
<Off main entry point implementation dependencies. >+= (U->) [<-D] #include <flux/debug.h> // for panic et al.
\subsection{Main entry point \cpp{} files}
The main entry point is kept in node/main.C. It has ``C''
linkage.
<main.C*>=
<Read the literate code instead warning. >
<Off main entry point implementation dependencies. >
extern "C" {
<Off main entry point implementation. >
} // "C" linkage
\subsection{System options}\label{sc:options}
System options are processed by Options. At creation time, every
argument given by the user will be taken into account. Later on we can
use Options methods to inspect option setting.
The main user of Options is the node. It accepts option
settings with this method.
<Other public methods of off_Node. >+= (<-U) [<-D->]
// Sets user options.
void set_options(const off_Options &opts);
The implementation will merely record the given value and process node specific options.
<Implementation of other methods ofoff_Node. >= (<-U) [D->] // Sets user options. void off_Node::set_options(const off_Options &opts) { assert(valid()); n_opts= new off_Options(opts); <Process node specific options fromn_opts. > }
<Other private members of off_Node. >+= (<-U) [<-D->]
off_Options *n_opts; // user options
<Off node dependencies. >+= (<-U) [<-D->] #include <node/Options.h> // for off_Options
<Off options. >= (U->)
class off_Options {
private:
<Other private members of off_Options. >
protected:
<Other protected methods of off_Options. >
public:
off_Options( int argc, char *argv[]);
<Node management public methods in off_Options. >
<Memory management public methods in off_Options. >
};
As we have seen in the code for Options, there are several kind of
options recognized by the system. We describe them below.
Option processing is done almost entirely by the Options
constructor. It calls a per-option static method used to find and
record the option value.
<off_Options::off_Options implementation. >= (U->)
off_Options::off_Options( int argc, char *argv[])
{
<Extract option settings. >
}
The ndid option may be used to explicitly set the node
identifier; useserial to enable the serial console (as we will see in
section
, basic system console output can be redirected
to a serial line for debugging purposes); owner to establish the
owner email; url to establish the base URL for the source
code; and name to name the node.
<Node management public methods in off_Options. >= (<-U)
// Returns the user specified node identifier.
inline off_id_t get_ndid(void) const {return o_ndid;}
// Returns the user specified useserial value.
inline boolean_t get_useserial(void) const {return o_useserial;}
// Returns the user specified owner value.
inline char * get_owner(void) const {return o_owner;}
// Returns the user specified url value.
inline char * get_url(void) const {return o_url;}
// Returns the user specified node value.
inline char * get_name(void) const {return o_name;}
<Other private members of off_Options. >= (<-U) [D->]
off_id_t o_ndid; // Node identifier.
boolean_t o_useserial; // Use serial console?
char *o_owner; // Owner's email
char *o_url; // Source code URL.
char *o_name; // Node name
<Off options dependencies. >= (U->) [D->] #include <klib/ids.h> // for off_id_t et al. #include <flux/types.h> // for boolean_t natural_t et al.
Option values are obtained by scan_ndid.
<Extract option settings. >= (<-U) [D->] o_ndid=scan_ndid(argc,argv); o_useserial=scan_useserial(argc,argv); o_owner=scan_owner(argc,argv); o_url=scan_url(argc,argv); o_name=scan_name(argc,argv);
<Other protected methods of off_Options. >= (<-U) [D->]
// Extracts ndid option value.
static off_id_t scan_ndid(int argc, char *argv[]);
// Extracts useserial option value.
static boolean_t scan_useserial(int argc, char *argv[]);
// Extracts owner option value.
static char *scan_owner(int argc, char *argv[]);
// Extracts url option value.
static char *scan_url(int argc, char *argv[]);
// Extracts name option value.
static char *scan_name(int argc, char *argv[]);
<off_Options::scan_ndid implementation. >= (U->)
// Extracts ndid option value.
off_id_t off_Options::scan_ndid(int argc, char *argv[])
{
char *idval = getenv("ndid");
(void)argc; (void)argv;
off_id_t id= OFF_ID_NULL;
if (idval!=NULL){
id.i_node=(off_node_t)atoi(idval);
}
else
id.i_node=1;
return id;
}
<Off options implementation dependencies. >= (U->) #include <stdlib.h> // for getenv and atoi
<off_Options::scan_useserial implementation. >= (U->)
// Extracts ndid option value.
boolean_t off_Options::scan_useserial(int argc, char *argv[])
{
char *val = getenv("useserial");
(void)argc; (void)argv;
return (val && *val == 'y' || *val == 'Y');
}
<off_Options::scan_owner implementation. >= (U->)
// Extracts owner option value.
char *off_Options::scan_owner(int argc, char *argv[])
{
char *val = getenv("owner");
(void)argc; (void)argv;
return val;
}
<off_Options::scan_url implementation. >= (U->)
// Extracts url option value.
char *off_Options::scan_url(int argc, char *argv[])
{
char *val = getenv("url");
(void)argc; (void)argv;
return val;
}
<off_Options::scan_name implementation. >= (U->)
// Extracts url option value.
char *off_Options::scan_name(int argc, char *argv[])
{
char *val = getenv("name");
(void)argc; (void)argv;
return val;
}
The node instance will process these option by adjusting the node
identifier to that specified by the user and activating the serial
console (with use_serial_console) if appropriate. The convention
is that the node domain is always the same of the node
identifier. Such domain will be used to allocate kernel-wide
physical resources.
<Process node specific options from n_opts. >= (<-U)
set_id(n_opts->get_ndid());
set_domain(get_id());
use_serial_console(n_opts->get_useserial());
if (using_serial_console())
n_mdep.start_dbserial();
n_owner="nemo";
n_name="titanic";
n_url="http://www.gsyc.inf.uc3m.es/~nemo/off";
#if 0 // XXX fix it.
n_owner=n_opts->get_owner();
if (!n_owner)
n_owner="whoever";
n_owner=strdup(n_owner);
n_url=strdup(n_opts->get_url());
if (!n_url)
n_url=strdup(off_Version::url);
n_name=strdup(n_opts->get_name());
if (!n_name)
n_name = "whereever";
n_name=strdup(n_name);
#endif
The machine dependent method n_mdep.start_dbserial will initialize a
debug-purpose serial console. A machine dependent node instance named
n_mdep is aggregated into the node as we have seen.
<Other protected members of off_Node. >+= (<-U) [<-D]
off_mdepNode n_mdep; // Machine dependent node
<Off node dependencies. >+= (<-U) [<-D->] #include <node/mdep/mNode.h> // for off_mdepNode
\subsubsection{Memory management options}
The usedmm option is a switch for DTLB support. Its value can
be inspected with get_usedmm.
<Memory management public methods in off_Options. >= (<-U)
// Activate DMM?
inline boolean_t usedmm(void) const {return o_usedmm;}
<Other private members of off_Options. >+= (<-U) [<-D]
boolean_t o_usedmm; // Should we support dtlbs?
<Off options dependencies. >+= (U->) [<-D] #include <flux/types.h> // for boolean_t natural_t et al.
The option value is obtained by scan_usedmm.
<Extract option settings. >+= (<-U) [<-D] o_usedmm=scan_usedmm(argc,argv);
<Other protected methods of off_Options. >+= (<-U) [<-D]
// Extracts ndid option value.
static boolean_t scan_usedmm(int argc, char *argv[]);
<off_Options::scan_usedmm implementation. >= (U->)
// Extracts ndid option value.
extern boolean_t off_Options::scan_usedmm(int argc, char *argv[])
{
char *val = getenv("usedmm");
(void)argc; (void)argv;
return !(val && (*val == 'N' || *val == 'n'));
}
\subsubsection{Node options \cpp{} source files}
Option processing code is kept in node/Options.h and
node/Options.C.
<Options.h*>= <Read the literate code instead warning. > #ifndef __OFF_OPTIONS_H #define __OFF_OPTIONS_H 1 <Off options dependencies. > #ifdef __KERNEL__ <Off options. > #endif // __KERNEL__ #endif // __OFF_OPTIONS_H
<Options.C*>= <Read the literate code instead warning. > #include <node/Options.h> // Exported interface. <Off options implementation dependencies. > <off_Options::scan_usedmmimplementation. > <off_Options::scan_ndidimplementation. > <off_Options::scan_useserialimplementation. > <off_Options::off_Optionsimplementation. > <off_Options::scan_ownerimplementation. > <off_Options::scan_urlimplementation. > <off_Options::scan_nameimplementation. >