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.
Therefore, we might be interested in any of three different processor contexts when referring to a shuttle processor context:
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.
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_uctxandset_kctximplementation. >= (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 propertypvalue inget_prop. >= (<-U) [D->] case OFF_SHTLP_PCTX: return OFF_PRTL_NULL;
<case to set predefined propertypvaluevalinset_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_enableandusrio_disableimplementation. >= (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?
DefinesOFF_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 propertypvalue inget_prop. >+= (<-U) [<-D->] case OFF_SHTLP_IOPL: return (is_usrio()) ? get_id() : OFF_PRTL_NULL;
<case to set predefined propertypvaluevalinset_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_prtyandoff_Shtl::set_prtyimplementation. >= (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 propertypvalue inget_prop. >+= (<-U) [<-D->] case OFF_SHTLP_IPL: return off_id_t(get_id(),get_prty());
<case to set predefined propertypvaluevalinset_prop. >+= (<-U) [<-D->] case OFF_SHTLP_IPL: set_prty(val.i_offset); break;
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_dtlbandset_dtlbimplementation. >= (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 propertypvalue inget_prop. >+= (<-U) [<-D->] case OFF_SHTLP_DTLB: return get_dtlb();
<case to set predefined propertypvaluevalinset_prop. >+= (<-U) [<-D->] case OFF_SHTLP_DTLB: set_dtlb(val); break;