Secure Monitor
Secure Monitor Calls
The secure monitor provides two top level handlers of which each provides a range of sub handlers.
Secure Monitor Calls follow the ARM SMC calling convention up to a small change:
Bit number | Bit mask | Description |
---|---|---|
31 | 0x80000000 | Set to 0 means Yielding Call; Set to 1 means Fast Call. |
30 | 0x40000000 | Set to 0 means SMC32 convention; Set to 1 means SMC64. |
29-24 | 0x3F000000 | Service Call ranges. |
23-16 | 0x00FF0000 | Must be zero. |
15-8 | 0x0000FF00 | Argument type. This is different from the ARM SMC calling convention. |
7-0 | 0x000000FF | Function number within the range call type. |
If bit n is set in the argument type then parameter Xn is treated as a pointer and the kernel will setup address translation for it in svcCallSecureMonitor.
SMC arguments are passed using registers X0-X7 with X0 always sending the call sub-id and returning the result of the call.
ID 0
Functions exposed to user-mode processes using svcCallSecureMonitor. SMCs should be called from CPUID 3 (where SPL runs).
Sub-ID | Name | In | Out |
---|---|---|---|
0xC3000401 | SetConfig | ||
0xC3000002 | GetConfig (Same as ID 1, Sub-ID 4) | ||
0xC3000003 | CheckStatus | ||
0xC3000404 | GetResult | ||
0xC3000E05 | ExpMod | ||
0xC3000006 | GetRandomBytes (Same as ID 1, Sub-ID 5) | ||
0xC3000007 | #GenerateAesKek | ||
0xC3000008 | #LoadAesKey | ||
0xC3000009 | #CryptAes | ||
0xC300000A | #GenerateSpecificAesKey | ||
0xC300040B | #ComputeCmac | ||
[1.0.0-4.1.0] 0xC300100C | #LoadRsaOaepKey | ||
[5.0.0+] 0xC300D60C | #EncryptRsaKeyForImport | ||
[1.0.0-4.1.0] 0xC300100D | #DecryptRsaPrivateKey | ||
[5.0.0] 0xC300100D | #DecryptOrImportRsaKey | ||
[1.0.0-4.1.0] 0xC300100E | #LoadSecureExpModKey | ||
0xC300060F | #SecureExpMod | ||
0xC3000610 | #UnwrapRsaOaepWrappedTitleKey | ||
0xC3000011 | #LoadTitleKey | ||
0xC3000012 | [2.0.0+] UnwrapAesWrappedTitleKey |
The overall concept here is the following:
- All key material (AES and RSA) is stored in userspace, but it's encrypted with random AES kek's ("key encryption keys").
- Each kek is generated as a function of an access key (picked at random).
- The kek is generated differently depending on the #CryptoUsecase the key is used for.
- This means: Each key is "locked" to the #CryptoUsecase it was designated for.
- You can use a key for a different usecase, but you will only get garbage output.
- After the kek has been generated, it is wrapped with a session-specific key and given back to userspace.
- This means: Plaintext kek keys never leave TrustZone.
- Further, this means: Actual AES/RSA keys never leave TrustZone.
Note: The CryptoUsecase_TitleKey represents a RSA wrapped AES key.
GenerateAesKek
Takes an "access key" as input, an #enum_CryptoUsecase.
Returns a session-unique kek for said usecase.
LoadAesKey
Takes a session kek created with #GenerateAesKek, and a wrapped AES key.
The session kek must have been created with CryptoUsecase_Aes.
CryptAes
Encrypts/decrypts using Aes (CTR and CBC).
Key must be set prior using one of the #LoadAesKey, #GenerateSpecificAesKey or #LoadRsaWrappedAesKey commands.
GenerateSpecificAesKey
Todo: This one seems unrelated to #enum_CryptoUsecase.
LoadRsaOaepKey
Takes a session kek created with #GenerateAesKek, a wrapped AES key, and a wrapped RSA private key.
The session kek must have been created with CryptoUsecase_RsaOaep.
This function was removed in 5.0.0, and replaced with #EncryptRsaKeyForImport.
EncryptRsaKeyForImport
Takes in two session keks created with #GenerateAesKek, two wrapped AES keys, an enum member, and a wrapped RSA private key.
Decrypts and validates the wrapped RSA private key with the first kek/wrapped key, and re-encrypts it with the second if valid.
The re-encrypted key is then passed to the user, for use with #DecryptOrImportRsaKey.
DecryptRsaPrivateKey
Takes a session kek created with #GenerateAesKek, a wrapped AES key, an enum member, and a wrapped RSA private key.
The session kek must have been created with CryptoUsecase_RsaPrivate.
[4.0.0+] The SMC handler when certain conditions pass and SMC_ID==0xC300100D now returns error 0x6 instead of calling the handler funcptr.
DecryptOrImportRsaKey
This function replaced #DecryptRsaPrivateKey in 5.0.0, adding an additional enum member argument.
This SMC extends DecryptRsaPrivateKey's original functionality to enable importing private keys into the security engine instead of decrypting them, when certain enum members are passed.
LoadSecureExpModKey
Takes a session kek created with #GenerateAesKek, and a wrapped RSA key.
The session kek must have been created with CryptoUsecase_RsaSecureExpMod.
This function was removed in 5.0.0, and replaced with #EncryptRsaKeyForImport.
SecureExpMod
Performs an Exp Mod operation using an exponent previously loaded with the #LoadSecureExpModKey command.
UnwrapRsaOaepWrappedTitleKey
Takes an Rsa-Oaep-wrapped TitleKey, an RSA Public Key, and a label hash.
Performs an Exp Mod operation using an exponent previously loaded with the #LoadRsaOaepKey command, and then validates/extracts a Titlekey from the resulting message.
Returns a session-unique AES key especially for use in #LoadTitleKey.
LoadTitleKey
Takes a session-unique AES key from #UnwrapAesWrappedTitleKey or #UnwrapRsaOaepWrappedTitleKey.
enum CryptoUsecase
Value | Name |
---|---|
0 | CryptoUsecase_Aes |
1 | CryptoUsecase_RsaPrivate |
2 | CryptoUsecase_RsaSecureExpMod |
3 | CryptoUsecase_RsaOaep |
ID 1
Functions exposed to the kernel internally.
Sub-ID | Name | In | Out |
---|---|---|---|
0xC4000001 | #CpuSuspend | X1=power_state, X2=entrypoint_addr, X3=context_id | None |
0x84000002 | #CpuOff | None | None |
0xC4000003 | #CpuOn | X1=target_cpu, X2=entrypoint_addr, X3=context_id, X4,X5,X6,X7=0 | X0=result |
0xC3000004 | #GetConfig (Same as ID 0, Sub-ID 2) | W1=config_item, X2,X3,X4,X5,X6,X7=0 | X0=result, X1,X2,X3,X4=config_val |
0xC3000005 | #GetRandomBytes (Same as ID 0, Sub-ID 6) | X1=size, X2,X3,X4,X5,X6,X7=0 | X0=result, X1,X2,X3,X4,X5,X6,X7=rand_bytes |
0xC3000006 | #Panic | W1=panic_color, X2,X3,X4,X5,X6,X7=0 | X0=result |
0xC3000007 | [2.0.0+] #ConfigureCarveout | X1=carveout_index, X2=region_phys_addr, X3=region_size, X4,X5,X6,X7=0 | X0=result |
0xC3000008 | [2.0.0+] #ReadWriteRegister | X1=reg_addr, W2=rw_mask, W3=in_val, X4,X5,X6,X7=0 | X0=result, W1=out_val |
CpuSuspend
Standard ARM PCSI SMC. Suspends the CPU (CPU0).
The kernel calls this SMC on shutdown with power_state set to 0x0201001B (power level: 0x02==system; power type: 0x01==powerdown; ID: 0x1B).
CpuOff
Standard ARM PCSI SMC. Turns off the CPU (CPU1, CPU2 or CPU3).
CpuOn
Standard ARM PCSI SMC. Turns on the CPU (CPU1, CPU2 or CPU3).
GetConfig
Takes a config_item and returns an associated config_val.
ConfigItem | Name |
---|---|
1 | #DisableProgramVerification |
2 | #DramId |
3 | #SecurityEngineIrqNumber |
4 | #Version |
5 | #HardwareType |
6 | #IsRetail |
7 | #IsRecoveryBoot |
8 | #DeviceId |
9 | [1.0.0-4.0.0] #BootReason |
10 | #MemoryArrange |
11 | #IsDebugMode |
12 | #KernelConfiguration |
13 | #IsChargerHiZModeEnabled |
14 | [4.0.0+] #IsKiosk |
15 | [5.0.0+] #NewHardwareType |
16 | [5.0.0+] #NewKeyGeneration |
17 | [5.0.0+] #Package2Hash |
DisableProgramVerification
PM checks this item and if non-zero, calls fsp-pr SetEnabledProgramVerification(false).
DramId
This is extracted directly from FUSE_RESERVED_ODM4.
Value | Description |
---|---|
0 | DramId_EristaIcosaSamsung4gb |
1 | DramId_EristaIcosaHynix4gb |
2 | DramId_EristaIcosaMicron4gb |
3 | Reserved |
4 | DramId_EristaIcosaSamsung6gb |
5 | [4.0.0+] Reserved (DramId_EristaIcosaHynix6gb) |
6 | [4.0.0+] Reserved (DramId_EristaIcosaMicron6gb) |
7 | [5.0.0+] DramId_MarikoIowax1x2Samsung4gb ([4.0.0-4.1.0] Reserved) |
8 | [5.0.0+] DramId_MarikoIowaSamsung4gb |
9 | [5.0.0+] DramId_MarikoIowaSamsung8gb |
10 | [6.0.0+] DramId_MarikoIowaHynix4gb ([5.0.0-5.1.0] Reserved) |
11 | [7.0.0+] DramId_MarikoIowaHynix8gb ([5.0.0-6.2.0] Reserved) |
12 | [5.0.0+] DramId_MarikoHoagSamsung4gb |
13 | [5.0.0+] DramId_MarikoHoagSamsung8gb |
14 | [7.0.0+] DramId_MarikoHoagHynix4gb ([5.0.0-6.2.0] Reserved) |
15 | [7.0.0+] DramId_MarikoHoagHynix8gb ([5.0.0-6.2.0] Reserved) |
PCV selects memory training tables based on DramId.
Platform | DramId | Revision | DVFS version |
---|---|---|---|
jetson-tx1 | N/A | 0x07 |
11_40800_01_V9.8.3_V1.6 11_68000_01_V9.8.3_V1.6 11_102000_01_V9.8.3_V1.6 11_204000_05_V9.8.3_V1.6 11_408000_02_V9.8.3_V1.6 11_665600_03_V9.8.3_V1.6 11_800000_01_V9.8.3_V1.6 11_1065600_01_V9.8.3_V1.6 11_1331200_01_V9.8.3_V1.6 11_1600000_02_V9.8.3_V1.6 |
nx-abca2 | EristaIcosaSamsung4gb or EristaIcosaMicron4gb | 0x07 |
10_40800_NoCfgVersion_V9.8.7_V1.6 10_68000_NoCfgVersion_V9.8.7_V1.6 10_102000_NoCfgVersion_V9.8.7_V1.6 10_204000_NoCfgVersion_V9.8.7_V1.6 10_408000_NoCfgVersion_V9.8.7_V1.6 10_665600_NoCfgVersion_V9.8.7_V1.6 10_800000_NoCfgVersion_V9.8.7_V1.6 10_1065600_NoCfgVersion_V9.8.7_V1.6 10_1331200_NoCfgVersion_V9.8.7_V1.6 10_1600000_NoCfgVersion_V9.8.7_V1.6 |
nx-abca2 | EristaIcosaHynix4gb | 0x07 |
10_40800_NoCfgVersion_V9.8.7_V1.6 10_68000_NoCfgVersion_V9.8.7_V1.6 10_102000_NoCfgVersion_V9.8.7_V1.6 10_204000_NoCfgVersion_V9.8.7_V1.6 10_408000_NoCfgVersion_V9.8.7_V1.6 10_665600_NoCfgVersion_V9.8.7_V1.6 10_800000_NoCfgVersion_V9.8.7_V1.6 10_1065600_NoCfgVersion_V9.8.7_V1.6 10_1331200_NoCfgVersion_V9.8.7_V1.6 10_1600000_NoCfgVersion_V9.8.7_V1.6 |
nx-abca2 | EristaIcosaSamsung6gb | 0x07 |
10_40800_NoCfgVersion_V9.8.7_V1.6 10_68000_NoCfgVersion_V9.8.7_V1.6 10_102000_NoCfgVersion_V9.8.7_V1.6 10_204000_NoCfgVersion_V9.8.7_V1.6 10_408000_NoCfgVersion_V9.8.7_V1.6 10_665600_NoCfgVersion_V9.8.7_V1.6 10_800000_NoCfgVersion_V9.8.7_V1.6 10_1065600_NoCfgVersion_V9.8.7_V1.6 10_1331200_NoCfgVersion_V9.8.7_V1.6 10_1600000_NoCfgVersion_V9.8.7_V1.6 |
nx-abca2 | MarikoIowax1x2Samsung4gb | 0x03 |
01_204000_NoCfgVersion_V0.3.1_V2.0 01_1331200.0_NoCfgVersion_V0.3.1_V2.0 01_1600000_NoCfgVersion_V0.3.1_V2.0 |
nx-abca2 | MarikoIowaSamsung4gb or MarikoHoagSamsung4gb | 0x03 |
01_204000_NoCfgVersion_V0.3.1_V2.0 01_1331200.0_NoCfgVersion_V0.3.1_V2.0 01_1600000_NoCfgVersion_V0.3.1_V2.0 |
nx-abca2 | MarikoIowaSamsung8gb or MarikoHoagSamsung8gb | 0x03 |
01_204000_NoCfgVersion_V0.4.2_V2.0 01_1331200.0_NoCfgVersion_V0.4.2_V2.0 01_1600000_NoCfgVersion_V0.4.2_V2.0 |
nx-abca2 | MarikoIowaHynix4gb | 0x03 |
01_204000_NoCfgVersion_V0.3.1_V2.0 01_1331200.0_NoCfgVersion_V0.3.1_V2.0 01_1600000_NoCfgVersion_V0.3.1_V2.0 |
nx-abcb | EristaIcosaSamsung4gb | 0x07 |
10_40800_NoCfgVersion_V9.8.7_V1.6 10_68000_NoCfgVersion_V9.8.7_V1.6 10_102000_NoCfgVersion_V9.8.7_V1.6 10_204000_NoCfgVersion_V9.8.7_V1.6 10_408000_NoCfgVersion_V9.8.7_V1.6 10_665600_NoCfgVersion_V9.8.7_V1.6 10_800000_NoCfgVersion_V9.8.7_V1.6 10_1065600_NoCfgVersion_V9.8.7_V1.6 10_1331200_NoCfgVersion_V9.8.7_V1.6 10_1600000_NoCfgVersion_V9.8.7_V1.6 |
nx-abcb | EristaIcosaMicron4gb | 0x07 |
10_40800_NoCfgVersion_V9.8.4_V1.6 10_68000_NoCfgVersion_V9.8.4_V1.6 10_102000_NoCfgVersion_V9.8.4_V1.6 10_204000_NoCfgVersion_V9.8.4_V1.6 10_408000_NoCfgVersion_V9.8.4_V1.6 10_665600_NoCfgVersion_V9.8.4_V1.6 10_800000_NoCfgVersion_V9.8.4_V1.6 10_1065600_NoCfgVersion_V9.8.4_V1.6 10_1331200_NoCfgVersion_V9.8.4_V1.6 10_1600000_NoCfgVersion_V9.8.4_V1.6 |
nx-abcb | EristaIcosaHynix4gb | 0x07 |
10_40800_NoCfgVersion_V9.8.4_V1.6 10_68000_NoCfgVersion_V9.8.4_V1.6 10_102000_NoCfgVersion_V9.8.4_V1.6 10_204000_NoCfgVersion_V9.8.4_V1.6 10_408000_NoCfgVersion_V9.8.4_V1.6 10_665600_NoCfgVersion_V9.8.4_V1.6 10_800000_NoCfgVersion_V9.8.4_V1.6 10_1065600_NoCfgVersion_V9.8.4_V1.6 10_1331200_NoCfgVersion_V9.8.4_V1.6 10_1600000_NoCfgVersion_V9.8.4_V1.6 |
nx-abcb (Copper) is the SDEV unit. Among other differences, this has extra hardware to support HDMI output.
nx-abca2 (Icosa) hardware types are variations of the retail form factor.
SecurityEngineIrqNumber
SPL uses this for setting up the security engine IRQ.
Version
The current Package1 Maxver Constant - 1.
HardwareType
[1.0.0+] This item is obtained by checking bits 8 and 2 from FUSE_RESERVED_ODM4. It can be 0 (Icosa), 1 (Copper) or 3 (Invalid).
[4.0.0+] This item is obtained by checking bits 8, 2 and 16-19 from FUSE_RESERVED_ODM4. It can be 0 (Icosa), 1 (Copper), 3 (Mariko) or 4 (Invalid).
A value of 2 (Hoag) is always mapped to 4 (Invalid).
IsRetail
This item is obtained by checking bits 9 and 0-1 from FUSE_RESERVED_ODM4. It can be 0 (Debug), 1 (Retail) or 2 (Invalid).
IsRecoveryBoot
Used to determine if the system is booting from SafeMode firmware.
DeviceId
NIM checks if this item matches the set:cal DeviceId with byte7 cleared. If they don't match, a panic is thrown.
BootReason
Used to determine how the system booted.
MemoryArrange
PM uses this item for selecting the appropriate size for each LimitableResource_Memory.
IsDebugMode
Kernel uses this to determine behavior of svcBreak positive arguments. It will break instead of just force-exiting the process which is what happens on retail.
[2.0.0+] This is also used with certain debug SVCs.
[3.0.0+] RO checks this and if set then skipping NRR rsa signatures is allowed.
The value of this field is loaded from BootConfig unsigned-config+0x10 u8 bit1.
KernelConfiguration
Kernel reads this when setting up memory-related code.
Bit 0 is a boolean determining whether kernel should it will memset various allocated memory-regions with 0x58, 0x59, 0x5A ('X', 'Y', 'Z') instead of zero. This allows Nintendo devs to find uninitialized memory bugs.
Bit 1 is a boolean determining whether kernel should forcefully enable usermode exception handlers (when false, only certain aborts (((1LL << (esr >> 26)) & 0x1115804400224001) == 0, typically data/prefetch aborts) that occur when the faulting address is in a readable region with MemoryType_CodeStatic will trigger usermode exception handlers).
Bit 2 is a boolean determining whether kernel should enable usermode access to the Performance Monitors (whether PMUSERENR_EL0 should be 1 or 0).
Bits 8-15 are a boolean determining whether kernel should call smcPanic on error instead of infinite-looping.
Bits 16-17 determine how much memory is available. 00/03 = 4 GB, 01 = 6 GB, 02 = 8 GB.
IsChargerHiZModeEnabled
This tells if the TI Charger (bq24192) is active.
NewKeyGeneration
This item is obtained from FUSE_RESERVED_ODM2 if bit 11 from FUSE_RESERVED_ODM4 is set, FUSE_RESERVED_ODM0 matches 0x8E61ECAE and FUSE_RESERVED_ODM1 matches 0xF2BA3BB2.
[5.0.0+] FS can now use this value for the KeyGeneration parameter when calling GenerateAesKek during "GetBisEncryptionKey".
IsKiosk
This item is bit 10 from FUSE_RESERVED_ODM4.
[4.0.0+] Settings uses this value to overwrite the quest flag from GetQuestFlag. This is used to detect if a Switch is a kiosk unit for display at retail stores.
NewHardwareType
This item is currently hardcoded to 0.
[5.0.0+] PCV overrides the value from HardwareType and configures PMIC devices with this item.
Value | Devices |
---|---|
0 | max77620_sd0, max77621_cpu and max77621_gpu |
1 | max77620_sd0, max77812_cpu and max77812_gpu |
2 | max77620_sd0, max77812_cpu and max77812_gpu |
Package2Hash
This is a SHA-256 hash calculated over the package2 image. Since the hash calculation is an optional step in pkg2ldr, this item is only valid in recovery mode. Otherwise, an error is returned instead.
GetRandomBytes
Takes a size and returns rand_bytes.
The kernel limits size to 0x38 (for fitting in return registers).
Panic
Issues a system panic.
The kernel always calls this with panic_color set to 0xF00.
ConfigureCarveout
Configures memory controller carveout regions.
If carveout_index is 0, region_phys_addr and region_size are used to configure MC_SECURITY_CARVEOUT4. If carveout_index is 1, region_phys_addr and region_size are used to configure MC_SECURITY_CARVEOUT5. Any other carveout_index values are invalid.
The kernel calls this with carveout_index set to 0, region_phys_addr set to 0x80060000 and region_size set to a dynamically calculated size which covers all the kernel and built-in sysmodules' DRAM regions.
ReadWriteRegister
Relays svcReadWriteRegister to the Secure Monitor.
Errors
Value | Description |
---|---|
2 | Invalid input |
3 | Busy |