Migration services: Difference between revisions
replaced 'new' description with firmware version when introduced |
No edit summary |
||
(31 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
Migration is a sysmodule designed for handling the Switch-to-Switch transfer system introduced in firmware [[4.0.0]]. | 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 [[Applet_Manager_services#ActivateMigrationService|ActivateMigrationService]]/[[Applet_Manager_services#DeactivateMigrationService|DeactivateMigrationService]]). | |||
= mig:usr = | = mig:usr = | ||
Line 8: | Line 10: | ||
! Cmd || Name | ! Cmd || Name | ||
|- | |- | ||
| 10 || TryGetLastMigrationInfo | | 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|CreateUserMigrationServer]] ([4.0.0-6.2.0] CreateServer) | |||
|- | |||
| 101 || [7.0.0+] [[#ResumeUserMigrationServer|ResumeUserMigrationServer]] ([4.0.0-6.2.0] ResumeServer) | |||
|- | |||
| 200 || [7.0.0+] [[#CreateUserMigrationClient|CreateUserMigrationClient]] ([4.0.0-6.2.0] CreateClient) | |||
|- | |||
| 201 || [7.0.0+] [[#ResumeUserMigrationClient|ResumeUserMigrationClient]] ([4.0.0-6.2.0] ResumeClient) | |||
|- | |||
| 1001 || [8.0.0+] [[#GetSaveDataMigrationPolicyInfoAsync|GetSaveDataMigrationPolicyInfoAsync]] | |||
|- | |||
| 1010 || [7.0.0+] TryGetLastSaveDataMigrationInfo | |||
|- | |||
| 1100 || [7.0.0-19.0.1] [[#CreateSaveDataMigrationServer|CreateSaveDataMigrationServer]] | |||
|- | |||
| 1101 || [7.0.0+] [[#ResumeSaveDataMigrationServer|ResumeSaveDataMigrationServer]] | |||
|- | |||
| 1110 || [17.0.0+] | |||
|- | |||
| 1200 || [7.0.0+] [[#CreateSaveDataMigrationClient|CreateSaveDataMigrationClient]] | |||
|- | |||
| 1201 || [7.0.0+] [[#ResumeSaveDataMigrationClient|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 [[NCM_services#ApplicationId|ApplicationId]], returns an [[#IAsyncSaveDataMigrationPolicyInfoContext]]. | |||
This is used by [[qlaunch]] before the actual savedata transfer is started. | |||
This starts a network request for [[Network|save_data_migration_policy]]. | |||
[20.0.0+] This was stubbed. The network request is no longer done and the various [[#IAsyncSaveDataMigrationPolicyInfoContext|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_2|IServer]]. | |||
== ResumeSaveDataMigrationServer == | |||
Takes an input u32, a TransferMemory handle, returns an [[#IServer_2|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_2|IServer]]. | |||
This is identical to [[#CreateSaveDataMigrationServer|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_2|IClient]]. | |||
== ResumeSaveDataMigrationClient == | |||
Takes an input u32, a TransferMemory handle, returns an [[#IClient_2|IClient]]. | |||
== Cmd2100 == | |||
Takes 4-bytes of input, a type-0x19 input buffer containing a 0x100-byte struct, a handle. Returns an [[#IServer_3|IServer]]. | |||
== Cmd2110 == | |||
Takes 4-bytes of input, a handle. Returns an [[#IServer_3|IServer]]. | |||
== Cmd2200 == | |||
Takes 4-bytes of input, a type-0x19 input buffer containing a 0x100-byte struct, a handle. Returns an [[#IClient_3|IClient]]. | |||
On NX this just calls a logging func and returns an error. | |||
== Cmd2210 == | |||
Takes 4-bytes of input, a handle. Returns an [[#IClient_3|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|IUnknown]]. | |||
== Cmd2260 == | |||
Takes 4-bytes of input, a handle. Returns an [[#IUnknown|IUnknown]]. | |||
== Cmd2280 == | |||
Takes an input u64, returns an [[#IAsyncContext]]. | |||
This starts a network request for [[Network|transfer_events/%lld/rollback]]. | |||
== Cmd2300 == | |||
Takes a total of 0x18-bytes of input, a handle. Returns an [[#IUploader|IUploader]]. | |||
== Cmd2310 == | |||
Takes 4-bytes of input, a handle. Returns an [[#IUploader|IUploader]]. | |||
== Cmd2400 == | |||
Takes a total of 0x10-bytes of input, a handle. Returns an [[#IDownloader|IDownloader]]. | |||
== Cmd2420 == | |||
Takes 4-bytes of input, a handle. Returns an [[#IDownloader|IDownloader]]. | |||
== IServer == | == IServer == | ||
Line 40: | Line 169: | ||
| 0 || GetUid | | 0 || GetUid | ||
|- | |- | ||
| 1 || GetServerProfile | | 1 || [[#GetServerProfile|GetServerProfile]] | ||
|- | |- | ||
| 100 || PrepareAsync | | 100 || PrepareAsync | ||
|- | |- | ||
| 101 || GetConnectionRequirement | | 101 || GetConnectionRequirement | ||
|- | |||
| 102 || [20.0.0+] | |||
|- | |- | ||
| 200 || WaitConnectionAsync | | 200 || WaitConnectionAsync | ||
Line 53: | Line 184: | ||
|- | |- | ||
| 203 || DeclineConnectionAsync | | 203 || DeclineConnectionAsync | ||
|- | |- | ||
| 300 || ProcessTransferAsync | | 300 || ProcessTransferAsync | ||
Line 61: | Line 190: | ||
|- | |- | ||
| 500 || Abort | | 500 || Abort | ||
|} | |} | ||
=== GetServerProfile === | |||
Takes a type-0x1A output buffer containing a [[#ServerProfile|ServerProfile]]. | |||
== IClient == | == IClient == | ||
Line 88: | Line 215: | ||
|- | |- | ||
| 101 || GetConnectionRequirement | | 101 || GetConnectionRequirement | ||
|- | |||
| 102 || [20.0.0+] | |||
|- | |- | ||
| 200 || ScanServersAsync | | 200 || ScanServersAsync | ||
Line 94: | Line 223: | ||
|- | |- | ||
| 210 || ConnectByServerIdAsync | | 210 || ConnectByServerIdAsync | ||
|- | |- | ||
| 300 || GetStorageShortfall | | 300 || GetStorageShortfall | ||
Line 117: | Line 242: | ||
| 500 || Abort | | 500 || Abort | ||
|- | |- | ||
| | | 999 || DebugSynchronizeStateInFinalizationAsync | ||
|} | |||
== IAsyncContext == | |||
This is "nn::migration::detail::IAsyncContext". | |||
{| class="wikitable" border="1" | |||
|- | |- | ||
| | ! Cmd || Name | ||
|- | |- | ||
| | | 0 || GetSystemEvent | ||
|- | |||
| 1 || Cancel | |||
|- | |- | ||
| | | 2 || HasDone | ||
|- | |- | ||
| 3 || GetResult | |||
|} | |} | ||
== | == IAsyncSaveDataMigrationPolicyInfoContext == | ||
This is "nn::migration:: | This is "nn::migration::detail::IAsyncSaveDataMigrationPolicyInfoContext". | ||
{| class="wikitable" border="1" | {| class="wikitable" border="1" | ||
Line 142: | Line 276: | ||
| 3 || GetResult | | 3 || GetResult | ||
|- | |- | ||
| 100 || GetSaveDataMigrationPolicyInfo | |||
|} | |} | ||
Line 153: | Line 288: | ||
! Cmd || Name | ! Cmd || Name | ||
|- | |- | ||
| 0 || | | 0 || GetUid | ||
|- | |||
| 1 || [7.0.0-19.0.1] GetApplicationId | |||
|- | |||
| 2 || [[#GetServerProfile_2|GetServerProfile]] | |||
|- | |||
| 3 || [17.0.0+] ListApplicationIds | |||
|- | |||
| 100 || [[#PrepareAsync|PrepareAsync]] | |||
|- | |||
| 101 || GetConnectionRequirement | |||
|- | |||
| 102 || [20.0.0+] | |||
|- | |||
| 200 || [[#WaitConnectionAsync|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|ServerProfile]]. | |||
=== PrepareAsync === | |||
No input, returns an [[#IAsyncContext|IAsyncContext]]. | |||
Besides various other functionality, the async task also uses functionality similar to [[#GetSaveDataMigrationPolicyInfoAsync|GetSaveDataMigrationPolicyInfoAsync]], throwing an error if needed. | |||
[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 === | |||
No input, returns an [[#IAsyncContext|IAsyncContext]]. | |||
Besides other functionality, this async task uses network request [[Network|transfer_events/%lld/abort_transfer]]. | |||
== IClient == | |||
This is "nn::migration::savedata::IClient". | |||
This was added with [7.0.0+]. | |||
{| class="wikitable" border="1" | |||
|- | |||
! Cmd || Name | |||
|- | |||
| 0 || GetClientProfile | |||
|- | |||
| 100 || GetConnectionRequirement | |||
|- | |||
| 101 || [20.0.0+] | |||
|- | |||
| 200 || ScanServersAsync | |||
|- | |||
| 201 || [[#ListServers|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|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|IAsyncContext]]. | |||
Besides other functionality, this async task uses network request [[Network|transfer_events/%lld/finish_transfer]]. | |||
== IServer == | |||
This is "nn::migration::device::IServer". | |||
This was added with [20.0.0+]. | |||
{| class="wikitable" border="1" | |||
|- | |||
! Cmd || Name | |||
|- | |||
| 0 || [[#GetServerProfile_3|GetServerProfile]] | |||
|- | |||
| 10 || | |||
|- | |||
| 20 || | |||
|- | |||
| 21 || | |||
|- | |||
| 30 || | |||
|- | |- | ||
| | | 40 || | ||
|- | |- | ||
| | | 50 || | ||
|- | |- | ||
| 100 || | | 100 || | ||
|- | |- | ||
| | | 110 || | ||
|- | |||
| 111 || | |||
|- | |||
| 120 || | |||
|- | |||
| 130 || | |||
|- | |- | ||
| 200 || | | 200 || | ||
|- | |- | ||
| | | 230 || | ||
|- | |||
| 290 || | |||
|- | |- | ||
| 300 || | | 300 || | ||
Line 171: | Line 468: | ||
| 400 || | | 400 || | ||
|- | |- | ||
| | | 510 || | ||
|- | |- | ||
| 900 || | |||
|} | |} | ||
=== GetServerProfile === | |||
Unofficial name. | |||
Takes a type-0x1A output buffer containing a [[#ServerProfile|ServerProfile]]. | |||
=== Cmd230 === | |||
No input, returns an [[#IAsyncContext|IAsyncContext]]. | |||
Besides other functionality, this async task uses network request [[Network|transfer_events/start]]. | |||
=== Cmd510 === | |||
No input, returns an [[#IAsyncContext|IAsyncContext]]. | |||
Besides other functionality, this async task uses network request [[Network|transfer_events/%lld/abort_transfer]]. | |||
== IClient == | == IClient == | ||
This is "nn::migration:: | This is "nn::migration::device::IClient". | ||
This was added with [ | This was added with [20.0.0+]. | ||
{| class="wikitable" border="1" | {| class="wikitable" border="1" | ||
Line 184: | Line 497: | ||
! Cmd || Name | ! Cmd || Name | ||
|- | |- | ||
| | | 10 || | ||
|- | |||
| 20 || | |||
|- | |||
| 30 || | |||
|- | |- | ||
| 100 || | | 100 || | ||
|- | |||
| 110 || | |||
|- | |||
| 111 || | |||
|- | |- | ||
| 200 || | | 200 || | ||
|- | |- | ||
| | | 210 || | ||
|- | |||
| 220 || | |||
|- | |||
| 230 || | |||
|- | |||
| 240 || | |||
|- | |||
| 250 || | |||
|- | |- | ||
| | | 290 || | ||
|- | |- | ||
| 300 || | | 300 || | ||
|- | |- | ||
| | | 310 || | ||
|- | |||
| 320 || | |||
|- | |||
| 330 || | |||
|- | |||
| 340 || | |||
|- | |||
| 400 || | |||
|- | |||
| 500 || | |||
|- | |||
| 610 || | |||
|- | |||
| 700 || | |||
|- | |||
| 710 || | |||
|- | |||
| 720 || | |||
|} | |||
== IUnknown == | |||
This was added with [20.0.0+]. | |||
{| class="wikitable" border="1" | |||
|- | |||
! Cmd || Name | |||
|- | |- | ||
| | | 0 || | ||
|- | |- | ||
| | | 1 || | ||
|- | |||
| 2 || | |||
|- | |||
| 3 || | |||
|- | |||
| 100 || | |||
|- | |||
| 110 || | |||
|} | |||
== IUploader == | |||
This is "nn::migration::device::IUploader". | |||
This was added with [20.0.0+]. | |||
{| class="wikitable" border="1" | |||
|- | |||
! Cmd || Name | |||
|- | |||
| 90 || | |||
|- | |||
| 100 || | |||
|- | |- | ||
| 310 || | | 310 || | ||
|- | |- | ||
| 320 || | | 320 || | ||
|- | |||
| 330 || | |||
|- | |||
| 340 || | |||
|- | |- | ||
| 350 || | | 350 || | ||
Line 212: | Line 593: | ||
| 500 || | | 500 || | ||
|- | |- | ||
| | | 610 || | ||
|- | |||
| 900 || | |||
|} | |||
=== Cmd100 === | |||
No input, returns an [[#IAsyncContext|IAsyncContext]]. | |||
Besides other functionality, this async task uses network request [[Network|transfer_events/start]]. | |||
=== Cmd500 === | |||
No input, returns an [[#IAsyncContext|IAsyncContext]]. | |||
Besides other functionality, this async task uses network request [[Network|transfer_events/%lld/finish_upload]]. | |||
=== Cmd610 === | |||
No input, returns an [[#IAsyncContext|IAsyncContext]]. | |||
Besides other functionality, this async task uses network request [[Network|transfer_events/%lld/abort_upload]]. | |||
=== Cmd900 === | |||
Takes a total of 4-bytes of input, returns an [[#IAsyncContext|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+]. | |||
{| class="wikitable" border="1" | |||
|- | |||
! Cmd || Name | |||
|- | |||
| 10 || | |||
|- | |||
| 20 || | |||
|- | |||
| 30 || | |||
|- | |||
| 90 || | |||
|- | |||
| 100 || | |||
|- | |||
| 300 || | |||
|- | |||
| 310 || | |||
|- | |||
| 320 || | |||
|- | |||
| 330 || | |||
|- | |||
| 400 || | |||
|- | |||
| 500 || | |||
|- | |||
| 610 || | |||
|- | |||
| 620 || | |||
|- | |||
| 900 || | |||
|} | |||
=== Cmd500 === | |||
No input, returns an [[#IAsyncContext|IAsyncContext]]. | |||
Besides other functionality, this async task uses network request [[Network|transfer_events/%lld/finish_download]]. | |||
=== Cmd900 === | |||
Takes a total of 4-bytes of input, returns an [[#IAsyncContext|IAsyncContext]]. | |||
Besides other functionality, this async task may call the same network-request func as [[#Cmd500_2|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_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 = | |||
These sections document the initial [[LDN_services|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 <code>nn::util::GenerateUuid</code>. | |||
== user == | |||
Used by nn::migration::user::*. | |||
{| class="wikitable" border="1" | |||
|- | |||
! Offset || Size || Description | |||
|- | |||
| 0x0 || 0x8 || AccountId | |||
|- | |||
| 0x8 || 0x58 || Unused | |||
|- | |||
| 0x60 || 0x100 || [[#ServerProfile|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::*. | |||
{| class="wikitable" border="1" | |||
|- | |||
! Offset || Size || Description | |||
|- | |||
| 0x0 || 0x8 || AccountId | |||
|- | |||
| 0x8 || 0x8 || ApplicationId | |||
|- | |||
| 0x10 || 0x50 || Unused | |||
|- | |||
| 0x60 || 0x100 || [[#ServerProfile|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+]: | |||
{| class="wikitable" border="1" | |||
|- | |||
! Offset || Size || Description | |||
|- | |||
| 0x0 || 0x10 || Uuid | |||
|- | |||
| 0x10 || 0x8 || AccountId | |||
|- | |||
| 0x18 || 0x8 || ApplicationId | |||
|- | |||
| 0x20 || 0x4 || [[Settings_services#GetProductModel|ProductModel]] | |||
|- | |||
| 0x24 || 0x4 || Padding | |||
|- | |||
| 0x28 || 0x100 || [[#ServerProfile|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::*. | |||
{| class="wikitable" border="1" | |||
|- | |||
! 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|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 [[NS_services#IManagerForSystemService|IManagerForSystemService]] GetAccountId is used here. The server stores this Id into state, and also stores it in the [[LDN_services|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+] [[Filesystem_services#ISaveDataTransferManagerWithDivision|ISaveDataTransferManagerWithDivision]] is now used instead of ISaveDataTransferManager. Besides [[Filesystem_services|SetKeySeedPackage]] being used now as needed, [[Filesystem_services|SetLocalKeySeedPackage]] is now used by the relevant functionality in [[#IClient_2|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). | |||
[[Category:Services]] | [[Category:Services]] |
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).