Migration services: Difference between revisions
No edit summary |
No edit summary |
||
(3 intermediate revisions by the same user not shown) | |||
Line 169: | Line 169: | ||
| 0 || GetUid | | 0 || GetUid | ||
|- | |- | ||
| 1 || GetServerProfile | | 1 || [[#GetServerProfile|GetServerProfile]] | ||
|- | |- | ||
| 100 || PrepareAsync | | 100 || PrepareAsync | ||
Line 191: | Line 191: | ||
| 500 || Abort | | 500 || Abort | ||
|} | |} | ||
=== GetServerProfile === | |||
Takes a type-0x1A output buffer containing a [[#ServerProfile|ServerProfile]]. | |||
== IClient == | == IClient == | ||
Line 289: | Line 292: | ||
| 1 || [7.0.0-19.0.1] GetApplicationId | | 1 || [7.0.0-19.0.1] GetApplicationId | ||
|- | |- | ||
| 2 || GetServerProfile | | 2 || [[#GetServerProfile_2|GetServerProfile]] | ||
|- | |- | ||
| 3 || [17.0.0+] ListApplicationIds | | 3 || [17.0.0+] ListApplicationIds | ||
Line 299: | Line 302: | ||
| 102 || [20.0.0+] | | 102 || [20.0.0+] | ||
|- | |- | ||
| 200 || WaitConnectionAsync | | 200 || [[#WaitConnectionAsync|WaitConnectionAsync]] | ||
|- | |- | ||
| 201 || GetClientProfile | | 201 || GetClientProfile | ||
Line 321: | Line 324: | ||
| 999 || [8.0.0+] DebugWaitStateSynchronizationFinalizedAsync | | 999 || [8.0.0+] DebugWaitStateSynchronizationFinalizedAsync | ||
|} | |} | ||
=== GetServerProfile === | |||
Takes a type-0x1A output buffer containing a [[#ServerProfile|ServerProfile]]. | |||
=== PrepareAsync === | === PrepareAsync === | ||
Line 328: | Line 334: | ||
[20.0.0+] The above policy functionality is no longer present. This now eventually uses network request [[Network|transfer_events/start]]. | [20.0.0+] The above policy functionality is no longer present. This now eventually uses network request [[Network|transfer_events/start]]. | ||
=== WaitConnectionAsync === | |||
No input, returns an [[#IAsyncContext|IAsyncContext]]. | |||
The async task does the following: | |||
* ... | |||
* Initializes the data used for the AdvertiseData. | |||
* Calls a func which: | |||
** Calls a func which handles [[LDN_services|ldn]] initialization and network creation. SetProtocol is also used with a value from state. | |||
** Calls a func which: | |||
*** Handles setup for the server socket. | |||
*** Uses [[LDN_services|SetAdvertiseData]] with the above AdvertiseData buffer. | |||
*** Waits for a ldn Node to connect, with timeout etc handling. | |||
*** Then [[LDN_services|SetStationAcceptPolicy]] is used with value 3 (WhiteList), and [[LDN_services|AddAcceptFilterEntry]] is used. | |||
** Calls a func which waits for a socket connection (with timeout etc), and handles socket setup for it. | |||
** Uses [[LDN_services|SetAdvertiseData]] with buf/size = 0 (empty AdvertiseData). | |||
** Uses [[LDN_services|SetStationAcceptPolicy]] with value 1 (AlwaysReject). | |||
** Enters a loop waiting for a state field to become value 0x3. Two funcs are called repeatedly in this loop, with cleanup and return being handled if these return error. | |||
*** The first func receives socket data and handles it. The second func calls a func to get data for sending, then sends the socket data. Both have timeout etc handling. | |||
** Updates state and returns 0. | |||
* ... | |||
=== Cmd510 === | === Cmd510 === | ||
Line 407: | Line 434: | ||
! Cmd || Name | ! Cmd || Name | ||
|- | |- | ||
| 0 || | | 0 || [[#GetServerProfile_3|GetServerProfile]] | ||
|- | |- | ||
| 10 || | | 10 || | ||
Line 445: | Line 472: | ||
| 900 || | | 900 || | ||
|} | |} | ||
=== GetServerProfile === | |||
Unofficial name. | |||
Takes a type-0x1A output buffer containing a [[#ServerProfile|ServerProfile]]. | |||
=== Cmd230 === | === Cmd230 === | ||
Line 636: | Line 668: | ||
= ServerProfile = | = ServerProfile = | ||
This is a 0x100-byte struct. | This is a 0x100-byte struct. | ||
It's unknown whether user/savedata/device use the same struct (size is the same for these). | |||
Any data here which is initialized is usually zeros? | |||
= Protocol = | |||
Once connected with [[LDN_services|ldn]], the client node connects to the server with TCP port 441. | |||
The first byte of messages is the message-type. | |||
Messages are encrypted with AES-128-GCM. The key is derived during the initial message-handling loop (WaitConnectionAsync). | |||
= AdvertiseData = | = AdvertiseData = | ||
Line 642: | Line 685: | ||
The global constant used with hashing below is the same regardless of the AdvertiseData. | The global constant used with hashing below is the same regardless of the AdvertiseData. | ||
Later the server also sets the AdvertiseData to {0x10-byte | Later the server also sets the AdvertiseData to {0x10-byte Uuid previously used below}. | ||
The Uuid used below is generated with <code>nn::util::GenerateUuid</code>. | |||
== user == | == user == | ||
Line 695: | Line 740: | ||
! Offset || Size || Description | ! Offset || Size || Description | ||
|- | |- | ||
| 0x0 || 0x10 || | | 0x0 || 0x10 || Uuid | ||
|- | |- | ||
| 0x10 || 0x8 || AccountId | | 0x10 || 0x8 || AccountId | ||
Line 711: | Line 756: | ||
The hash is calculated by using SHA256-update with each field separately, followed by global constant: | The hash is calculated by using SHA256-update with each field separately, followed by global constant: | ||
* | * Uuid | ||
* AccountId | * AccountId | ||
* ApplicationId | * ApplicationId | ||
Line 725: | Line 770: | ||
! Offset || Size || Description | ! Offset || Size || Description | ||
|- | |- | ||
| 0x0 || 0x10 || | | 0x0 || 0x10 || Uuid | ||
|- | |- | ||
| 0x10 || 0x40 (0x8*0x8) || Array of u64s with the below count. | | 0x10 || 0x40 (0x8*0x8) || Array of u64s with the below count. | ||
Line 739: | Line 784: | ||
The hash is calculated by using SHA256-update with each field separately, followed by global constant: | The hash is calculated by using SHA256-update with each field separately, followed by global constant: | ||
* | * Uuid | ||
* +0x10 size 0x40-bytes | * +0x10 size 0x40-bytes | ||
* +0x50 size 0x4-bytes | * +0x50 size 0x4-bytes |
Latest revision as of 19:59, 11 October 2025
Migration is a sysmodule designed for handling the Switch-to-Switch transfer system introduced in firmware 4.0.0.
With newer system-versions this sysmodule is only running when qlaunch is using migration from the relevant transfer menus (see ActivateMigrationService/DeactivateMigrationService).
mig:usr
This is "nn::migration::user::IService"
Cmd | Name |
---|---|
0 | [19.0.0+] |
1 | [20.0.0+] |
2 | [20.0.0+] |
10 | [7.0.0-19.0.1] TryGetLastUserMigrationInfo ([4.0.0-6.2.0] TryGetLastMigrationInfo) |
11 | [20.0.0+] |
100 | [7.0.0+] CreateUserMigrationServer ([4.0.0-6.2.0] CreateServer) |
101 | [7.0.0+] ResumeUserMigrationServer ([4.0.0-6.2.0] ResumeServer) |
200 | [7.0.0+] CreateUserMigrationClient ([4.0.0-6.2.0] CreateClient) |
201 | [7.0.0+] ResumeUserMigrationClient ([4.0.0-6.2.0] ResumeClient) |
1001 | [8.0.0+] GetSaveDataMigrationPolicyInfoAsync |
1010 | [7.0.0+] TryGetLastSaveDataMigrationInfo |
1100 | [7.0.0-19.0.1] CreateSaveDataMigrationServer |
1101 | [7.0.0+] ResumeSaveDataMigrationServer |
1110 | [17.0.0+] |
1200 | [7.0.0+] CreateSaveDataMigrationClient |
1201 | [7.0.0+] ResumeSaveDataMigrationClient |
2001 | [20.0.0+] |
2010 | [20.0.0+] |
2100 | [20.0.0+] |
2110 | [20.0.0+] |
2200 | [20.0.0+] |
2210 | [20.0.0+] |
2220 | [20.0.0+] |
2230 | [20.0.0+] |
2231 | [20.0.0+] |
2232 | [20.0.0+] |
2233 | [20.0.0+] |
2234 | [20.0.0+] |
2250 | [20.0.0+] |
2260 | [20.0.0+] |
2270 | [20.0.0+] |
2280 | [20.0.0+] |
2300 | [20.0.0+] |
2310 | [20.0.0+] |
2400 | [20.0.0+] |
2420 | [20.0.0+] |
CreateUserMigrationServer
Takes a total of 0x18-bytes of input, a type-0x19 input buffer, a TransferMemory handle, returns an #IServer.
ResumeUserMigrationServer
Takes an input u32, a TransferMemory handle, returns an #IServer.
CreateUserMigrationClient
Takes an input u32, a type-0x19 input buffer, a TransferMemory handle, returns an #IClient.
ResumeUserMigrationClient
Takes an input u32, a TransferMemory handle, returns an #IClient.
GetSaveDataMigrationPolicyInfoAsync
Takes an ApplicationId, returns an #IAsyncSaveDataMigrationPolicyInfoContext.
This is used by qlaunch before the actual savedata transfer is started.
This starts a network request for save_data_migration_policy.
[20.0.0+] This was stubbed. The network request is no longer done and the various IAsyncSaveDataMigrationPolicyInfoContext cmds just return success (besides GetSystemEvent). GetSaveDataMigrationPolicyInfo just returns the data which was set during object creation.
CreateSaveDataMigrationServer
Takes a total of 0x20-bytes of input, a type-0x19 input buffer, a TransferMemory handle, returns an IServer.
ResumeSaveDataMigrationServer
Takes an input u32, a TransferMemory handle, returns an IServer.
Cmd1110
Takes a total of 0x18-bytes of input, a type-0x19 input buffer, a type-0x5 input buffer containing an array of u64s, and a TransferMemory handle. Returns an IServer.
This is identical to CreateSaveDataMigrationServer except the u64-array is passed directly instead of from a single input u64.
CreateSaveDataMigrationClient
Takes an input u32, a type-0x19 input buffer, a TransferMemory handle, returns an IClient.
ResumeSaveDataMigrationClient
Takes an input u32, a TransferMemory handle, returns an IClient.
Cmd2100
Takes 4-bytes of input, a type-0x19 input buffer containing a 0x100-byte struct, a handle. Returns an IServer.
Cmd2110
Takes 4-bytes of input, a handle. Returns an IServer.
Cmd2200
Takes 4-bytes of input, a type-0x19 input buffer containing a 0x100-byte struct, a handle. Returns an IClient.
On NX this just calls a logging func and returns an error.
Cmd2210
Takes 4-bytes of input, a handle. Returns an IClient.
On NX this just calls a logging func and returns an error.
Cmd2250
Takes a total of 0x18-bytes of input, a handle. Returns an IUnknown.
Cmd2260
Takes 4-bytes of input, a handle. Returns an IUnknown.
Cmd2280
Takes an input u64, returns an #IAsyncContext.
This starts a network request for transfer_events/%lld/rollback.
Cmd2300
Takes a total of 0x18-bytes of input, a handle. Returns an IUploader.
Cmd2310
Takes 4-bytes of input, a handle. Returns an IUploader.
Cmd2400
Takes a total of 0x10-bytes of input, a handle. Returns an IDownloader.
Cmd2420
Takes 4-bytes of input, a handle. Returns an IDownloader.
IServer
This is "nn::migration::user::IServer"
Cmd | Name |
---|---|
0 | GetUid |
1 | GetServerProfile |
100 | PrepareAsync |
101 | GetConnectionRequirement |
102 | [20.0.0+] |
200 | WaitConnectionAsync |
201 | GetClientProfile |
202 | AcceptConnectionAsync |
203 | DeclineConnectionAsync |
300 | ProcessTransferAsync |
400 | CompleteAsync |
500 | Abort |
GetServerProfile
Takes a type-0x1A output buffer containing a ServerProfile.
IClient
This is "nn::migration::user::IClient"
Cmd | Name |
---|---|
0 | GetClientProfile |
10 | CreateLoginSession |
11 | GetNetworkServiceAccountId |
12 | GetUserNickname |
13 | GetUserProfileImage |
100 | PrepareAsync |
101 | GetConnectionRequirement |
102 | [20.0.0+] |
200 | ScanServersAsync |
201 | ListServers |
210 | ConnectByServerIdAsync |
300 | GetStorageShortfall |
301 | GetTotalTransferInfo |
302 | GetImmigrantUid |
310 | GetCurrentTransferInfo |
311 | GetCurrentRelatedApplications |
320 | TransferNextAsync |
350 | SuspendAsync |
400 | CompleteAsync |
500 | Abort |
999 | DebugSynchronizeStateInFinalizationAsync |
IAsyncContext
This is "nn::migration::detail::IAsyncContext".
Cmd | Name |
---|---|
0 | GetSystemEvent |
1 | Cancel |
2 | HasDone |
3 | GetResult |
IAsyncSaveDataMigrationPolicyInfoContext
This is "nn::migration::detail::IAsyncSaveDataMigrationPolicyInfoContext".
Cmd | Name |
---|---|
0 | GetSystemEvent |
1 | Cancel |
2 | HasDone |
3 | GetResult |
100 | GetSaveDataMigrationPolicyInfo |
IServer
This is "nn::migration::savedata::IServer".
This was added with [7.0.0+].
Cmd | Name |
---|---|
0 | GetUid |
1 | [7.0.0-19.0.1] GetApplicationId |
2 | GetServerProfile |
3 | [17.0.0+] ListApplicationIds |
100 | PrepareAsync |
101 | GetConnectionRequirement |
102 | [20.0.0+] |
200 | WaitConnectionAsync |
201 | GetClientProfile |
202 | [20.0.0+] |
203 | [20.0.0+] |
210 | [8.0.0+] WaitAcceptanceAsync |
300 | ProcessTransferAsync |
400 | CompleteAsync |
500 | [7.0.0-19.0.1] Abort |
510 | [19.0.0+] |
998 | [8.0.0+] DebugTryGetState |
999 | [8.0.0+] DebugWaitStateSynchronizationFinalizedAsync |
GetServerProfile
Takes a type-0x1A output buffer containing a ServerProfile.
PrepareAsync
No input, returns an IAsyncContext.
Besides various other functionality, the async task also uses functionality similar to GetSaveDataMigrationPolicyInfoAsync, throwing an error if needed.
[20.0.0+] The above policy functionality is no longer present. This now eventually uses network request transfer_events/start.
WaitConnectionAsync
No input, returns an IAsyncContext.
The async task does the following:
- ...
- Initializes the data used for the AdvertiseData.
- Calls a func which:
- Calls a func which handles ldn initialization and network creation. SetProtocol is also used with a value from state.
- Calls a func which:
- Handles setup for the server socket.
- Uses SetAdvertiseData with the above AdvertiseData buffer.
- Waits for a ldn Node to connect, with timeout etc handling.
- Then SetStationAcceptPolicy is used with value 3 (WhiteList), and AddAcceptFilterEntry is used.
- Calls a func which waits for a socket connection (with timeout etc), and handles socket setup for it.
- Uses SetAdvertiseData with buf/size = 0 (empty AdvertiseData).
- Uses SetStationAcceptPolicy with value 1 (AlwaysReject).
- Enters a loop waiting for a state field to become value 0x3. Two funcs are called repeatedly in this loop, with cleanup and return being handled if these return error.
- The first func receives socket data and handles it. The second func calls a func to get data for sending, then sends the socket data. Both have timeout etc handling.
- Updates state and returns 0.
- ...
Cmd510
No input, returns an IAsyncContext.
Besides other functionality, this async task uses network request transfer_events/%lld/abort_transfer.
IClient
This is "nn::migration::savedata::IClient".
This was added with [7.0.0+].
Cmd | Name |
---|---|
0 | GetClientProfile |
100 | GetConnectionRequirement |
101 | [20.0.0+] |
200 | ScanServersAsync |
201 | ListServers |
210 | ConnectByServerIdAsync |
220 | [8.0.0+] AcceptAsync |
221 | [8.0.0+] DeclineAsync |
300 | GetStorageShortfall |
301 | GetTotalTransferInfo |
302 | GetImmigrantUid |
303 | GetApplicationId |
304 | [17.0.0+] ListApplicationIds |
310 | GetCurrentTransferInfo |
320 | TransferNextAsync |
350 | SuspendAsync |
400 | CompleteAsync |
500 | [7.0.0-19.0.1] Abort |
510 | [19.0.0+] |
996 | [8.0.0+] DebugTryGetState |
997 | [8.0.0+] DebugSynchronizeStateInFinalization0Async |
998 | [8.0.0+] DebugSynchronizeStateInFinalization1Async |
999 | DebugSynchronizeStateFinalizedAsync |
ListServers
[20.0.0+] The struct size is now 0x130-bytes instead of 0x128-bytes.
CompleteAsync
No input, returns an IAsyncContext.
Besides other functionality, this async task uses network request transfer_events/%lld/finish_transfer.
IServer
This is "nn::migration::device::IServer".
This was added with [20.0.0+].
Cmd | Name |
---|---|
0 | GetServerProfile |
10 | |
20 | |
21 | |
30 | |
40 | |
50 | |
100 | |
110 | |
111 | |
120 | |
130 | |
200 | |
230 | |
290 | |
300 | |
400 | |
510 | |
900 |
GetServerProfile
Unofficial name.
Takes a type-0x1A output buffer containing a ServerProfile.
Cmd230
No input, returns an IAsyncContext.
Besides other functionality, this async task uses network request transfer_events/start.
Cmd510
No input, returns an IAsyncContext.
Besides other functionality, this async task uses network request transfer_events/%lld/abort_transfer.
IClient
This is "nn::migration::device::IClient".
This was added with [20.0.0+].
Cmd | Name |
---|---|
10 | |
20 | |
30 | |
100 | |
110 | |
111 | |
200 | |
210 | |
220 | |
230 | |
240 | |
250 | |
290 | |
300 | |
310 | |
320 | |
330 | |
340 | |
400 | |
500 | |
610 | |
700 | |
710 | |
720 |
IUnknown
This was added with [20.0.0+].
Cmd | Name |
---|---|
0 | |
1 | |
2 | |
3 | |
100 | |
110 |
IUploader
This is "nn::migration::device::IUploader".
This was added with [20.0.0+].
Cmd | Name |
---|---|
90 | |
100 | |
310 | |
320 | |
330 | |
340 | |
350 | |
400 | |
500 | |
610 | |
900 |
Cmd100
No input, returns an IAsyncContext.
Besides other functionality, this async task uses network request transfer_events/start.
Cmd500
No input, returns an IAsyncContext.
Besides other functionality, this async task uses network request transfer_events/%lld/finish_upload.
Cmd610
No input, returns an IAsyncContext.
Besides other functionality, this async task uses network request transfer_events/%lld/abort_upload.
Cmd900
Takes a total of 4-bytes of input, returns an IAsyncContext.
Besides other functionality, this async task may call the same network-request func as #Cmd500.
IDownloader
This is "nn::migration::device::IDownloader".
This was added with [20.0.0+].
Cmd | Name |
---|---|
10 | |
20 | |
30 | |
90 | |
100 | |
300 | |
310 | |
320 | |
330 | |
400 | |
500 | |
610 | |
620 | |
900 |
Cmd500
No input, returns an IAsyncContext.
Besides other functionality, this async task uses network request transfer_events/%lld/finish_download.
Cmd900
Takes a total of 4-bytes of input, returns an IAsyncContext.
Besides other functionality, this async task may call the same network-request func as Cmd500.
ServerProfile
This is a 0x100-byte struct.
It's unknown whether user/savedata/device use the same struct (size is the same for these).
Any data here which is initialized is usually zeros?
Protocol
Once connected with ldn, the client node connects to the server with TCP port 441.
The first byte of messages is the message-type.
Messages are encrypted with AES-128-GCM. The key is derived during the initial message-handling loop (WaitConnectionAsync).
AdvertiseData
These sections document the initial AdvertiseData used by migration.
The global constant used with hashing below is the same regardless of the AdvertiseData.
Later the server also sets the AdvertiseData to {0x10-byte Uuid previously used below}.
The Uuid used below is generated with nn::util::GenerateUuid
.
user
Used by nn::migration::user::*.
Offset | Size | Description |
---|---|---|
0x0 | 0x8 | AccountId |
0x8 | 0x58 | Unused |
0x60 | 0x100 | ServerProfile |
0x160 | 0x20 | SHA256 hash |
The hash is calculated by using SHA256-update with each field separately, followed by global constant:
- AccountId
- ServerProfile
- 0x100-byte global constant
savedata
Used by nn::migration::savedata::*.
Offset | Size | Description |
---|---|---|
0x0 | 0x8 | AccountId |
0x8 | 0x8 | ApplicationId |
0x10 | 0x50 | Unused |
0x60 | 0x100 | ServerProfile |
0x160 | 0x20 | SHA256 hash |
The hash is calculated by using SHA256-update with each field separately, followed by global constant:
- AccountId
- ApplicationId
- ServerProfile
- 0x100-bytes global constant
[20.0.0+]:
Offset | Size | Description |
---|---|---|
0x0 | 0x10 | Uuid |
0x10 | 0x8 | AccountId |
0x18 | 0x8 | ApplicationId |
0x20 | 0x4 | ProductModel |
0x24 | 0x4 | Padding |
0x28 | 0x100 | ServerProfile |
0x128 | 0x20 | SHA256 hash |
The hash is calculated by using SHA256-update with each field separately, followed by global constant:
- Uuid
- AccountId
- ApplicationId
- ProductModel
- ServerProfile
- 0x100-bytes global constant
device
Used by nn::migration::device::*.
Offset | Size | Description |
---|---|---|
0x0 | 0x10 | Uuid |
0x10 | 0x40 (0x8*0x8) | Array of u64s with the below count. |
0x50 | 0x4 | Count for the above array. |
0x54 | 0xC | Unused |
0x60 | 0x100 | ServerProfile |
0x160 | 0x20 | SHA256 hash |
The hash is calculated by using SHA256-update with each field separately, followed by global constant:
- Uuid
- +0x10 size 0x40-bytes
- +0x50 size 0x4-bytes
- ServerProfile
- 0x100-bytes global constant
Notes
Savedata transfer ("nn::migration::savedata::IServer"/"nn::migration::savedata::IClient") requires that accounts are linked to the same network-account. acc:su IManagerForSystemService GetAccountId is used here. The server stores this Id into state, and also stores it in the ldn AdvertiseData. The client also verifies the Id in AdvertiseData against the Id from GetAccountId for the account(s) being used, during ScanServersAsync.
[20.0.0+] ISaveDataTransferManagerWithDivision is now used instead of ISaveDataTransferManager. Besides SetKeySeedPackage being used now as needed, SetLocalKeySeedPackage is now used by the relevant functionality in IClient TransferNextAsync.
[S2] qlaunch now has an additional menu once send-savedata is selected, for selecting whether to send to Nintendo Switch or Nintendo Switch 2. If Switch 2 is selected, and there's a S1 receiving, the S2 will display a message regarding wrong target system. The receiving system also displays an error. Note that savedata for S2-only applications is only listed with the dest-S2 option (S1-applications are also listed).