next up previous contents
Next: 3.2.0.1 Complete option listing Up: 3. The node Previous: 3.1.1 The node navigator

3.2 System booting

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

Defines main (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 of off_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 from n_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. >
}

\subsubsection{Node options}

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_usedmm implementation. >
<off_Options::scan_ndid implementation. >
<off_Options::scan_useserial implementation. >
<off_Options::off_Options implementation. >
<off_Options::scan_owner implementation. >
<off_Options::scan_url implementation. >
<off_Options::scan_name implementation. >



 
next up previous contents
Next: 3.2.0.1 Complete option listing Up: 3. The node Previous: 3.1.1 The node navigator
Francisco J. Ballesteros
1998-05-25