next up previous contents
Next: 5.1.2.3 Run state Up: 5.1.2 Shuttles Previous: 5.1.2.1 Shuttle property allocation

5.1.2.2 Processor contexts

The processor context is a hardwired property which contains the processor register values.

As we will see in the next chapter, shuttles may move from one protection domain to another, including the kernel protection domain, by means of portals. Besides, the use of upcalls from kernel to user space means that the set of kernel and user activations5.4 (see figure [*]) will be mixed. Thus, a user shuttle may call the kernel and the kernel my upcall back to the user during the system call; the upcall routine may in turn issue new system calls, and so on.


  
Figure 5.2: Shuttle activations: system calls and up-calls.

Therefore, we might be interested in any of three different processor contexts when referring to a shuttle processor context:

1.
The current register set, i.e. the last saved register set which holds register values at the time the shuttle was last preempted. This is the one used when we are interested in the ``current'' state of the shuttle, no matter what its privilege level is.

2.
The last user register set, i.e. the topmost user context saved in the stack of activations (system calls/upcalls).

This is the one used when we are interested either in the user state at the last system call or in the state to be returned to the user.

3.
The last kernel register set, i.e. the topmost kernel context saved in the stack of activations (system calls/upcalls). This is the one used when we are interested in the state of a shuttle while proceeding inside the kernel.
The first register set will always match one of the two latter ones.

To implement this, shuttles contain references to the three kinds of processor contexts.

<Other private members of off_Shtl. >= (<-U) [D->]
off_ProcRegs *s_uctx;           // Top-most user context.
off_ProcRegs *s_kctx;           // Top-most kernel context.
off_ProcRegs *s_ctx;            // Top-most context.

Such references are initially null.

<Initialize other aggregate members of off_Shtl. >= (<-U) [D->]
s_uctx(NULL),s_kctx(NULL),s_ctx(NULL),

<Other public methods of off_Shtl. >+= (<-U) [<-D->]
// Get a pointer to current, last user, or last kernel
// registers in the stack.
inline off_ProcRegs *get_regs(void);
inline off_ProcRegs *get_uregs(void);
inline off_ProcRegs *get_kregs(void);

<off_Shtl::get_regs et al. implementation. >= (U->)
// Get a pointer to current, last user, or last kernel
// registers in the stack.
inline off_ProcRegs *off_Shtl::get_regs(void) { return  s_ctx; }
inline off_ProcRegs *off_Shtl::get_uregs(void){ return  s_uctx;}
inline off_ProcRegs *off_Shtl::get_kregs(void){ return  s_kctx;}

In certain cases we may want to access a context down in the stack. This method makes a provision for that. It takes a processor context and return the next context down in the stack.

<Other public methods of off_Shtl. >+= (<-U) [<-D->]
// Dig in the stack for another (kernel or user) processor context. 
inline off_ProcRegs *get_next_regs(const off_ProcRegs *r);

The implementation is done in the machine dependent part because it is there where we usually link one context to another through the kernel stack.

<off_Shtl::get_next_regs implementation. >= (U->)
// Digs in the stack for another (kernel or user) processor context. 
// Returns NULL when no more frames are available. 
inline off_ProcRegs *off_Shtl::get_next_regs(const off_ProcRegs *r) { 
  return s_mdep.get_next_regs(r);
}

But, How are nested contexts handled? The machine dependent part is responsible for handling a linked list of contexts. The list uses local variables of the upcall and system call procedures so that the stack is used to maintain the list. Those procedures call set_uctx and set_kctx every time a new context is pushed/removed from the stack.

<Other protected methods of off_Shtl. >= (<-U) [D->]
<Friend declaration for upcall and system call procedures. >
// Installs a new user context as the top-most.
inline void set_uctx(off_ProcRegs *r);

// Installs a new kernel context as the top-most.
inline void set_kctx(off_ProcRegs *r);

<off_Shtl::set_uctx and set_kctx implementation. >= (U->)
// Installs a new user context as the top-most.
inline void off_Shtl::set_uctx(off_ProcRegs *r) 
{
  s_uctx=s_ctx=r;
}

// Installs a new kernel context as the top-most.
inline void off_Shtl::set_kctx(off_ProcRegs *r)
{
  s_kctx=s_ctx=r;
}

Finally, we should say the the coprocessor context is considered to be part of the processor context. It is handled by the machine dependent part using lazy coprocessor context switching.

The processor context property is ignored by get_prop and set_prop; specific methods shown above must be used instead.

<case to return predefined property p value in get_prop. >= (<-U) [D->]
case OFF_SHTLP_PCTX:
   return OFF_PRTL_NULL;

<case to set predefined property p value val in set_prop. >= (<-U) [D->]
case OFF_SHTLP_PCTX:
   break;

\subsubsection{I/O privilege level}

This is a hardwired property which determines if the shuttle can do I/O when running in user mode. To keep the state of such capability we use the resource flags.

<Other public methods of off_Shtl. >+= (<-U) [<-D->]
// Supports user IO?
inline boolean_t is_usrio(void) const;
// Enables user IO.
void usrio_enable(void);
// Disables user IO.
void usrio_disable(void);

<off_Shtl::is_usrio implementation. >= (U->)
// Supports user IO?
inline boolean_t off_Shtl::is_usrio(void) const { return r_flags&OFF_SF_USR; }

<off_Shtl::usrio_enable and usrio_disable implementation. >= (U->)
// Enables user IO.
void off_Shtl::usrio_enable(void) 
{
  r_flags |= OFF_SF_USR;
  s_mdep.usrio_enable(&get_uregs()->p_mdep);
}
// Disables user IO.
void off_Shtl::usrio_disable(void) 
{
  r_flags &= ~OFF_SF_USR;
  s_mdep.usrio_disable(&get_uregs()->p_mdep);
}

