Re: MIG RPC from kext question
Re: MIG RPC from kext question
- Subject: Re: MIG RPC from kext question
- From: Jim Magee <email@hidden>
- Date: Fri, 26 Sep 2003 11:55:58 -0400
On Sep 26, 2003, at 5:12 AM, Marek Kozubal wrote:
I am trying to use an INOUT parameter in a MIG RPC call from the
kernel to
a user land application, unfortunately all my attempts just cause a
kernel
panic to happen.
Here is basically what I'm doing (hopefully someone can tell me what I
am
missing):
mig .defs file:
#include <mach/std_types.defs>
#include <mach/mach_types.defs>
subsystem KernelUser foo_subsystemServer 1000;
type foo_array_t = array[] of uint8_t;
routine
foo_rpc(
server: mach_port_t;
inout io_foo_array: foo_array_t );
This produces a function that looks like:
mig_external kern_return_t foo_rpc(
mach_port_t server,
foo_array_t *io_foo_array,
mach_msg_type_number_t *io_foo_arrayCnt )
So my question is this, what sort of pointer needs to go into the RPC
call
and what do I get in user land and what do I get back in return?
I have posted about this in the past (look for vm_map_copy_t in the
archives - that should hit on the previous discussions). But it's been
a while, so a refresher would be nice:
The kernel is not "just another task" when it comes to Mach IPC -
especially when you use the KernelUser or KernelServer MIG keywords.
In those conditions, you are really working with "half-cooked" IPC
messages. That is, when a message is sent from user-space to a
"KernelServer" kernel receiver, we copyin() and process the message
into Mach IPC's internal (message sitting on a queue) form - and then
directly call the specified routine. We don't "copyout()" the message
to the kernel's address/port name space. The same is true in the
opposite direction. When you send a message from the kernel to
user-space using "KernelUser" it must already be in Mach IPC's
"already-been-copied-in" form.
So what does this mean - specifically?
First, the two ports in the message header (local and remote) usually
get flip-flopped during the message copyout operation (so that each the
sender and receiver see local and remote in accordance with their own
perspectives). So, after just a message copyin operation, the internal
format is still laid out just like the sender would normally see it.
If this is a KernelServer routine, we will call the message handling
code at this point - and it must deal with the fact that the ports in
the header have not been swapped yet. MIG generates code specifically
to handle this for you.
Second, all port rights are "naked." That is, they are not represented
by a name in a port namespace with specific rights enumerated in that
space. Each port is represented by just a pointer to the port itself.
The right(s) in question are accounted for in the port's global rights
counts, but not in any private to the kernel namespace. You have sharp
tools at that point (i.e. you have the ability to manipulate the port
in any way you chose). You have to be careful to consume only the
right you were explicitly granted in the message. But nothing enforces
this for you (you are part of the kernel - after all). When sending
naked port rights, just the opposite is true. You have to manually add
the appropriate right references to the ports first (since the message
is assumed to be in the "already copied in" state). Only "move" type
dispositions are expected on the ports (since the copy, make, etc...
side-effects apply during the message copyin operation). Often, the
built-in kernel object's have MIG intrans and outtrans routines defined
to handle this. They convert object references to a specific kind of
[potentially added] right on the port that is a handle for the object -
and then just the naked port pointer and the appropriate "move"
disposition in put in the message's port-handling descriptor by MIG.
Third, all out-of-line arrays of ports are stored as naked rights
(again, just as above) but in kalloc()'ed storage. This is different
than the user environment where vm_allocate()'ed storage is used to
send/receive out-of-line port arrays. Code that sends and receives
these from within the kernel has to allocate and free the kalloc()
memory rather than freeing whole vm_allocate()'ed pages.
Finally, and most directly related to the question at hand, out-of-line
data (unbounded non-port data) is represented by a vm_map_copy_t data
structure while messages sit on queues. They are "shorthand notion"
about where the data can be found (which VM object's and for what
ranges, permissions, etc...), since it would be silly to require the
kernel to map all that data into the kernel's address space for every
message sitting in a message queue. Because "KernelServer" and
"KernelUser" code has to deal with messages in this "sitting on a
queue" format, they have to deal with the vm_map_copy_t data structures
directly as well. So, what you get and send for out-of-line data is
not a pointer to the data, but a pointer to the vm_map_copy_t structure
that describes where the data is. If you receive one of these messages
in the kernel, you have to vm_map_copyout() the vm_map_copy_t to the
kernel's address space manually before you can look at the data. If
you want to send this kind of data from the kernel, you have to
vm_map_copyin() the data from the kernel's address space and then send
a pointer to the vm_map_copy structure.
It is often quite a bit more efficient to deal with these half-cooked
messages (and since all the Mach kernel operations are implemented
using messages, the savings is well worth it). But it's not for the
faint of heart. Which is why it is classified as SPI. Things like the
IOKit user-client libraries encapsulate all these details, so the
average KEXT writer needn't be bothered. But when you wonder outside
those protections, that's when the "fun" begins.
--Jim
_______________________________________________
darwin-kernel mailing list | email@hidden
Help/Unsubscribe/Archives:
http://www.lists.apple.com/mailman/listinfo/darwin-kernel
Do not post admin requests to the list. They will be ignored.