diff --git a/Makefile b/Makefile
index 8edd2a64..a8a75e94 100644
--- a/Makefile
+++ b/Makefile
@@ -16,7 +16,7 @@ include $(DEVKITPRO)/libnx/switch_rules
# INCLUDES is a list of directories containing header files
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
-SOURCES := source
+SOURCES := source source/spl source/spl/smc
DATA := data
INCLUDES := include
diff --git a/include/stratosphere/spl.hpp b/include/stratosphere/spl.hpp
new file mode 100644
index 00000000..d0b43e9d
--- /dev/null
+++ b/include/stratosphere/spl.hpp
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+#include
+
+#include "spl/spl_types.hpp"
+#include "spl/spl_api.hpp"
+#include "spl/smc/spl_smc.hpp"
\ No newline at end of file
diff --git a/include/stratosphere/spl/smc/spl_smc.hpp b/include/stratosphere/spl/smc/spl_smc.hpp
new file mode 100644
index 00000000..46a011a3
--- /dev/null
+++ b/include/stratosphere/spl/smc/spl_smc.hpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+#include
+
+#include "../spl_types.hpp"
+
+namespace sts::spl::smc {
+
+ /* Helpers for converting arguments. */
+ inline u32 GetCryptAesMode(CipherMode mode, u32 keyslot) {
+ return static_cast((static_cast(mode) << 4) | (keyslot & 7));
+ }
+
+ inline u32 GetUnwrapEsKeyOption(EsKeyType type, u32 generation) {
+ return static_cast((static_cast(type) << 6) | (generation & 0x3F));
+ }
+
+ /* Functions. */
+ Result SetConfig(SplConfigItem which, const u64 *value, size_t num_qwords);
+ Result GetConfig(u64 *out, size_t num_qwords, SplConfigItem which);
+ Result CheckStatus(Result *out, AsyncOperationKey op);
+ Result GetResult(Result *out, void *out_buf, size_t out_buf_size, AsyncOperationKey op);
+ Result ExpMod(AsyncOperationKey *out_op, const void *base, const void *exp, size_t exp_size, const void *mod);
+ Result GenerateRandomBytes(void *out, size_t size);
+ Result GenerateAesKek(AccessKey *out, const KeySource &source, u32 generation, u32 option);
+ Result LoadAesKey(u32 keyslot, const AccessKey &access_key, const KeySource &source);
+ Result CryptAes(AsyncOperationKey *out_op, u32 mode, const IvCtr &iv_ctr, u32 dst_addr, u32 src_addr, size_t size);
+ Result GenerateSpecificAesKey(AesKey *out_key, const KeySource &source, u32 generation, u32 which);
+ Result ComputeCmac(Cmac *out_mac, u32 keyslot, const void *data, size_t size);
+ Result ReEncryptRsaPrivateKey(void *data, size_t size, const AccessKey &access_key_dec, const KeySource &source_dec, const AccessKey &access_key_enc, const KeySource &source_enc, u32 option);
+ Result DecryptOrImportRsaPrivateKey(void *data, size_t size, const AccessKey &access_key, const KeySource &source, DecryptOrImportMode mode);
+ Result SecureExpMod(AsyncOperationKey *out_op, const void *base, const void *mod, SecureExpModMode mode);
+ Result UnwrapTitleKey(AsyncOperationKey *out_op, const void *base, const void *mod, const void *label_digest, size_t label_digest_size, u32 option);
+ Result LoadTitleKey(u32 keyslot, const AccessKey &access_key);
+ Result UnwrapCommonTitleKey(AccessKey *out, const KeySource &source, u32 generation);
+
+ /* Deprecated functions. */
+ Result ImportEsKey(const void *data, size_t size, const AccessKey &access_key, const KeySource &source, u32 option);
+ Result DecryptRsaPrivateKey(size_t *out_size, void *data, size_t size, const AccessKey &access_key, const KeySource &source, u32 option);
+ Result ImportSecureExpModKey(const void *data, size_t size, const AccessKey &access_key, const KeySource &source, u32 option);
+
+}
diff --git a/include/stratosphere/spl/spl_api.hpp b/include/stratosphere/spl/spl_api.hpp
new file mode 100644
index 00000000..62291f0d
--- /dev/null
+++ b/include/stratosphere/spl/spl_api.hpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+
+#include "spl_types.hpp"
+
+namespace sts::spl {
+
+ HardwareType GetHardwareType();
+ bool IsRecoveryBoot();
+ bool IsMariko();
+
+}
diff --git a/include/stratosphere/spl/spl_types.hpp b/include/stratosphere/spl/spl_types.hpp
new file mode 100644
index 00000000..84680ab5
--- /dev/null
+++ b/include/stratosphere/spl/spl_types.hpp
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#pragma once
+#include
+#include "../results.hpp"
+
+namespace sts::spl {
+
+ namespace smc {
+
+ enum class FunctionId : u32 {
+ SetConfig = 0xC3000401,
+ GetConfig = 0xC3000002,
+ CheckStatus = 0xC3000003,
+ GetResult = 0xC3000404,
+ ExpMod = 0xC3000E05,
+ GenerateRandomBytes = 0xC3000006,
+ GenerateAesKek = 0xC3000007,
+ LoadAesKey = 0xC3000008,
+ CryptAes = 0xC3000009,
+ GenerateSpecificAesKey = 0xC300000A,
+ ComputeCmac = 0xC300040B,
+ ReEncryptRsaPrivateKey = 0xC300D60C,
+ DecryptOrImportRsaPrivateKey = 0xC300100D,
+
+ SecureExpMod = 0xC300060F,
+ UnwrapTitleKey = 0xC3000610,
+ LoadTitleKey = 0xC3000011,
+ UnwrapCommonTitleKey = 0xC3000012,
+
+ /* Deprecated functions. */
+ ImportEsKey = 0xC300100C,
+ DecryptRsaPrivateKey = 0xC300100D,
+ ImportSecureExpModKey = 0xC300100E,
+ };
+
+ enum class Result {
+ Success = 0,
+ NotImplemented = 1,
+ InvalidArgument = 2,
+ InProgress = 3,
+ NoAsyncOperation = 4,
+ InvalidAsyncOperation = 5,
+ Blacklisted = 6,
+
+ Max = 99,
+ };
+
+ inline ::Result ConvertResult(Result result) {
+ if (result == Result::Success) {
+ return ResultSuccess;
+ }
+ if (result < Result::Max) {
+ return MAKERESULT(Module_Spl, static_cast(result));
+ }
+ return ResultSplUnknownSmcResult;
+ }
+
+ enum class CipherMode {
+ CbcEncrypt = 0,
+ CbcDecrypt = 1,
+ Ctr = 2,
+ };
+
+ enum class DecryptOrImportMode {
+ DecryptRsaPrivateKey = 0,
+ ImportLotusKey = 1,
+ ImportEsKey = 2,
+ ImportSslKey = 3,
+ ImportDrmKey = 4,
+ };
+
+ enum class SecureExpModMode {
+ Lotus = 0,
+ Ssl = 1,
+ Drm = 2,
+ };
+
+ enum class EsKeyType {
+ TitleKey = 0,
+ ElicenseKey = 1,
+ };
+
+ struct AsyncOperationKey {
+ u64 value;
+ };
+ }
+
+ enum class HardwareType {
+ Icosa = 0,
+ Copper = 1,
+ Hoag = 2,
+ Iowa = 3,
+ };
+
+ struct BootReasonValue {
+ union {
+ struct {
+ u8 power_intr;
+ u8 rtc_intr;
+ u8 nv_erc;
+ u8 boot_reason;
+ };
+ u32 value;
+ };
+ };
+ static_assert(sizeof(BootReasonValue) == sizeof(u32), "BootReasonValue definition!");
+ #pragma pack(push, 1)
+
+ struct AesKey {
+ union {
+ u8 data[AES_128_KEY_SIZE];
+ u64 data64[AES_128_KEY_SIZE / sizeof(u64)];
+ };
+ };
+ static_assert(alignof(AesKey) == alignof(u8), "AesKey definition!");
+
+ struct IvCtr {
+ union {
+ u8 data[AES_128_KEY_SIZE];
+ u64 data64[AES_128_KEY_SIZE / sizeof(u64)];
+ };
+ };
+ static_assert(alignof(IvCtr) == alignof(u8), "IvCtr definition!");
+
+ struct Cmac {
+ union {
+ u8 data[AES_128_KEY_SIZE];
+ u64 data64[AES_128_KEY_SIZE / sizeof(u64)];
+ };
+ };
+ static_assert(alignof(Cmac) == alignof(u8), "Cmac definition!");
+
+ struct AccessKey {
+ union {
+ u8 data[AES_128_KEY_SIZE];
+ u64 data64[AES_128_KEY_SIZE / sizeof(u64)];
+ };
+ };
+ static_assert(alignof(AccessKey) == alignof(u8), "AccessKey definition!");
+
+ struct KeySource {
+ union {
+ u8 data[AES_128_KEY_SIZE];
+ u64 data64[AES_128_KEY_SIZE / sizeof(u64)];
+ };
+ };
+ static_assert(alignof(AccessKey) == alignof(u8), "KeySource definition!");
+ #pragma pack(pop)
+
+}
diff --git a/source/spl/smc/spl_smc.cpp b/source/spl/smc/spl_smc.cpp
new file mode 100644
index 00000000..89e35067
--- /dev/null
+++ b/source/spl/smc/spl_smc.cpp
@@ -0,0 +1,312 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include
+#include
+
+namespace sts::spl::smc {
+
+ Result SetConfig(SplConfigItem which, const u64 *value, size_t num_qwords) {
+ SecmonArgs args;
+
+ args.X[0] = static_cast(FunctionId::SetConfig);
+ args.X[1] = which;
+ args.X[2] = 0;
+ for (size_t i = 0; i < std::min(size_t(4), num_qwords); i++) {
+ args.X[3 + i] = value[i];
+ }
+ svcCallSecureMonitor(&args);
+
+ return static_cast(args.X[0]);
+ }
+
+ Result GetConfig(u64 *out, size_t num_qwords, SplConfigItem which) {
+ SecmonArgs args;
+
+ args.X[0] = static_cast(FunctionId::GetConfig);
+ args.X[1] = which;
+ svcCallSecureMonitor(&args);
+
+ for (size_t i = 0; i < std::min(size_t(4), num_qwords); i++) {
+ out[i] = args.X[1 + i];
+ }
+ return static_cast(args.X[0]);
+ }
+
+ Result CheckStatus(Result *out, AsyncOperationKey op) {
+ SecmonArgs args;
+
+ args.X[0] = static_cast(FunctionId::CheckStatus);
+ args.X[1] = op.value;
+ svcCallSecureMonitor(&args);
+
+ *out = static_cast(args.X[1]);
+ return static_cast(args.X[0]);
+ }
+
+ Result GetResult(Result *out, void *out_buf, size_t out_buf_size, AsyncOperationKey op) {
+ SecmonArgs args;
+
+ args.X[0] = static_cast(FunctionId::GetResult);
+ args.X[1] = op.value;
+ args.X[2] = reinterpret_cast(out_buf);
+ args.X[3] = out_buf_size;
+ svcCallSecureMonitor(&args);
+
+ *out = static_cast(args.X[1]);
+ return static_cast(args.X[0]);
+ }
+
+ Result ExpMod(AsyncOperationKey *out_op, const void *base, const void *exp, size_t exp_size, const void *mod) {
+ SecmonArgs args;
+
+ args.X[0] = static_cast(FunctionId::ExpMod);
+ args.X[1] = reinterpret_cast(base);
+ args.X[2] = reinterpret_cast(exp);
+ args.X[3] = reinterpret_cast(mod);
+ args.X[4] = exp_size;
+ svcCallSecureMonitor(&args);
+
+ out_op->value = args.X[1];
+ return static_cast(args.X[0]);
+ }
+
+ Result GenerateRandomBytes(void *out, size_t size) {
+ SecmonArgs args;
+
+ args.X[0] = static_cast(FunctionId::GenerateRandomBytes);
+ args.X[1] = size;
+ svcCallSecureMonitor(&args);
+
+ if (args.X[0] == static_cast(Result::Success) && (size <= sizeof(args) - sizeof(args.X[0]))) {
+ std::memcpy(out, &args.X[1], size);
+ }
+ return static_cast(args.X[0]);
+ }
+
+ Result GenerateAesKek(AccessKey *out, const KeySource &source, u32 generation, u32 option) {
+ SecmonArgs args;
+
+ args.X[0] = static_cast(FunctionId::GenerateAesKek);
+ args.X[1] = source.data64[0];
+ args.X[2] = source.data64[1];
+ args.X[3] = generation;
+ args.X[4] = option;
+ svcCallSecureMonitor(&args);
+
+ out->data64[0] = args.X[1];
+ out->data64[1] = args.X[2];
+ return static_cast(args.X[0]);
+ }
+
+ Result LoadAesKey(u32 keyslot, const AccessKey &access_key, const KeySource &source) {
+ SecmonArgs args;
+
+ args.X[0] = static_cast(FunctionId::LoadAesKey);
+ args.X[1] = keyslot;
+ args.X[2] = access_key.data64[0];
+ args.X[3] = access_key.data64[1];
+ args.X[4] = source.data64[0];
+ args.X[5] = source.data64[1];
+ svcCallSecureMonitor(&args);
+
+ return static_cast(args.X[0]);
+ }
+
+ Result CryptAes(AsyncOperationKey *out_op, u32 mode, const IvCtr &iv_ctr, u32 dst_addr, u32 src_addr, size_t size) {
+ SecmonArgs args;
+
+ args.X[0] = static_cast(FunctionId::CryptAes);
+ args.X[1] = mode;
+ args.X[2] = iv_ctr.data64[0];
+ args.X[3] = iv_ctr.data64[1];
+ args.X[4] = src_addr;
+ args.X[5] = dst_addr;
+ args.X[6] = size;
+ svcCallSecureMonitor(&args);
+
+ out_op->value = args.X[1];
+ return static_cast(args.X[0]);
+ }
+
+ Result GenerateSpecificAesKey(AesKey *out_key, const KeySource &source, u32 generation, u32 which) {
+ SecmonArgs args;
+
+ args.X[0] = static_cast(FunctionId::GenerateSpecificAesKey);
+ args.X[1] = source.data64[0];
+ args.X[2] = source.data64[1];
+ args.X[3] = generation;
+ args.X[4] = which;
+ svcCallSecureMonitor(&args);
+
+ out_key->data64[0] = args.X[1];
+ out_key->data64[1] = args.X[2];
+ return static_cast(args.X[0]);
+ }
+
+ Result ComputeCmac(Cmac *out_mac, u32 keyslot, const void *data, size_t size) {
+ SecmonArgs args;
+
+ args.X[0] = static_cast(FunctionId::ComputeCmac);
+ args.X[1] = keyslot;
+ args.X[2] = reinterpret_cast(data);
+ args.X[3] = size;
+ svcCallSecureMonitor(&args);
+
+ out_mac->data64[0] = args.X[1];
+ out_mac->data64[1] = args.X[2];
+ return static_cast(args.X[0]);
+ }
+
+ Result ReEncryptRsaPrivateKey(void *data, size_t size, const AccessKey &access_key_dec, const KeySource &source_dec, const AccessKey &access_key_enc, const KeySource &source_enc, u32 option) {
+ SecmonArgs args;
+
+ args.X[0] = static_cast(FunctionId::ReEncryptRsaPrivateKey);
+ args.X[1] = reinterpret_cast(&access_key_dec);
+ args.X[2] = reinterpret_cast(&access_key_enc);
+ args.X[3] = option;
+ args.X[4] = reinterpret_cast(data);
+ args.X[5] = size;
+ args.X[6] = reinterpret_cast(&source_dec);
+ args.X[7] = reinterpret_cast(&source_enc);
+ svcCallSecureMonitor(&args);
+
+ return static_cast(args.X[0]);
+ }
+
+ Result DecryptOrImportRsaPrivateKey(void *data, size_t size, const AccessKey &access_key, const KeySource &source, DecryptOrImportMode mode) {
+ SecmonArgs args;
+
+ args.X[0] = static_cast(FunctionId::DecryptOrImportRsaPrivateKey);
+ args.X[1] = access_key.data64[0];
+ args.X[2] = access_key.data64[1];
+ args.X[3] = static_cast(mode);
+ args.X[4] = reinterpret_cast(data);
+ args.X[5] = size;
+ args.X[6] = source.data64[0];
+ args.X[7] = source.data64[1];
+ svcCallSecureMonitor(&args);
+
+ return static_cast(args.X[0]);
+ }
+
+ Result SecureExpMod(AsyncOperationKey *out_op, const void *base, const void *mod, SecureExpModMode mode) {
+ SecmonArgs args;
+
+ args.X[0] = static_cast(FunctionId::SecureExpMod);
+ args.X[1] = reinterpret_cast(base);
+ args.X[2] = reinterpret_cast(mod);
+ args.X[3] = static_cast(mode);
+ svcCallSecureMonitor(&args);
+
+ out_op->value = args.X[1];
+ return static_cast(args.X[0]);
+ }
+
+ Result UnwrapTitleKey(AsyncOperationKey *out_op, const void *base, const void *mod, const void *label_digest, size_t label_digest_size, u32 option) {
+ SecmonArgs args;
+
+ args.X[0] = static_cast(FunctionId::UnwrapTitleKey);
+ args.X[1] = reinterpret_cast(base);
+ args.X[2] = reinterpret_cast(mod);
+ std::memset(&args.X[3], 0, 4 * sizeof(args.X[3]));
+ std::memcpy(&args.X[3], label_digest, std::min(size_t(4 * sizeof(args.X[3])), label_digest_size));
+ args.X[7] = option;
+ svcCallSecureMonitor(&args);
+
+ out_op->value = args.X[1];
+ return static_cast(args.X[0]);
+ }
+
+ Result LoadTitleKey(u32 keyslot, const AccessKey &access_key) {
+ SecmonArgs args;
+
+ args.X[0] = static_cast(FunctionId::LoadTitleKey);
+ args.X[1] = keyslot;
+ args.X[2] = access_key.data64[0];
+ args.X[3] = access_key.data64[1];
+ svcCallSecureMonitor(&args);
+
+ return static_cast(args.X[0]);
+ }
+
+ Result UnwrapCommonTitleKey(AccessKey *out, const KeySource &source, u32 generation) {
+ SecmonArgs args;
+
+ args.X[0] = static_cast(FunctionId::UnwrapCommonTitleKey);
+ args.X[1] = source.data64[0];
+ args.X[2] = source.data64[1];
+ args.X[3] = generation;
+ svcCallSecureMonitor(&args);
+
+ out->data64[0] = args.X[1];
+ out->data64[1] = args.X[2];
+ return static_cast(args.X[0]);
+ }
+
+
+ /* Deprecated functions. */
+ Result ImportEsKey(const void *data, size_t size, const AccessKey &access_key, const KeySource &source, u32 option) {
+ SecmonArgs args;
+
+ args.X[0] = static_cast(FunctionId::ImportEsKey);
+ args.X[1] = access_key.data64[0];
+ args.X[2] = access_key.data64[1];
+ args.X[3] = option;
+ args.X[4] = reinterpret_cast(data);
+ args.X[5] = size;
+ args.X[6] = source.data64[0];
+ args.X[7] = source.data64[1];
+ svcCallSecureMonitor(&args);
+
+ return static_cast(args.X[0]);
+ }
+
+ Result DecryptRsaPrivateKey(size_t *out_size, void *data, size_t size, const AccessKey &access_key, const KeySource &source, u32 option) {
+ SecmonArgs args;
+
+ args.X[0] = static_cast(FunctionId::DecryptRsaPrivateKey);
+ args.X[1] = access_key.data64[0];
+ args.X[2] = access_key.data64[1];
+ args.X[3] = option;
+ args.X[4] = reinterpret_cast(data);
+ args.X[5] = size;
+ args.X[6] = source.data64[0];
+ args.X[7] = source.data64[1];
+ svcCallSecureMonitor(&args);
+
+ *out_size = static_cast(args.X[1]);
+ return static_cast(args.X[0]);
+ }
+
+ Result ImportSecureExpModKey(const void *data, size_t size, const AccessKey &access_key, const KeySource &source, u32 option) {
+ SecmonArgs args;
+
+ args.X[0] = static_cast(FunctionId::ImportSecureExpModKey);
+ args.X[1] = access_key.data64[0];
+ args.X[2] = access_key.data64[1];
+ args.X[3] = option;
+ args.X[4] = reinterpret_cast(data);
+ args.X[5] = size;
+ args.X[6] = source.data64[0];
+ args.X[7] = source.data64[1];
+ svcCallSecureMonitor(&args);
+
+ return static_cast(args.X[0]);
+ }
+
+}
diff --git a/source/spl/spl_api.cpp b/source/spl/spl_api.cpp
new file mode 100644
index 00000000..9d0cb604
--- /dev/null
+++ b/source/spl/spl_api.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2018-2019 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include
+#include
+
+namespace sts::spl {
+
+ HardwareType GetHardwareType() {
+ u64 out_val = 0;
+ R_ASSERT(splGetConfig(SplConfigItem_HardwareType, &out_val));
+ return static_cast(out_val);
+ }
+
+ bool IsRecoveryBoot() {
+ u64 val = 0;
+ R_ASSERT(splGetConfig(SplConfigItem_IsRecoveryBoot, &val));
+ return val != 0;
+ }
+
+ bool IsMariko() {
+ const auto hw_type = GetHardwareType();
+ switch (hw_type) {
+ case HardwareType::Icosa:
+ case HardwareType::Copper:
+ return false;
+ case HardwareType::Hoag:
+ case HardwareType::Iowa:
+ return true;
+ default:
+ std::abort();
+ }
+ }
+
+}