We have to defined the new flag.

<Off shuttle flag bits. >= (U->) [D->]
const unsigned8_t OFF_SF_USR = 0x10; // User level IO?
Defines OFF_SF_USR (links are to index).

Finally, this property is initialized at shuttle creation time whenever a prototype shuttle is supplied.

<Initialize hardwired properties with those of proto. >= (<-U) [D->]
if (proto->is_usrio())
  usrio_enable();
else
  usrio_disable();

We must complete get_prop and set_prop now.

<case to return predefined property p value in get_prop. >+= (<-U) [<-D->]
case OFF_SHTLP_IOPL:
   return (is_usrio()) ? get_id() : OFF_PRTL_NULL;

<case to set predefined property p value val in set_prop. >+= (<-U) [<-D->]
case OFF_SHTLP_IOPL:
   if (val == OFF_PRTL_NULL)
     usrio_disable();
   else
     usrio_enable();
   break;

\subsubsection{Interrupt priority levels}

Shuttles include an interrupt priority level so that the kernel could choose whether to evict the current shuttle on interrupt or to defer interrupt delivering.

<Other private members of off_Shtl. >+= (<-U) [<-D->]
off_pl_t s_pl;                  // Shuttle interrupt priority level.

<Off shuttle dependencies. >+= (U->) [<-D->]
#include <hw/Event.h>           // for off_pl_t

<Other public methods of off_Shtl. >+= (<-U) [<-D->]
// Returns the interrupt priority level for this shuttle.
inline off_pl_t get_prty(void) const;
// Sets the interrupt priority level for this shuttle.
inline err_t    set_prty(off_pl_t prty);

<off_Shtl::get_prty and off_Shtl::set_prty implementation. >= (U->)
// Returns the interrupt priority level for this shuttle.
inline off_pl_t off_Shtl::get_prty(void) const { return s_pl; }
// Sets the interrupt priority level for this shuttle.
inline err_t    off_Shtl::set_prty(off_pl_t prty) { s_pl = prty; return EOK; }

Interrupt priority levels must be honored by Processors (see section [*]).

The priority level is initialized to a default value so that by default shuttles yield to interrupts.

<Initialize other aggregate members of off_Shtl. >+= (<-U) [<-D->]
s_pl(OFF_NPL_DFL),

Finally, this property also set at shuttle creation time whenever a prototype shuttle is supplied.

<Initialize hardwired properties with those of proto. >+= (<-U) [<-D->]
set_prty(proto->get_prty());

We must complete get_prop and set_prop now.

<case to return predefined property p value in get_prop. >+= (<-U) [<-D->]
case OFF_SHTLP_IPL:
   return off_id_t(get_id(),get_prty());

<case to set predefined property p value val in set_prop. >+= (<-U) [<-D->]
case OFF_SHTLP_IPL:
   set_prty(val.i_offset);
   break;

\subsubsection{\dtlb{}}

Although DTLB usage is optional, it is the common case that each shuttle has an associated DTLB to be used as its protection domain. Thus, this property is also hardwired.

When there is no DTLB support and the kernel is expected to operate on a single address space, these methods should never be called.

<Other private members of off_Shtl. >+= (<-U) [<-D->]
off_dtlb_id_t s_dtlb;           // DTLB used by this shuttle.

<Other public methods of off_Shtl. >+= (<-U) [<-D->]
// Returns the identifier of the DTLB being used.
inline const off_dtlb_id_t &get_dtlb(void) const;

// Sets the associated DTLB for this shuttle.
inline void set_dtlb(const off_dtlb_id_t &dtlb);

<off_Shtl::get_dtlb and set_dtlb implementation. >= (U->)
// Returns the identifier of the DTLB being used.
inline const off_dtlb_id_t &off_Shtl::get_dtlb(void) const 
{
  return s_dtlb;
}

// Sets the associated DTLB for this shuttle.
inline void off_Shtl::set_dtlb(const off_dtlb_id_t &dtlb)
{
  s_dtlb=dtlb;
  if (get_id()==off_Shtl::self()->get_id())
    s_mdep.set_dtlb(dtlb);
}

Initially, the \dtlb{} identifier is set to a null value.

<Initialize other aggregate members of off_Shtl. >+= (<-U) [<-D->]
s_dtlb(OFF_DTLB_NULL),

Finally, this property also set at shuttle creation time whenever a prototype shuttle is supplied and DTLB support is enabled.

<Initialize hardwired properties with those of proto. >+= (<-U) [<-D->]
set_dtlb(proto->get_dtlb());

Note how if no \dtlb{} support has been enabled, we do not care about the set_dtlb effects, as the hardware will ignore the attempt to set a protection domain. Depending on the architecture being used, the page table register (e.g. on Intels) will be silently set and ignored or the domain tag (e.g. on MIPS) will be set to the only one being used.

We must complete get_prop and set_prop now.

<case to return predefined property p value in get_prop. >+= (<-U) [<-D->]
case OFF_SHTLP_DTLB:
   return get_dtlb();

<case to set predefined property p value val in set_prop. >+= (<-U) [<-D->]
case OFF_SHTLP_DTLB:
     set_dtlb(val);
   break;


next up previous contents
Next: 5.1.2.3 Run state Up: 5.1.2 Shuttles Previous: 5.1.2.1 Shuttle property allocation
Francisco J. Ballesteros
1998-05-25