The following ones, for example, redirects system output within the
kernel to several places. Output can be redirected at any time to
either a serial line console or to a user-level OS provided console
(identified by a portal named cout).
<Other public methods of off_Node. >+= (<-U) [<-D->]
// Use the serial console?
err_t use_serial_console(boolean_t doit = TRUE );
boolean_t using_serial_console(void) {assert(valid()); return n_comredirect; }
// User OS provided output services?
err_t set_cout(const off_prtl_id_t &cout );
boolean_t using_cout(void) const {assert(valid()); return n_coutredirect; }
<off_Node::use_serial_consoleandset_coutimplementation. >= (U->) // Use the serial console? err_t off_Node::use_serial_console(boolean_t doit = TRUE ) { assert(valid()); if (is_frozen()) return EFROZEN; n_comredirect=doit; if (doit) { kcout << "node: using serial line console" << nl; } return EOK; } // User OS provided output services? err_t off_Node::set_cout(const off_prtl_id_t &cout ) { assert(valid()); w_lock(); n_cout = cout; if (cout != OFF_PRTL_NULL) n_coutredirect = TRUE; w_unlock(); return EOK; }
<Implementation of other methods ofoff_Node. >+= (<-U) [<-D->] <off_Node::use_serial_consoleandset_coutimplementation. >
These methods are exported to system users.
<Other public methods of off_uNode. >= (<-U) [D->]
// Use the serial console?
err_t use_serial_console(boolean_t doit = TRUE, const off_Rights &r) ;
boolean_t using_serial_console(const off_Rights &r) const ;
// User OS provided output services?
err_t set_cout( const off_prtl_id_t &cout, const off_Rights &r) ;
boolean_t using_cout(const off_Rights &r) const;
The implementation sets flags which is in turn consulted by basic console output routines.
<Other private members of off_Node. >+= (<-U) [<-D]
boolean_t n_comredirect; // Using serial console?
boolean_t n_coutredirect; // Using OS provided console?
off_prtl_id_t n_cout; // portal for OS output services.
<Initialize other private members of off_Node. >+= (<-U) [<-D]
n_comredirect = n_coutredirect = FALSE;
n_cout = OFF_PRTL_NULL;
Basic system output proceeds through the \oskit{} \C{} library which
in turn calls putchar. It is putchar that
redirects character output to appropriate places.
<putchar implementation. >= (U->)
// Same of stdio/putchar
//
int putchar(int c)
{
assert(c);
#if 0 // XXX fix this
if (nd.using_cout()){
char m[2];
m[0]=(char)c;
m[1]='\0';
nd.puts(m);
}
else {
#endif
direct_cons_putchar(c);
// if (nd.using_serial_console())
// gdb_serial_putchar(c);
// }
return c;
}
Definesputchar(links are to index).
<Off basic console output dependencies. >= (U->) [D->] #include <flux/machine/direct_cons.h> // for direct_cons_putchar #include <flux/gdb_serial.h> // for gdb_serial_putchar #include <node/Node.h> // for nd et al.
The routine direct_cons_putchar is provided by the OSKit. It
echoes a character to the system console. gdb_serial_putchar is
also provided by the OSKit and does the same job using the serial
line instead.
Besides, more routines (called by the OSKit) need to be provided.
<direct_cons_puts implementation. >= (U->)
// same of stdio/puts
//
int direct_cons_puts(const char *s)
{
assert(s);
while (*s)
{
direct_cons_putchar(*s);
s++;
}
direct_cons_putchar('\n');
return 0;
}
Definesdirect_cons_puts(links are to index).
<Off basic console output dependencies. >+= (U->) [<-D] #include <assert.h> // for assert
We have use a new method puts provided by nd. It simply
upcalls to the user-established cout so that it could print the
string using the OS console driver.
<Other public methods of off_Node. >+= (<-U) [<-D]
// Print the string using the user-provided cout device.
err_t puts(const char *s);
This method is exported to system users.
<Other public methods of off_uNode. >+= (<-U) [<-D]
err_t puts(const char *s, const off_Rights &r);
<Implementation of other methods of off_Node. >+= (<-U) [<-D]
// Prints the string using the user-provided cout device.
err_t off_Node::puts(const char *s)
{
assert(valid());
assert(s);
if (n_cout == OFF_PRTL_NULL){
w_unlock();
return ENOPRTL;
}
else {
static off_PutsReq m;
err_t r=EOK;
w_lock();
m.fill(s);
// r=prtl.kdeliver(n_cout,OFF_SHTL_NULL, m.size(), &m, 0); XXX fix it
w_unlock();
return r;
}
}
The message sent to the user OS cout handler contains the string
to be printed. The whole string is send as a message so that we do not
depend on any additional user-level message servers (which could have
been used to pass the message by reference).
<Off user-kernel messages. >+= [<-D]
// Puts request.
struct off_PutsReq : off_MsgReq {
natural_t p_nbytes;
char p_str[OFF_NPUTS_MAX];
// Creates a puts request.
off_PutsReq(void) : off_MsgReq(OFF_EX_PUTS), p_nbytes(0) {;}
// Fills the message with the given string.
err_t fill(const char *s);
// Returns the size of this message.
vm_size_t size(void) {
return sizeof(off_MsgReq) + // for the base class
sizeof(p_nbytes) + // for p_bytes
p_nbytes+p_nbytes%sizeof(natural_t); // for the bytes in p_str
// (rounted to word size)
}
};
Definesoff_PutsReq(links are to index).
<Off user-kernel messages implementation. >=
// Fills the message with the given string.
err_t off_PutsReq::fill(const char *s)
{
assert(s);
size_t len=strlen(s);
err_t res = EOK;
if (len>=OFF_NPUTS_MAX){
len=OFF_NPUTS_MAX-1;
res=E2BIG;
}
strncpy(p_str,s,len);
p_str[OFF_NPUTS_MAX-1]='\0';
p_nbytes=len+1;
return res;
}
There is a new system limit: the maximum number of characters sent in
PutsReqs.
<Off limits. >+= [<-D] const int OFF_NPUTS_MAX = 255; // Max. number of characters in PutsReq
DefinesOFF_NPUTS_MAX(links are to index).
We have introduced new dependencies which must be resolved now.
<Off user-kernel messages dependencies. >+= [<-D] #include <klib/limits.h> // for OFF_NPUTS_MAX #include <assert.h> // for assert #include <flux/types.h> // for boolean_t natural_t et al. #include <klib/err.h> // for err_t and error numbers.
<Off user-kernel messages implementation dependencies. >= #include <string.h> // for strncpy.
\subsubsection{Basic console output \cpp{} source files}
The code shown above is kept in bconio.C with C linkage.
<bconio.C*>=
<Read the literate code instead warning. >
<Off basic console output dependencies. >
extern "C" {
<putchar implementation. >
<direct_cons_puts implementation. >
}; // "C" linkage
\section{The node for ix86 based architectures}
In this section we will describe the implementation of machine dependent code in the node system server.
A machine dependent node, mdepNode deals architecture
specifics for node operation.
<Off machine dependent node. >= (U->)
class off_mdepNode {
private:
public:
// Loads OS modules using loader.
void load_mods(off_ModLoader &loader);
// Halts this node.
void halt(void);
// Reboots this node.
void reboot(void);
// Returns the number of memory/IO/DMA/... banks. (for intel/PCs)
natural_t get_num_mbanks(void) const { return off_mdepMBank::how_many();}
natural_t get_num_iobanks(void) const { return off_mdepIOBank::how_many();}
natural_t get_num_procs(void) const { return off_mdepProcessor::how_many();}
// XXX fix this
// natural_t get_num_dmas(void) const { return off_mdepDMA::how_many();}
// Returns machine dependent information about the memory/IO/DMA/... bank #n
off_mdepMBank get_minfo(natural_t n) const;
off_mdepIOBank get_ioinfo(natural_t n) const;
off_mdepProcessor get_procinfo(natural_t n) const;
// XXX fix this
// off_mdepDMA get_dmainfo(natural_t n) const;
// Starts serial-line debugging.
void start_dbserial(void);
};
Definesoff_mdepNode(links are to index).
<Off machine dependent node dependencies. >= (U->) #include <node/mod.h> // for off_ModLoader #include <hw/mdep/mMBank.h> // for off_mdepMBank #include <hw/mdep/mIOBank.h> // for off_mdepIOBank #include <hw/mdep/mProcessor.h> // for off_mdepProcessor
\subsection{Machine dependent node \cpp{} source files}
Machine dependent node code is kept in node/mdep/mNode.h and
node/mdep/mNode.C.
<mdep mNode.h*>= <Read the literate code instead warning. > #ifndef __OFF_MDEP_NODE_H #define __OFF_MDEP_NODE_H 1 <Off machine dependent node dependencies. > #ifdef __KERNEL__ <Off machine dependent node. > #endif // __KERNEL__ #endif // __OFF_MDEP_NODE_H
<mdep mNode.C*>= <Read the literate code instead warning. > #include <node/mdep/mNode.h> // Exported interface. <Off machine dependent node implementation dependencies. > <off_mdepNode::load_modsimplementation. > <off_mdepNode::haltimplementation. > <off_mdepNode::rebootimplementation. > <off_mdepNode::get_minfoimplementation. > <off_mdepNode::get_ioinfoimplementation. > <off_mdepNode::get_dmainfoimplementation. > <off_mdepNode::get_procinfoimplementation. > <off_mdepNode::start_dbserialimplementation. >
\subsection{System halt and reboot}
We must implement the machine dependent routine used to halt the system.
<off_mdepNode::halt implementation. >= (<-U)
// Halts this node.
void off_mdepNode::halt(void)
{
<Off machine dependent node halt. >
}
It tries to halt the processor and (when unable to do so) loops forever.
<Off machine dependent node halt. >= (<-U U->)
off_Processor::halt();
off_Processor::clri();
for(;;)
off_Processor::clri();
panic("halt failed");
<off_mdepNode::reboot implementation. >= (<-U)
// Reboots this node.
void off_mdepNode::reboot(void)
{
pc_reset();
}
<Off machine dependent node implementation dependencies. >= (<-U) [D->] #include <flux/debug.h> // for panic #include <hw/Processor.h> // for clri,seti,has_clri et al. #include <flux/machine/pc/reset.h> // for pc_reset
A default _exit routine must be also implemented for the in-kernel
C library. It must have ``C'' linkage.
<_exit implementation. >= (U->)
// Terminates the kernel.
//
extern void _exit(int rc)
{
kcout << "exit with code " << rc << " -- System halted." << nl;
gdb_serial_exit(0);
<Off machine dependent node halt. >
}
Defines_exit(links are to index).
It is included on its own in exit.C.
<exit.C*>=
#include <klib/str.h> // for kcout
#include <flux/gdb_serial.h> // OSKit gdb support
#include <flux/gdb.h> // OSKit gdb support
#include <flux/debug.h> // for panic
#include <hw/Processor.h> // for clri, seti, halt et al.
extern "C" {
<_exit implementation. >
}; // C linkage
\subsection{OS modules startup}
The module information is kept in the boot_info structure by the
OSKit. Among other things, such structure contains the physical
address the information for each module can be found at
(mods_addr), a flag set when any module has been loaded
(MULTIBOOT_MODS, set in flags) and the number of modules
(mods_count).
<off_mdepNode::load_mods implementation. >= (<-U)
// Loads OS modules using loader.
void off_mdepNode::load_mods(off_ModLoader &loader)
{
u_int i;
struct multiboot_module *m = (struct multiboot_module*)
phystokv(boot_info.mods_addr);
if (!(boot_info.flags & MULTIBOOT_MODS))
panic("node: no OS boot modules to run. Is this a demo?");
for (i = 0; i < boot_info.mods_count; i++){
off_mdepModule mod(m+i);
if (loader.load(mod)) {
kcout << "node: boot module could not be started." << nl;
}
}
}
We depend now in the \oskit{} multiboot implementation.
<Off machine dependent node implementation dependencies. >+= (<-U) [<-D->] #include <flux/machine/multiboot.h> // for multiboot_info #include <flux/machine/pc/base_multiboot.h> // for boot_info
We have also used phystokv to translate the physical address found
in mods_addr to a kernel virtual address.
<Off machine dependent node implementation dependencies. >+= (<-U) [<-D->] #include <flux/machine/base_vm.h> // for phystokv
\subsubsection{The module loader}
The machine dependent module loader defines a data type to keep boot module information.
<Off machine dependent module. >= (U->)
// OS module info.
//
class off_mdepModule {
protected:
struct multiboot_module *m_minfo; // Multiboot module information.
public:
// Creates an off_mdepModule
off_mdepModule(struct multiboot_module *minfo) : m_minfo(minfo) {}
// Returns the argument string for this module.
char *argl(void) const { return (char*)m_minfo->string; }
};
Definesoff_mdepModule(links are to index).
As we can see, it depends on the multiboot module information.
<Off machine dependent module dependencies. >= (U->) #include <flux/machine/multiboot.h> // For multiboot_module et al.
\subsubsection{OS modules startup \cpp{} source files}
The machine dependent module loader code is kept in node/mdep/mmod.h.
<mdep mmod.h*>= <Read the literate code instead warning. > #ifndef __OFF_MDEP_MOD_H #define __OFF_MDEP_MOD_H 1 <Off machine dependent module dependencies. > #ifdef __KERNEL__ <Off machine dependent module. > #endif // __KERNEL__ #endif // __OFF_MDEP_MOD_H
\subsection{Getting physical resources boot time information}