Difference between revisions of "IPC"

From 3dbrew
Jump to navigation Jump to search
(accidently changed the semantics compared to the C code that was on the page before. I actually don't know what should happen if bit4 and bit5 are set.)
Line 87: Line 87:
 
|-
 
|-
 
| 4
 
| 4
| If set, the handle is closed for the caller. Ignored if bit5 is set.
+
| If set, the handle is closed for the caller. Interaction with bit5 unknown.
 
|-
 
|-
 
| 5
 
| 5

Revision as of 21:57, 31 January 2016

The 3DS software stack follows a server/client architecture, where common functionality is provided by global server processes through interfaces called ports and services. Applications can access this functionality by creating sessions to the provided services. Technically, this is implemented using interprocess communication (IPC).

Concepts

Sessions

Sessions are communication channels consisting of a client and server, through which data can be exchanged in the form of a request and response, respectively. Through sessions, the standard IPC command protocol is implemented. Clients use their client session handle to send IPC commands to the server using svcSendSyncRequest, while servers use svcReplyAndReceive to reply. In both cases, the kernel takes care of transferring IPC command data from the Thread Local Storage of the sending thread to the TLS of the receiving thread. Sessions can be created through svcCreateSession, which provides the caller with the client and server handles.

Sessions are used in their raw form to implement file handles. In this case, fs-module creates a raw session using svcCreateSession, and provides the "fs:USER" client with the resulting client session handle, keeping the server session handle for its own use. These sessions expose their own set of IPC commands which act on the file that was opened through fs:USER.

Ports

Ports are interfaces for managing multiple client sessions to the same high-level server. Ports are created through svcCreatePort, and can be global or private. A global port is created by passing a name for the port to svcCreatePort. Clients can start IPC sessions to global ports by connecting to the port using svcConnectToPort, passing the desired port name. If the port is private, it is not possible to create a session through svcConnectToPort - sessions can only be created if one has a handle to the port itself, obtained from the call to svcCreatePort. When a client wishes to connect to a port (i.e. create a session), the server must accept the new session using svcAcceptSession. The kernel notifies the server whenever a new session is incoming via the server's port handle. The server can wait for this notification through svcWaitSynchronization or svcReplyAndReceive.

The two global ports usually found on retail are "srv:" (service manager) and "err:f" (ErrDisp).

Services

Services are an abstraction of ports that are managed by service manager. Services cannot be connected to through svcConnectToPort, as the underlying port is a private port. The service name is instead entirely handled by service manager, and the kernel is not aware of it. Clients are instead expected to open a session to a service using the "srv:" port command GetServiceHandle. The client process must pass an access permission check for each service (by name) that it attempts to request a session with. These permissions are granted in the "service access control" of the title's extended header. A service is registered with service manager using the command RegisterService.

Internally, service manager creates services by creating a private port which it associates with the desired service name. The resulting server port handle is returned in the RegisterService response, for the server's own use (see ports). To create sessions to a service on behalf of a client, service manager uses svcCreateSessionToPort, passing the client port handle it obtained when creating the port, to the kernel. Service manager then returns the resulting session handle to the client in the GetServiceHandle response.

The majority of 3DS inter-process communication is implemented as services.

Command Structure

IPC commands are written to the Thread Local Storage at offset 0x80 and submitted using svcSendSyncRequest. If the kernel was able to dispatch the request, the server reply will be written to TLS+0x80. Often the second word of the response data is the error code (or 0 if success).

Every IPC command sent to IPC server processes starts with a u32 header code. Parameters, if any, are written following this header. There are "normal parameters", which are fixed-size words, and there are "translate parameters", which are of flexible size and each of which begins with a header. The entire command has the following structure:

Word Size Description
0 1 Header code
1 x Normal parameters
x y Translate parameters

The IPC command header has the following structure:

Bits Description
0-5 Total size in words of the translate parameters (=y). Note that this is in general different from the number of translate parameters
6-11 Number of normal parameters (=x)
12-15 Unused
16-31 Command ID

Each translate parameter starts with a translation descriptor:

Bits Description
1-3 type

Translate parameters are modified/translated transparently by the kernel. They are used to transfer handles/buffers between the different processes.

The type of parameter is described by the bits 1-3 in the translation descriptor. Parameter types accepted for sending by the kernel are: 0, 1, 2, 5, 6, 7.

Type 0 is used to send handles across processes. The corresponding translation descriptor looks as follows:

Bits Description
4 If set, the handle is closed for the caller. Interaction with bit5 unknown.
5 If set, the process ID is written to value. Otherwise, translate the handle between client and server.

For replies, only 0, 1, 5, 6, 7 are allowed. In other words any type 2 fields must be zeroed before calling svcReplyAndReceive on the server-side. For replies, type 0, 1, and 2 are ignored. Types 5, 6, and 7 do something with the mem pointer upon reply. The type 0 descriptor can be used to ignore parameters. The number of parameters covered by a type-0 descriptor is (desc >> 26) + 1.

Type Usual form Description
0 0x00000000 | ((num_handles-1)<<26)

<handle 0>

<handle 1> ...

The corresponding values are KHandles, that should be closed in calling process. Or zero otherwise.
0 0x00000010 | ((num_handles-1)<<26)

<handle 0>

<handle 1> ...

The corresponding values are KHandles, that will be duplicated. Or zero otherwise.
0 0x00000020

<placeholder>

Let kernel set value to calling process ProcessID.
1 0x00000002 | (size<<14) | (static_buffer_id<<10)

<ptr>

The corresponding value contains a ptr to a buffer of said size, that should be copied to an already set-up buffer in destination process at Thread Local Storage offset 0x180 + static_buffer_id*8. The static_buffer_id is only 4 bits, making it possible for at most up to 16 buffers in total per thread.
2 0x00000004 | (size<<8) | (id<<4)

<ptr>

This is used for RW buffers over PXI. The address written to the destination cmd-buf is a phys-addr for a table located in the BASE memregion. This table contains the phys-addrs for the actual data, the array entries have the following format: {u32 *datachunk_physaddr, u32 datachunk_bytesize}.
3 0x00000006 | (size<<8) | (id<<4)

<ptr>

Same as above except for read-only buffer.
4 0x00000008 This command will cause a kernelpanic.
5 0x0000000A | (size<<4)

<ptr>

The corresponding value contains a ptr to a buffer of said size. It is mapped R- in the destination process??
6 0x0000000C | (size<<4)

<ptr>

The corresponding value contains a ptr to a buffer of said size. It is mapped -W in the destination process??
7 0x0000000E | (size<<4)

<ptr>

The corresponding value contains a ptr to a buffer of said size. It is mapped RW in the destination process??

Buffers from commands 5,6,7 will get mapped at virtual address 0x04000000+ in destination process.