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 | darwin-kernel@lists.apple.com Help/Unsubscribe/Archives: http://www.lists.apple.com/mailman/listinfo/darwin-kernel Do not post admin requests to the list. They will be ignored.