next up previous contents
Next: 3.3.1.1 Memory banks Up: 3.3 Miscellaneous node operations Previous: 3.3 Miscellaneous node operations

  
3.3.1 Basic console output

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_console and set_cout implementation. >= (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 of off_Node. >+= (<-U) [<-D->]
<off_Node::use_serial_console and set_cout implementation. >

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;
}

Defines putchar (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;
}

Defines direct_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)
  }
};

Defines off_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
Defines OFF_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);
};

Defines off_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_mods implementation. >
<off_mdepNode::halt implementation. >
<off_mdepNode::reboot implementation. >
<off_mdepNode::get_minfo implementation. >
<off_mdepNode::get_ioinfo implementation. >
<off_mdepNode::get_dmainfo implementation. >
<off_mdepNode::get_procinfo implementation. >
<off_mdepNode::start_dbserial implementation. >

\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; }

};

Defines off_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}


 
next up previous contents
Next: 3.3.1.1 Memory banks Up: 3.3 Miscellaneous node operations Previous: 3.3 Miscellaneous node operations
Francisco J. Ballesteros
1998-05-25