Comparing IPC and capability invocation

Hermann Härtig haertig at os.inf.tu-dresden.de
Sun Dec 7 16:10:12 CET 2003


shap: [I apologize in advance for both the length and the depth of this 
note.]
so do I

In Dresden, a group of us is discussing related issues since a while ...

I am convinced since a long time
* that communication should *not* be addressed to threads, or - in other 
words - a service's thread structure should be completely transparent to 
clients.
* that all resources exported by the kernel (address spaces, threads, 
memory, interrupt lines, ...) should have a local name (now in L4, only 
memory has local names -> virtual addresses). All types of resources 
provided by the micro kernel can be transfered from a "pager" to a 
"client" via IPC just like page faults are handled now in L4 (mapping 
and flushing). Then, all resources are initially owned by sigma0 and 
L4's elegant pager hierarchies can be easily extended to all (types of) 
resources. "Mapping data bases" are then needed for all types of 
resources  as well. (We are discussing generalizations to the "sigma0" 
scheme as well. E.g., each pager might be able to provide a resource to 
the kernel to obtain a number of threads in exchange and then owns these 
threads. It then can act as thread pager for the clients. I leave 
further discussion of that out of this email to avoid confusion.)

We refer to that scheme as "generalized mapping". Here is an example for 
the usage: a server may discover that it would like to use an extra 
thread, it simply uses it by its local name ("start 57" or "set 
registers 57, ..."). If 57 is void (a void capability in EROS speak), a 
"thread fault" is discovered by the micro kernel and transformed to a 
message to the responsible pager who may or may not respond by providing 
a thread mapping.

(I see no benefit though of a "unified capability space". Memory 
"descriptors" are PTEs.)

IPC provides the means for communication between units of protection. 
Units of protection in L4 are address spaces. Thus, messages should be 
sent from one address space to another using local names for address 
spaces. Such "send mappings" (could be called "send capabilities" for 
address spaces) are all owned by sigma0, can be mapped and flushed. For 
example, sending a message to 57 results(if void) in a "send mapping 
fault" to the responsible pager who may or may not respond by providing 
a send mapping. Arbitrary message interception structures can be 
established that way. No need to consider thread structures of services 
from the point of view of any client, be it an "original" client or an 
interceptor.

(I see no benefit though of restricting communication to "calls on 
objects". User-level SW of course can restrict communication to such 
patterns, but why should a micro-kernel enforce such restriction? 
Dresden's version of an IDL compiler is designed to support arbitrary 
message patterns. User-land "objects" should remain a user-land issue.)

The question now is: how does a receiver identify the sender?
J.S. says: no need to identify since there are capabilities. I disagree 
here: relying just on capabilities leads to much-too-fine grained 
clustering of protection units.
 From my point of view, we need to enable protection units to securely 
find out from which clients messages came in, or - in other words - to 
serve clients with fairly different rights.

In Dresden, we discussed many schemes by which a receiver can identify a 
sender, here are my two current favorites (alternatives):
* Identification via local names for "Ports":
each address space (unit of protection) has several ports. Ports are 
entrance points into the address spaces; threads of an address space 
wait for messages on ports; ports are resources provided by the kernel 
and handled at user level using mapping and flushing operations.
A send mapping (for example send mapping with local name 57) has the form
  (address space, Port-Range)
where "Port-Range" is an interval in the local name of the addressee.
All threads within an address space can wait on all its ports. Senders 
are identified via local name of the port a message comes in.
(Some tricky details of this scheme omitted here. "Ports" are close to 
MACH ports, notable differences: synchronous IPC, flushing, ...)
* Identification via "Principal ID"s of senders.
They are enforced by the kernel(as with physical thread ids in L4), but 
managed at user level. A send mapping (for example send mapping with 
local name 57) has the form
  (address space, Principal ID: LENGTH,VALUE)
IPC may then send out a message M by calling: send (57, M).
The kernel assigns "57.VALUE" to the first part of M where the "first 
part" has length "57.LENGTH".
Again, send mappings are passed from pagers to clients. While doing so, 
LENGTH may increase, but not decrease. That way, senders further down a 
pager hierarchy can be restricted in their "Principal ID" space or - in 
other words - in which content the first part of messages may contain.
Almost arbitrary user-level naming schemes can be provided.
(I omit to discuss the "closed wait" problems that need to be addressed 
in this scheme.)


--hermann






More information about the l4-hackers mailing list