bug-hurd
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH mig] Make MIG work for pure 64 bit kernel and userland.


From: Sergey Bugaev
Subject: Re: [PATCH mig] Make MIG work for pure 64 bit kernel and userland.
Date: Sun, 12 Feb 2023 21:16:16 +0300

On Sun, Feb 12, 2023 at 7:34 PM Luca <luca@orpolo.org> wrote:
>
> Il 12/02/23 17:05, Sergey Bugaev ha scritto:
> > On Sun, Feb 12, 2023 at 7:01 PM Luca <luca@orpolo.org> wrote:
> >> It seems XNU's mig [0] always sets the alignment to natural_t (=4) with
> >> a #pragma... I still have to understand how they handle these issues.
> >
> > Please note that XNU uses "untyped messaging", and Apple's version of
> > MIG is "untyped MIG". They don't have the kernel parse the message
> > body, it just passes it to the receiver task as a blob, where it's up
> > to MIG again to make sense of it.
>
> It seems the body is parsed more or less in the same way as in gnumach,
> at least to translate ports. Other fields seems just forwarded to the
> mig stubs, also as in gnumach. Could you be more specific about the
> "untyped messaging"?

Sure. There are two major versions of Mach IPC around; the older typed
version (used in GNU Mach), and the newer untyped version (used in
XNU). As I understand it, the CMU version of Mach (Mach 3) still had
typed MIG, and OSF has introduced untyped IPC, but I don't know Mach
history *that* well, so don't quote me on that (feel free to quote me
on the rest of this message though :D).

The difference is best illustrated with a sample message generated by
MIG. Given the following routine declaration:

routine foo(req: mach_port_t;
    arg1: int;
    arg2: int);

GNU MIG generates these request/reply structs:

    typedef struct {
        mach_msg_header_t Head;
        mach_msg_type_t arg1Type;
        int arg1;
        mach_msg_type_t arg2Type;
        int arg2;
} Request;

typedef struct {
    mach_msg_header_t Head;
    mach_msg_type_t RetCodeType;
    kern_return_t RetCode;
} Reply;

notice how each field is preceded by its corresponding type, as a
mach_msg_type_t. Now, Apple's MIG (running on Darling, of course :),
generates this for the same input:

typedef struct {
    mach_msg_header_t Head;
    NDR_record_t NDR;
    int arg1;
    int arg2;
} Request;

typedef struct {
    mach_msg_header_t Head;
    NDR_record_t NDR;
    kern_return_t RetCode;
    mach_msg_trailer_t trailer;
} Reply;

as you can see the mach_msg_type_t's are gone -- the kernel couldn't
care less about what you put into your message bodies! You can also
notice the appearance of the NDR (which has little to do with Mach IPC
and everything to do with MIG -- more on that later) as well as the
message trailer.

But how do port rights work in this scheme -- after all, the kernel
has to be able to tell port rights and plain integers apart, right?
Well, if you want to pass ports (or OOL ports, or OOL memory), you add
"descriptors" between the message header and the message body. Let's
look at another routine:

routine bar(req: mach_port_t;
    int_arg: int;
    port_arg: mach_port_t;
    out out_port_arg: mach_port_t);

GNU MIG expectedly generates:

typedef struct {
    mach_msg_header_t Head;
    mach_msg_type_t int_argType;
    int int_arg;
    mach_msg_type_t port_argType;
    mach_port_t port_arg;
} Request;

typedef struct {
    mach_msg_header_t Head;
    mach_msg_type_t RetCodeType;
    kern_return_t RetCode;
    mach_msg_type_t out_port_argType;
    mach_port_t out_port_arg;
} Reply;

But look at what Apple MIG does:

typedef struct {
    mach_msg_header_t Head;
    /* start of the kernel processed data */
    mach_msg_body_t msgh_body;
    mach_msg_port_descriptor_t port_arg;
    /* end of the kernel processed data */
    NDR_record_t NDR;
    int int_arg;
} Request

typedef struct {
    mach_msg_header_t Head;
    /* start of the kernel processed data */
    mach_msg_body_t msgh_body;
    mach_msg_port_descriptor_t out_port_arg;
    /* end of the kernel processed data */
    mach_msg_trailer_t trailer;
} Reply;

If MACH_MSGH_BITS_COMPLEX is set, the kernel expects to find a
mach_msg_body_t after the header. msgh_body.msgh_descriptor_count
specifies the number of descriptors, which can be of type
mach_msg_port_descriptor_t, mach_msg_ool_descriptor_t,
mach_msg_ool_ports_descriptor_t, a generic mach_msg_type_descriptor_t
(which only really defines the 'type' member, this is like a 'struct
sockaddr' of descriptors), and in overly new versions of macOS,
mach_msg_guarded_port_descriptor_t (don't ask). A
mach_msg_port_descriptor_t is set up like this:

InP->port_arg.name = port_arg;  // This is the port name itself
InP->port_arg.disposition = 19;  // MACH_MSG_TYPE_COPY_SEND
InP->port_arg.type = MACH_MSG_PORT_DESCRIPTOR;

After the descriptors, we once again have an uninterpreted message
body where MIG puts all the data arguments into (here, NDR and the
int_arg).

Also, notice how both the NDR and the RetCode are gone from the reply
message -- how come? Well, since the message in the unsuccessful case
is always ! MACH_MSGH_BITS_COMPLEX, in case the successful reply
message should be complex, MIG does not bother including RetCode into
it: it looks at whether the received message is complex or not, and
interprets its as either Reply or mig_reply_error_t.

As for NDR: this is the Network Data Representation record:

typedef struct {
    unsigned char       mig_vers;
    unsigned char       if_vers;
    unsigned char       reserved1;
    unsigned char       mig_encoding;
    unsigned char       int_rep;
    unsigned char       char_rep;
    unsigned char       float_rep;
    unsigned char       reserved2;
} NDR_record_t;

It encodes things about the current machine's ABI, like endianness.
This is for transparent Mach IPC over the network (think the old
netname/netmsg server, or the newer NORMA), or just for talking
between PowerPC and Intel processes running on the same machine. In
the typed IPC, the message is broken down into typed chunks, and each
typed chunk can be converted to another representation (e.g.
byte-flipped) by whatever software does the network transparency
magic. With untyped IPC, the info has to be encoded into the message
by MIG on the sender side explicitly, and checked by the MIG on the
receiver side; if the received NDR doesn't match its local one, it
does the conversion.

But also note that in the above example, Reply does not contain an
NDR. This is because it has no untyped fields. But if a
mig_reply_error_t is received instead, that one does contain an NDR,
since it has the RetCode field.

Attaching: two PostScript documents that describe this in some more
detail. It's not very easy to find Mach documentation on the web
nowadays. You can spend hours searching Google for "Untyped MIG" and
still would not find much.

Feel free to ask me more about this!

Sergey

Attachment: MIG_GUIDE.ps
Description: PostScript document

Attachment: MIG_PROTOCOL.ps
Description: PostScript document


reply via email to

[Prev in Thread] Current Thread [Next in Thread]