mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2025-05-22 02:45:07 -04:00
exo2: rename exosphere2 -> exosphere
This commit is contained in:
parent
282f8f6612
commit
42f1a3bf60
136 changed files with 15 additions and 15 deletions
30
exosphere/program/source/smc/secmon_define_access_table.inc
Normal file
30
exosphere/program/source/smc/secmon_define_access_table.inc
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
using __ACCESS_TABLE_NAME__ = AccessTable<__ACCESS_TABLE_ADDRESS__, [] {
|
||||
/* Declare a table. */
|
||||
std::array<u8, 0x80> table = {};
|
||||
|
||||
/* Declare a helper. */
|
||||
auto SetRegisterAllowed = [&](uintptr_t reg) { SetRegisterTableAllowed(table, reg); };
|
||||
|
||||
/* Populate the table. */
|
||||
#include __ACCESS_TABLE_INC__
|
||||
|
||||
return table;
|
||||
}()>;
|
||||
|
||||
static_assert(__ACCESS_TABLE_NAME__::Address >= __ACCESS_TABLE_ADDRESS__);
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define __ACCESS_TABLE_NAME__ Mc01AccessTable
|
||||
#define __ACCESS_TABLE_ADDRESS__ 0
|
||||
#define __ACCESS_TABLE_INC__ "secmon_mc01_access_table_data.inc"
|
||||
|
||||
#include "secmon_define_access_table.inc"
|
||||
|
||||
#undef __ACCESS_TABLE_INC__
|
||||
#undef __ACCESS_TABLE_ADDRESS__
|
||||
#undef __ACCESS_TABLE_NAME__
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define __ACCESS_TABLE_NAME__ McAccessTable
|
||||
#define __ACCESS_TABLE_ADDRESS__ MemoryRegionPhysicalDeviceMemoryController.GetAddress()
|
||||
#define __ACCESS_TABLE_INC__ "secmon_mc_access_table_data.inc"
|
||||
|
||||
#include "secmon_define_access_table.inc"
|
||||
|
||||
#undef __ACCESS_TABLE_INC__
|
||||
#undef __ACCESS_TABLE_ADDRESS__
|
||||
#undef __ACCESS_TABLE_NAME__
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#define __ACCESS_TABLE_NAME__ PmcAccessTable
|
||||
#define __ACCESS_TABLE_ADDRESS__ MemoryRegionPhysicalDevicePmc.GetAddress()
|
||||
#define __ACCESS_TABLE_INC__ "secmon_pmc_access_table_data.inc"
|
||||
|
||||
#include "secmon_define_access_table.inc"
|
||||
|
||||
#undef __ACCESS_TABLE_INC__
|
||||
#undef __ACCESS_TABLE_ADDRESS__
|
||||
#undef __ACCESS_TABLE_NAME__
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
SetRegisterAllowed(MC_STAT_CONTROL); /* 0x100 */
|
||||
SetRegisterAllowed(MC_STAT_EMC_CLOCK_LIMIT); /* 0x108 */
|
||||
SetRegisterAllowed(MC_STAT_EMC_CLOCK_LIMIT_MSBS); /* 0x10C */
|
||||
SetRegisterAllowed(MC_STAT_EMC_FILTER_SET0_ADR_LIMIT_LO); /* 0x118 */
|
||||
SetRegisterAllowed(MC_STAT_EMC_FILTER_SET0_ADR_LIMIT_HI); /* 0x11C */
|
||||
SetRegisterAllowed(MC_STAT_EMC_FILTER_SET0_SPARE); /* 0x124 */
|
||||
SetRegisterAllowed(MC_STAT_EMC_FILTER_SET0_CLIENT_0); /* 0x128 */
|
||||
SetRegisterAllowed(MC_STAT_EMC_FILTER_SET0_CLIENT_1); /* 0x12C */
|
||||
SetRegisterAllowed(MC_STAT_EMC_FILTER_SET0_CLIENT_2); /* 0x130 */
|
||||
SetRegisterAllowed(MC_STAT_EMC_FILTER_SET0_CLIENT_3); /* 0x134 */
|
||||
SetRegisterAllowed(MC_STAT_EMC_SET0_COUNT); /* 0x138 */
|
||||
SetRegisterAllowed(MC_STAT_EMC_SET0_COUNT_MSBS); /* 0x13C */
|
||||
SetRegisterAllowed(MC_STAT_EMC_FILTER_SET1_ADR_LIMIT_LO); /* 0x158 */
|
||||
SetRegisterAllowed(MC_STAT_EMC_FILTER_SET1_ADR_LIMIT_HI); /* 0x15C */
|
||||
SetRegisterAllowed(MC_STAT_EMC_FILTER_SET1_SPARE); /* 0x164 */
|
||||
SetRegisterAllowed(MC_STAT_EMC_FILTER_SET1_CLIENT_0); /* 0x168 */
|
||||
SetRegisterAllowed(MC_STAT_EMC_FILTER_SET1_CLIENT_1); /* 0x16C */
|
||||
SetRegisterAllowed(MC_STAT_EMC_FILTER_SET1_CLIENT_2); /* 0x170 */
|
||||
SetRegisterAllowed(MC_STAT_EMC_FILTER_SET1_CLIENT_3); /* 0x174 */
|
||||
SetRegisterAllowed(MC_STAT_EMC_SET1_COUNT); /* 0x178 */
|
||||
SetRegisterAllowed(MC_STAT_EMC_SET1_COUNT_MSBS); /* 0x17C */
|
||||
SetRegisterAllowed(MC_STAT_EMC_FILTER_SET0_ADR_LIMIT_UPPER); /* 0xA20 */
|
||||
SetRegisterAllowed(MC_STAT_EMC_FILTER_SET1_ADR_LIMIT_UPPER); /* 0xA24 */
|
||||
SetRegisterAllowed(MC_STAT_EMC_FILTER_SET0_CLIENT_4); /* 0xB88 */
|
||||
SetRegisterAllowed(MC_STAT_EMC_FILTER_SET1_CLIENT_4); /* 0xB8C */
|
||||
SetRegisterAllowed(MC_STAT_EMC_FILTER_SET0_CLIENT_5); /* 0xBC4 */
|
||||
SetRegisterAllowed(MC_STAT_EMC_FILTER_SET1_CLIENT_5); /* 0xBC8 */
|
195
exosphere/program/source/smc/secmon_mc_access_table_data.inc
Normal file
195
exosphere/program/source/smc/secmon_mc_access_table_data.inc
Normal file
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
SetRegisterAllowed(MC_INTSTATUS); /* 0x000 */
|
||||
SetRegisterAllowed(MC_INTMASK); /* 0x004 */
|
||||
SetRegisterAllowed(MC_ERR_STATUS); /* 0x008 */
|
||||
SetRegisterAllowed(MC_ERR_ADR); /* 0x00C */
|
||||
SetRegisterAllowed(MC_SMMU_CONFIG); /* 0x010 */
|
||||
SetRegisterAllowed(MC_SMMU_PTB_ASID); /* 0x01C */
|
||||
SetRegisterAllowed(MC_SMMU_PTB_DATA); /* 0x020 */
|
||||
SetRegisterAllowed(MC_SMMU_TLB_FLUSH); /* 0x030 */
|
||||
SetRegisterAllowed(MC_SMMU_PTC_FLUSH); /* 0x034 */
|
||||
SetRegisterAllowed(MC_EMEM_CFG); /* 0x050 */
|
||||
SetRegisterAllowed(MC_EMEM_ADR_CFG); /* 0x054 */
|
||||
SetRegisterAllowed(MC_EMEM_ARB_CFG); /* 0x090 */
|
||||
SetRegisterAllowed(MC_EMEM_ARB_OUTSTANDING_REQ); /* 0x094 */
|
||||
SetRegisterAllowed(MC_EMEM_ARB_TIMING_RCD); /* 0x098 */
|
||||
SetRegisterAllowed(MC_EMEM_ARB_TIMING_RP); /* 0x09C */
|
||||
SetRegisterAllowed(MC_EMEM_ARB_TIMING_RC); /* 0x0A0 */
|
||||
SetRegisterAllowed(MC_EMEM_ARB_TIMING_RAS); /* 0x0A4 */
|
||||
SetRegisterAllowed(MC_EMEM_ARB_TIMING_FAW); /* 0x0A8 */
|
||||
SetRegisterAllowed(MC_EMEM_ARB_TIMING_RRD); /* 0x0AC */
|
||||
SetRegisterAllowed(MC_EMEM_ARB_TIMING_RAP2PRE); /* 0x0B0 */
|
||||
SetRegisterAllowed(MC_EMEM_ARB_TIMING_WAP2PRE); /* 0x0B4 */
|
||||
SetRegisterAllowed(MC_EMEM_ARB_TIMING_R2R); /* 0x0B8 */
|
||||
SetRegisterAllowed(MC_EMEM_ARB_TIMING_W2W); /* 0x0BC */
|
||||
SetRegisterAllowed(MC_EMEM_ARB_TIMING_R2W); /* 0x0C0 */
|
||||
SetRegisterAllowed(MC_EMEM_ARB_TIMING_W2R); /* 0x0C4 */
|
||||
SetRegisterAllowed(MC_EMEM_ARB_MISC2); /* 0x0C8 */
|
||||
SetRegisterAllowed(MC_EMEM_ARB_DA_TURNS); /* 0x0D0 */
|
||||
SetRegisterAllowed(MC_EMEM_ARB_DA_COVERS); /* 0x0D4 */
|
||||
SetRegisterAllowed(MC_EMEM_ARB_MISC0); /* 0x0D8 */
|
||||
SetRegisterAllowed(MC_EMEM_ARB_MISC1); /* 0x0DC */
|
||||
SetRegisterAllowed(MC_EMEM_ARB_RING1_THROTTLE); /* 0x0E0 */
|
||||
SetRegisterAllowed(MC_CLIENT_HOTRESET_CTRL); /* 0x200 */
|
||||
SetRegisterAllowed(MC_CLIENT_HOTRESET_STATUS); /* 0x204 */
|
||||
SetRegisterAllowed(MC_SMMU_AFI_ASID); /* 0x238 */
|
||||
SetRegisterAllowed(MC_SMMU_DC_ASID); /* 0x240 */
|
||||
SetRegisterAllowed(MC_SMMU_DCB_ASID); /* 0x244 */
|
||||
SetRegisterAllowed(MC_SMMU_HC_ASID); /* 0x250 */
|
||||
SetRegisterAllowed(MC_SMMU_HDA_ASID); /* 0x254 */
|
||||
SetRegisterAllowed(MC_SMMU_ISP2_ASID); /* 0x258 */
|
||||
SetRegisterAllowed(MC_SMMU_NVENC_ASID); /* 0x264 */
|
||||
SetRegisterAllowed(MC_SMMU_NV_ASID); /* 0x268 */
|
||||
SetRegisterAllowed(MC_SMMU_NV2_ASID); /* 0x26C */
|
||||
SetRegisterAllowed(MC_SMMU_PPCS_ASID); /* 0x270 */
|
||||
SetRegisterAllowed(MC_SMMU_SATA_ASID); /* 0x274 */
|
||||
SetRegisterAllowed(MC_SMMU_VI_ASID); /* 0x280 */
|
||||
SetRegisterAllowed(MC_SMMU_VIC_ASID); /* 0x284 */
|
||||
SetRegisterAllowed(MC_SMMU_XUSB_HOST_ASID); /* 0x288 */
|
||||
SetRegisterAllowed(MC_SMMU_XUSB_DEV_ASID); /* 0x28C */
|
||||
SetRegisterAllowed(MC_SMMU_TSEC_ASID); /* 0x294 */
|
||||
SetRegisterAllowed(MC_LATENCY_ALLOWANCE_AVPC_0); /* 0x2E4 */
|
||||
SetRegisterAllowed(MC_LATENCY_ALLOWANCE_DC_0); /* 0x2E8 */
|
||||
SetRegisterAllowed(MC_LATENCY_ALLOWANCE_DC_1); /* 0x2EC */
|
||||
SetRegisterAllowed(MC_LATENCY_ALLOWANCE_DCB_0); /* 0x2F4 */
|
||||
SetRegisterAllowed(MC_LATENCY_ALLOWANCE_DCB_1); /* 0x2F8 */
|
||||
SetRegisterAllowed(MC_LATENCY_ALLOWANCE_HC_0); /* 0x310 */
|
||||
SetRegisterAllowed(MC_LATENCY_ALLOWANCE_HC_1); /* 0x314 */
|
||||
SetRegisterAllowed(MC_LATENCY_ALLOWANCE_MPCORE_0); /* 0x320 */
|
||||
SetRegisterAllowed(MC_LATENCY_ALLOWANCE_NVENC_0); /* 0x328 */
|
||||
SetRegisterAllowed(MC_LATENCY_ALLOWANCE_PPCS_0); /* 0x344 */
|
||||
SetRegisterAllowed(MC_LATENCY_ALLOWANCE_PPCS_1); /* 0x348 */
|
||||
SetRegisterAllowed(MC_LATENCY_ALLOWANCE_ISP2_0); /* 0x370 */
|
||||
SetRegisterAllowed(MC_LATENCY_ALLOWANCE_ISP2_1); /* 0x374 */
|
||||
SetRegisterAllowed(MC_LATENCY_ALLOWANCE_XUSB_0); /* 0x37C */
|
||||
SetRegisterAllowed(MC_LATENCY_ALLOWANCE_XUSB_1); /* 0x380 */
|
||||
SetRegisterAllowed(MC_LATENCY_ALLOWANCE_TSEC_0); /* 0x390 */
|
||||
SetRegisterAllowed(MC_LATENCY_ALLOWANCE_VIC_0); /* 0x394 */
|
||||
SetRegisterAllowed(MC_LATENCY_ALLOWANCE_VI2_0); /* 0x398 */
|
||||
SetRegisterAllowed(MC_LATENCY_ALLOWANCE_GPU_0); /* 0x3AC */
|
||||
SetRegisterAllowed(MC_LATENCY_ALLOWANCE_SDMMCA_0); /* 0x3B8 */
|
||||
SetRegisterAllowed(MC_LATENCY_ALLOWANCE_SDMMCAA_0); /* 0x3BC */
|
||||
SetRegisterAllowed(MC_LATENCY_ALLOWANCE_SDMMC_0); /* 0x3C0 */
|
||||
SetRegisterAllowed(MC_LATENCY_ALLOWANCE_SDMMCAB_0); /* 0x3C4 */
|
||||
SetRegisterAllowed(MC_LATENCY_ALLOWANCE_NVDEC_0); /* 0x3D8 */
|
||||
SetRegisterAllowed(MC_LATENCY_ALLOWANCE_GPU2_0); /* 0x3E8 */
|
||||
SetRegisterAllowed(MC_DIS_PTSA_RATE); /* 0x41C */
|
||||
SetRegisterAllowed(MC_DIS_PTSA_MIN); /* 0x420 */
|
||||
SetRegisterAllowed(MC_DIS_PTSA_MAX); /* 0x424 */
|
||||
SetRegisterAllowed(MC_DISB_PTSA_RATE); /* 0x428 */
|
||||
SetRegisterAllowed(MC_DISB_PTSA_MIN); /* 0x42C */
|
||||
SetRegisterAllowed(MC_DISB_PTSA_MAX); /* 0x430 */
|
||||
SetRegisterAllowed(MC_VE_PTSA_RATE); /* 0x434 */
|
||||
SetRegisterAllowed(MC_VE_PTSA_MIN); /* 0x438 */
|
||||
SetRegisterAllowed(MC_VE_PTSA_MAX); /* 0x43C */
|
||||
SetRegisterAllowed(MC_MLL_MPCORER_PTSA_RATE); /* 0x44C */
|
||||
SetRegisterAllowed(MC_RING1_PTSA_RATE); /* 0x47C */
|
||||
SetRegisterAllowed(MC_RING1_PTSA_MIN); /* 0x480 */
|
||||
SetRegisterAllowed(MC_RING1_PTSA_MAX); /* 0x484 */
|
||||
SetRegisterAllowed(MC_PCX_PTSA_RATE); /* 0x4AC */
|
||||
SetRegisterAllowed(MC_PCX_PTSA_MIN); /* 0x4B0 */
|
||||
SetRegisterAllowed(MC_PCX_PTSA_MAX); /* 0x4B4 */
|
||||
SetRegisterAllowed(MC_MSE_PTSA_RATE); /* 0x4C4 */
|
||||
SetRegisterAllowed(MC_MSE_PTSA_MIN); /* 0x4C8 */
|
||||
SetRegisterAllowed(MC_MSE_PTSA_MAX); /* 0x4CC */
|
||||
SetRegisterAllowed(MC_AHB_PTSA_RATE); /* 0x4DC */
|
||||
SetRegisterAllowed(MC_AHB_PTSA_MIN); /* 0x4E0 */
|
||||
SetRegisterAllowed(MC_AHB_PTSA_MAX); /* 0x4E4 */
|
||||
SetRegisterAllowed(MC_APB_PTSA_RATE); /* 0x4E8 */
|
||||
SetRegisterAllowed(MC_APB_PTSA_MIN); /* 0x4EC */
|
||||
SetRegisterAllowed(MC_APB_PTSA_MAX); /* 0x4F0 */
|
||||
SetRegisterAllowed(MC_FTOP_PTSA_RATE); /* 0x50C */
|
||||
SetRegisterAllowed(MC_HOST_PTSA_RATE); /* 0x518 */
|
||||
SetRegisterAllowed(MC_HOST_PTSA_MIN); /* 0x51C */
|
||||
SetRegisterAllowed(MC_HOST_PTSA_MAX); /* 0x520 */
|
||||
SetRegisterAllowed(MC_USBX_PTSA_RATE); /* 0x524 */
|
||||
SetRegisterAllowed(MC_USBX_PTSA_MIN); /* 0x528 */
|
||||
SetRegisterAllowed(MC_USBX_PTSA_MAX); /* 0x52C */
|
||||
SetRegisterAllowed(MC_USBD_PTSA_RATE); /* 0x530 */
|
||||
SetRegisterAllowed(MC_USBD_PTSA_MIN); /* 0x534 */
|
||||
SetRegisterAllowed(MC_USBD_PTSA_MAX); /* 0x538 */
|
||||
SetRegisterAllowed(MC_GK_PTSA_RATE); /* 0x53C */
|
||||
SetRegisterAllowed(MC_GK_PTSA_MIN); /* 0x540 */
|
||||
SetRegisterAllowed(MC_GK_PTSA_MAX); /* 0x544 */
|
||||
SetRegisterAllowed(MC_AUD_PTSA_RATE); /* 0x548 */
|
||||
SetRegisterAllowed(MC_AUD_PTSA_MIN); /* 0x54C */
|
||||
SetRegisterAllowed(MC_AUD_PTSA_MAX); /* 0x550 */
|
||||
SetRegisterAllowed(MC_VICPC_PTSA_RATE); /* 0x554 */
|
||||
SetRegisterAllowed(MC_VICPC_PTSA_MIN); /* 0x558 */
|
||||
SetRegisterAllowed(MC_VICPC_PTSA_MAX); /* 0x55C */
|
||||
SetRegisterAllowed(MC_JPG_PTSA_RATE); /* 0x584 */
|
||||
SetRegisterAllowed(MC_JPG_PTSA_MIN); /* 0x588 */
|
||||
SetRegisterAllowed(MC_JPG_PTSA_MAX); /* 0x58C */
|
||||
SetRegisterAllowed(MC_GK2_PTSA_RATE); /* 0x610 */
|
||||
SetRegisterAllowed(MC_GK2_PTSA_MIN); /* 0x614 */
|
||||
SetRegisterAllowed(MC_GK2_PTSA_MAX); /* 0x618 */
|
||||
SetRegisterAllowed(MC_SDM_PTSA_RATE); /* 0x61C */
|
||||
SetRegisterAllowed(MC_SDM_PTSA_MIN); /* 0x620 */
|
||||
SetRegisterAllowed(MC_SDM_PTSA_MAX); /* 0x624 */
|
||||
SetRegisterAllowed(MC_HDAPC_PTSA_RATE); /* 0x628 */
|
||||
SetRegisterAllowed(MC_HDAPC_PTSA_MIN); /* 0x62C */
|
||||
SetRegisterAllowed(MC_HDAPC_PTSA_MAX); /* 0x630 */
|
||||
SetRegisterAllowed(MC_SEC_CARVEOUT_BOM); /* 0x670 */
|
||||
SetRegisterAllowed(MC_SEC_CARVEOUT_SIZE_MB); /* 0x674 */
|
||||
SetRegisterAllowed(MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0A); /* 0x690 */
|
||||
SetRegisterAllowed(MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0AB); /* 0x694 */
|
||||
SetRegisterAllowed(MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0B); /* 0x698 */
|
||||
SetRegisterAllowed(MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0BB); /* 0x69C */
|
||||
SetRegisterAllowed(MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0C); /* 0x6A0 */
|
||||
SetRegisterAllowed(MC_SCALED_LATENCY_ALLOWANCE_DISPLAY0CB); /* 0x6A4 */
|
||||
SetRegisterAllowed(MC_EMEM_ARB_TIMING_RFCPB); /* 0x6C0 */
|
||||
SetRegisterAllowed(MC_EMEM_ARB_TIMING_CCDMW); /* 0x6C4 */
|
||||
SetRegisterAllowed(MC_EMEM_ARB_REFPB_HP_CTRL); /* 0x6F0 */
|
||||
SetRegisterAllowed(MC_EMEM_ARB_REFPB_BANK_CTRL); /* 0x6F4 */
|
||||
SetRegisterAllowed(MC_PTSA_GRANT_DECREMENT); /* 0x960 */
|
||||
SetRegisterAllowed(MC_CLIENT_HOTRESET_CTRL_1); /* 0x970 */
|
||||
SetRegisterAllowed(MC_CLIENT_HOTRESET_STATUS_1); /* 0x974 */
|
||||
SetRegisterAllowed(MC_SMMU_PTC_FLUSH_1); /* 0x9B8 */
|
||||
SetRegisterAllowed(MC_SMMU_DC1_ASID); /* 0xA88 */
|
||||
SetRegisterAllowed(MC_SMMU_SDMMC1A_ASID); /* 0xA94 */
|
||||
SetRegisterAllowed(MC_SMMU_SDMMC2A_ASID); /* 0xA98 */
|
||||
SetRegisterAllowed(MC_SMMU_SDMMC3A_ASID); /* 0xA9C */
|
||||
SetRegisterAllowed(MC_SMMU_SDMMC4A_ASID); /* 0xAA0 */
|
||||
SetRegisterAllowed(MC_SMMU_ISP2B_ASID); /* 0xAA4 */
|
||||
SetRegisterAllowed(MC_SMMU_GPU_ASID); /* 0xAA8 */
|
||||
SetRegisterAllowed(MC_SMMU_GPUB_ASID); /* 0xAAC */
|
||||
SetRegisterAllowed(MC_SMMU_PPCS2_ASID); /* 0xAB0 */
|
||||
SetRegisterAllowed(MC_SMMU_NVDEC_ASID); /* 0xAB4 */
|
||||
SetRegisterAllowed(MC_SMMU_APE_ASID); /* 0xAB8 */
|
||||
SetRegisterAllowed(MC_SMMU_SE_ASID); /* 0xABC */
|
||||
SetRegisterAllowed(MC_SMMU_NVJPG_ASID); /* 0xAC0 */
|
||||
SetRegisterAllowed(MC_SMMU_HC1_ASID); /* 0xAC4 */
|
||||
SetRegisterAllowed(MC_SMMU_SE1_ASID); /* 0xAC8 */
|
||||
SetRegisterAllowed(MC_SMMU_AXIAP_ASID); /* 0xACC */
|
||||
SetRegisterAllowed(MC_SMMU_ETR_ASID); /* 0xAD0 */
|
||||
SetRegisterAllowed(MC_SMMU_TSECB_ASID); /* 0xAD4 */
|
||||
SetRegisterAllowed(MC_SMMU_TSEC1_ASID); /* 0xAD8 */
|
||||
SetRegisterAllowed(MC_SMMU_TSECB1_ASID); /* 0xADC */
|
||||
SetRegisterAllowed(MC_SMMU_NVDEC1_ASID); /* 0xAE0 */
|
||||
SetRegisterAllowed(MC_EMEM_ARB_DHYST_CTRL); /* 0xBCC */
|
||||
SetRegisterAllowed(MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_0); /* 0xBD0 */
|
||||
SetRegisterAllowed(MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_1); /* 0xBD4 */
|
||||
SetRegisterAllowed(MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_2); /* 0xBD8 */
|
||||
SetRegisterAllowed(MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_3); /* 0xBDC */
|
||||
SetRegisterAllowed(MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_4); /* 0xBE0 */
|
||||
SetRegisterAllowed(MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_5); /* 0xBE4 */
|
||||
SetRegisterAllowed(MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_6); /* 0xBE8 */
|
||||
SetRegisterAllowed(MC_EMEM_ARB_DHYST_TIMEOUT_UTIL_7); /* 0xBEC */
|
||||
SetRegisterAllowed(MC_ERR_GENERALIZED_CARVEOUT_STATUS); /* 0xC00 */
|
||||
SetRegisterAllowed(MC_SECURITY_CARVEOUT2_BOM); /* 0xC5C */
|
||||
SetRegisterAllowed(MC_SECURITY_CARVEOUT3_BOM); /* 0xCAC */
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
SetRegisterAllowed(APBDEV_PMC_CNTRL); /* 0x000 */
|
||||
SetRegisterAllowed(APBDEV_PMC_WAKE_MASK); /* 0x00C */
|
||||
SetRegisterAllowed(APBDEV_PMC_WAKE_LVL); /* 0x010 */
|
||||
SetRegisterAllowed(APBDEV_PMC_WAKE_STATUS); /* 0x014 */
|
||||
SetRegisterAllowed(APBDEV_PMC_DPD_PADS_ORIDE); /* 0x01C */
|
||||
SetRegisterAllowed(APBDEV_PMC_DPD_SAMPLE); /* 0x020 */
|
||||
SetRegisterAllowed(APBDEV_PMC_CLAMP_STATUS); /* 0x02C */
|
||||
SetRegisterAllowed(APBDEV_PMC_PWRGATE_TOGGLE); /* 0x030 */
|
||||
SetRegisterAllowed(APBDEV_PMC_REMOVE_CLAMPING_CMD ); /* 0x034 */
|
||||
SetRegisterAllowed(APBDEV_PMC_PWRGATE_STATUS); /* 0x038 */
|
||||
SetRegisterAllowed(APBDEV_PMC_PWRGOOD_TIMER); /* 0x03C */
|
||||
SetRegisterAllowed(APBDEV_PMC_BLINK_TIMER); /* 0x040 */
|
||||
SetRegisterAllowed(APBDEV_PMC_NO_IOPOWER); /* 0x044 */
|
||||
SetRegisterAllowed(APBDEV_PMC_PWR_DET); /* 0x048 */
|
||||
SetRegisterAllowed(APBDEV_PMC_AUTO_WAKE_LVL_MASK); /* 0x0DC */
|
||||
SetRegisterAllowed(APBDEV_PMC_WAKE_DELAY); /* 0x0E0 */
|
||||
SetRegisterAllowed(APBDEV_PMC_PWR_DET_VAL); /* 0x0E4 */
|
||||
SetRegisterAllowed(APBDEV_PMC_WAKE2_MASK); /* 0x160 */
|
||||
SetRegisterAllowed(APBDEV_PMC_WAKE2_LVL); /* 0x164 */
|
||||
SetRegisterAllowed(APBDEV_PMC_WAKE2_STATUS); /* 0x168 */
|
||||
SetRegisterAllowed(APBDEV_PMC_AUTO_WAKE2_LVL_MASK ); /* 0x170 */
|
||||
SetRegisterAllowed(APBDEV_PMC_CLK_OUT_CNTRL); /* 0x1A8 */
|
||||
SetRegisterAllowed(APBDEV_PMC_IO_DPD_REQ); /* 0x1B8 */
|
||||
SetRegisterAllowed(APBDEV_PMC_IO_DPD_STATUS); /* 0x1BC */
|
||||
SetRegisterAllowed(APBDEV_PMC_IO_DPD2_REQ); /* 0x1C0 */
|
||||
SetRegisterAllowed(APBDEV_PMC_IO_DPD2_STATUS); /* 0x1C4 */
|
||||
SetRegisterAllowed(APBDEV_PMC_SEL_DPD_TIM); /* 0x1C8 */
|
||||
SetRegisterAllowed(APBDEV_PMC_TSC_MULT); /* 0x2B4 */
|
||||
SetRegisterAllowed(APBDEV_PMC_GPU_RG_CNTRL); /* 0x2D4 */
|
||||
SetRegisterAllowed(APBDEV_PMC_CNTRL2); /* 0x440 */
|
||||
SetRegisterAllowed(APBDEV_PMC_WAKE_DEBOUNCE_EN); /* 0x4D8 */
|
96
exosphere/program/source/smc/secmon_random_cache.cpp
Normal file
96
exosphere/program/source/smc/secmon_random_cache.cpp
Normal file
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include "secmon_smc_common.hpp"
|
||||
#include "secmon_random_cache.hpp"
|
||||
|
||||
namespace ams::secmon::smc {
|
||||
|
||||
namespace {
|
||||
|
||||
constinit int g_random_offset_low = 0;
|
||||
constinit int g_random_offset_high = 0;
|
||||
|
||||
void FillRandomCache(int offset, int size) {
|
||||
/* Get the cache. */
|
||||
u8 * const random_cache_loc = GetRandomBytesCache() + offset;
|
||||
|
||||
/* Flush the region we're about to fill to ensure consistency with the SE. */
|
||||
hw::FlushDataCache(random_cache_loc, size);
|
||||
hw::DataSynchronizationBarrierInnerShareable();
|
||||
|
||||
/* Generate random bytes. */
|
||||
se::GenerateRandomBytes(random_cache_loc, size);
|
||||
hw::DataSynchronizationBarrierInnerShareable();
|
||||
|
||||
/* Flush to ensure the CPU sees consistent data for the region. */
|
||||
hw::FlushDataCache(random_cache_loc, size);
|
||||
hw::DataSynchronizationBarrierInnerShareable();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void FillRandomCache() {
|
||||
/* Fill the cache. */
|
||||
FillRandomCache(0, GetRandomBytesCacheSize());
|
||||
|
||||
/* Set the extents. */
|
||||
g_random_offset_low = 0;
|
||||
g_random_offset_high = GetRandomBytesCacheSize() - 1;
|
||||
}
|
||||
|
||||
void RefillRandomCache() {
|
||||
/* Check that we need to do any refilling. */
|
||||
if (const int used_start = (g_random_offset_high + 1) % GetRandomBytesCacheSize(); used_start != g_random_offset_low) {
|
||||
if (used_start < g_random_offset_low) {
|
||||
/* The region we need to fill is after used_start but before g_random_offset_low. */
|
||||
const auto size = g_random_offset_low - used_start;
|
||||
FillRandomCache(used_start, size);
|
||||
g_random_offset_high += size;
|
||||
} else {
|
||||
/* We need to fill the space from high to the end and from low to start. */
|
||||
const int high_size = GetRandomBytesCacheSize() - used_start;
|
||||
if (high_size > 0) {
|
||||
FillRandomCache(used_start, high_size);
|
||||
g_random_offset_high += high_size;
|
||||
}
|
||||
|
||||
const int low_size = g_random_offset_low;
|
||||
if (low_size > 0) {
|
||||
FillRandomCache(0, low_size);
|
||||
g_random_offset_high += low_size;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
g_random_offset_high %= GetRandomBytesCacheSize();
|
||||
}
|
||||
}
|
||||
|
||||
void GetRandomFromCache(void *dst, size_t size) {
|
||||
/* Copy out the requested size. */
|
||||
std::memcpy(dst, GetRandomBytesCache() + g_random_offset_low, size);
|
||||
|
||||
/* Advance. */
|
||||
g_random_offset_low += size;
|
||||
|
||||
/* Ensure that at all times g_random_offset_low is not within 0x38 bytes of the end of the pool. */
|
||||
if (g_random_offset_low + MaxRandomBytes >= GetRandomBytesCacheSize()) {
|
||||
g_random_offset_low = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
28
exosphere/program/source/smc/secmon_random_cache.hpp
Normal file
28
exosphere/program/source/smc/secmon_random_cache.hpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <exosphere.hpp>
|
||||
#include "secmon_smc_common.hpp"
|
||||
|
||||
namespace ams::secmon::smc {
|
||||
|
||||
constexpr inline size_t MaxRandomBytes = sizeof(SmcArguments) - sizeof(SmcArguments{}.r[0]);
|
||||
|
||||
void FillRandomCache();
|
||||
void RefillRandomCache();
|
||||
void GetRandomFromCache(void *dst, size_t size);
|
||||
|
||||
}
|
768
exosphere/program/source/smc/secmon_smc_aes.cpp
Normal file
768
exosphere/program/source/smc/secmon_smc_aes.cpp
Normal file
|
@ -0,0 +1,768 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include "../secmon_error.hpp"
|
||||
#include "../secmon_key_storage.hpp"
|
||||
#include "../secmon_misc.hpp"
|
||||
#include "../secmon_page_mapper.hpp"
|
||||
#include "secmon_smc_aes.hpp"
|
||||
#include "secmon_smc_device_unique_data.hpp"
|
||||
#include "secmon_smc_se_lock.hpp"
|
||||
|
||||
namespace ams::secmon::smc {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline auto AesKeySize = se::AesBlockSize;
|
||||
constexpr inline size_t CmacSizeMax = 1_KB;
|
||||
constexpr inline size_t DeviceUniqueDataSizeMin = 0x130;
|
||||
constexpr inline size_t DeviceUniqueDataSizeMax = 0x240;
|
||||
|
||||
enum SealKey {
|
||||
SealKey_LoadAesKey = 0,
|
||||
SealKey_DecryptDeviceUniqueData = 1,
|
||||
SealKey_ImportLotusKey = 2,
|
||||
SealKey_ImportEsDeviceKey = 3,
|
||||
SealKey_ReencryptDeviceUniqueData = 4,
|
||||
SealKey_ImportSslKey = 5,
|
||||
SealKey_ImportEsClientCertKey = 6,
|
||||
|
||||
SealKey_Count,
|
||||
};
|
||||
|
||||
enum KeyType {
|
||||
KeyType_Default = 0,
|
||||
KeyType_NormalOnly = 1,
|
||||
KeyType_RecoveryOnly = 2,
|
||||
KeyType_NormalAndRecovery = 3,
|
||||
|
||||
KeyType_Count,
|
||||
};
|
||||
|
||||
enum CipherMode {
|
||||
CipherMode_CbcEncryption = 0,
|
||||
CipherMode_CbcDecryption = 1,
|
||||
CipherMode_Ctr = 2,
|
||||
CipherMode_Cmac = 3,
|
||||
};
|
||||
|
||||
enum SpecificAesKey {
|
||||
SpecificAesKey_CalibrationEncryption0 = 0,
|
||||
SpecificAesKey_CalibrationEncryption1 = 1,
|
||||
|
||||
SpecificAesKey_Count,
|
||||
};
|
||||
|
||||
enum DeviceUniqueData {
|
||||
DeviceUniqueData_DecryptDeviceUniqueData = 0,
|
||||
DeviceUniqueData_ImportLotusKey = 1,
|
||||
DeviceUniqueData_ImportEsDeviceKey = 2,
|
||||
DeviceUniqueData_ImportSslKey = 3,
|
||||
DeviceUniqueData_ImportEsClientCertKey = 4,
|
||||
|
||||
DeviceUniqueData_Count,
|
||||
};
|
||||
|
||||
/* Ensure that our "subtract one" simplification is valid for the cases we care about. */
|
||||
static_assert(DeviceUniqueData_ImportLotusKey - 1 == ImportRsaKey_Lotus);
|
||||
static_assert(DeviceUniqueData_ImportEsDeviceKey - 1 == ImportRsaKey_EsDrmCert);
|
||||
static_assert(DeviceUniqueData_ImportSslKey - 1 == ImportRsaKey_Ssl);
|
||||
static_assert(DeviceUniqueData_ImportEsClientCertKey - 1 == ImportRsaKey_EsClientCert);
|
||||
|
||||
constexpr ImportRsaKey ConvertToImportRsaKey(DeviceUniqueData data) {
|
||||
/* Not necessary, but if this is invoked at compile-time this will force a compile-time error. */
|
||||
AMS_ASSUME(data != DeviceUniqueData_DecryptDeviceUniqueData);
|
||||
AMS_ASSUME(data < DeviceUniqueData_Count);
|
||||
|
||||
return static_cast<ImportRsaKey>(static_cast<int>(data) - 1);
|
||||
}
|
||||
|
||||
enum SecureData {
|
||||
SecureData_Calibration = 0,
|
||||
SecureData_SafeMode = 1,
|
||||
SecureData_UserSystemProperEncryption = 2,
|
||||
SecureData_UserSystem = 3,
|
||||
|
||||
SecureData_Count,
|
||||
};
|
||||
|
||||
struct GenerateAesKekOption {
|
||||
using IsDeviceUnique = util::BitPack32::Field<0, 1, bool>;
|
||||
using KeyTypeIndex = util::BitPack32::Field<1, 4, KeyType>;
|
||||
using SealKeyIndex = util::BitPack32::Field<5, 3, SealKey>;
|
||||
using Reserved = util::BitPack32::Field<8, 24, u32>;
|
||||
};
|
||||
|
||||
struct ComputeAesOption {
|
||||
using KeySlot = util::BitPack32::Field<0, 3, int>;
|
||||
using CipherModeIndex = util::BitPack32::Field<4, 2, CipherMode>;
|
||||
};
|
||||
|
||||
struct DecryptDeviceUniqueDataOption {
|
||||
using DeviceUniqueDataIndex = util::BitPack32::Field<0, 3, DeviceUniqueData>;
|
||||
using Reserved = util::BitPack32::Field<3, 29, u32>;
|
||||
};
|
||||
|
||||
constexpr const u8 SealKeySources[SealKey_Count][AesKeySize] = {
|
||||
[SealKey_LoadAesKey] = { 0xF4, 0x0C, 0x16, 0x26, 0x0D, 0x46, 0x3B, 0xE0, 0x8C, 0x6A, 0x56, 0xE5, 0x82, 0xD4, 0x1B, 0xF6 },
|
||||
[SealKey_DecryptDeviceUniqueData] = { 0x7F, 0x54, 0x2C, 0x98, 0x1E, 0x54, 0x18, 0x3B, 0xBA, 0x63, 0xBD, 0x4C, 0x13, 0x5B, 0xF1, 0x06 },
|
||||
[SealKey_ImportLotusKey] = { 0xC7, 0x3F, 0x73, 0x60, 0xB7, 0xB9, 0x9D, 0x74, 0x0A, 0xF8, 0x35, 0x60, 0x1A, 0x18, 0x74, 0x63 },
|
||||
[SealKey_ImportEsDeviceKey] = { 0x0E, 0xE0, 0xC4, 0x33, 0x82, 0x66, 0xE8, 0x08, 0x39, 0x13, 0x41, 0x7D, 0x04, 0x64, 0x2B, 0x6D },
|
||||
[SealKey_ReencryptDeviceUniqueData] = { 0xE1, 0xA8, 0xAA, 0x6A, 0x2D, 0x9C, 0xDE, 0x43, 0x0C, 0xDE, 0xC6, 0x17, 0xF6, 0xC7, 0xF1, 0xDE },
|
||||
[SealKey_ImportSslKey] = { 0x74, 0x20, 0xF6, 0x46, 0x77, 0xB0, 0x59, 0x2C, 0xE8, 0x1B, 0x58, 0x64, 0x47, 0x41, 0x37, 0xD9 },
|
||||
[SealKey_ImportEsClientCertKey] = { 0xAA, 0x19, 0x0F, 0xFA, 0x4C, 0x30, 0x3B, 0x2E, 0xE6, 0xD8, 0x9A, 0xCF, 0xE5, 0x3F, 0xB3, 0x4B },
|
||||
};
|
||||
|
||||
constexpr const u8 KeyTypeSources[KeyType_Count][AesKeySize] = {
|
||||
[KeyType_Default] = { 0x4D, 0x87, 0x09, 0x86, 0xC4, 0x5D, 0x20, 0x72, 0x2F, 0xBA, 0x10, 0x53, 0xDA, 0x92, 0xE8, 0xA9 },
|
||||
[KeyType_NormalOnly] = { 0x25, 0x03, 0x31, 0xFB, 0x25, 0x26, 0x0B, 0x79, 0x8C, 0x80, 0xD2, 0x69, 0x98, 0xE2, 0x22, 0x77 },
|
||||
[KeyType_RecoveryOnly] = { 0x76, 0x14, 0x1D, 0x34, 0x93, 0x2D, 0xE1, 0x84, 0x24, 0x7B, 0x66, 0x65, 0x55, 0x04, 0x65, 0x81 },
|
||||
[KeyType_NormalAndRecovery] = { 0xAF, 0x3D, 0xB7, 0xF3, 0x08, 0xA2, 0xD8, 0xA2, 0x08, 0xCA, 0x18, 0xA8, 0x69, 0x46, 0xC9, 0x0B },
|
||||
};
|
||||
|
||||
constexpr const u8 SealKeyMasks[SealKey_Count][AesKeySize] = {
|
||||
[SealKey_LoadAesKey] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
|
||||
[SealKey_DecryptDeviceUniqueData] = { 0xA2, 0xAB, 0xBF, 0x9C, 0x92, 0x2F, 0xBB, 0xE3, 0x78, 0x79, 0x9B, 0xC0, 0xCC, 0xEA, 0xA5, 0x74 },
|
||||
[SealKey_ImportLotusKey] = { 0x57, 0xE2, 0xD9, 0x45, 0xE4, 0x92, 0xF4, 0xFD, 0xC3, 0xF9, 0x86, 0x38, 0x89, 0x78, 0x9F, 0x3C },
|
||||
[SealKey_ImportEsDeviceKey] = { 0xE5, 0x4D, 0x9A, 0x02, 0xF0, 0x4F, 0x5F, 0xA8, 0xAD, 0x76, 0x0A, 0xF6, 0x32, 0x95, 0x59, 0xBB },
|
||||
[SealKey_ReencryptDeviceUniqueData] = { 0x59, 0xD9, 0x31, 0xF4, 0xA7, 0x97, 0xB8, 0x14, 0x40, 0xD6, 0xA2, 0x60, 0x2B, 0xED, 0x15, 0x31 },
|
||||
[SealKey_ImportSslKey] = { 0xFD, 0x6A, 0x25, 0xE5, 0xD8, 0x38, 0x7F, 0x91, 0x49, 0xDA, 0xF8, 0x59, 0xA8, 0x28, 0xE6, 0x75 },
|
||||
[SealKey_ImportEsClientCertKey] = { 0x89, 0x96, 0x43, 0x9A, 0x7C, 0xD5, 0x59, 0x55, 0x24, 0xD5, 0x24, 0x18, 0xAB, 0x6C, 0x04, 0x61 },
|
||||
};
|
||||
|
||||
constexpr const SealKey DeviceUniqueDataToSealKey[DeviceUniqueData_Count] = {
|
||||
[DeviceUniqueData_DecryptDeviceUniqueData] = SealKey_DecryptDeviceUniqueData,
|
||||
[DeviceUniqueData_ImportLotusKey] = SealKey_ImportLotusKey,
|
||||
[DeviceUniqueData_ImportEsDeviceKey] = SealKey_ImportEsDeviceKey,
|
||||
[DeviceUniqueData_ImportSslKey] = SealKey_ImportSslKey,
|
||||
[DeviceUniqueData_ImportEsClientCertKey] = SealKey_ImportEsClientCertKey,
|
||||
};
|
||||
|
||||
constexpr const u8 CalibrationKeySource[AesKeySize] = {
|
||||
0xE2, 0xD6, 0xB8, 0x7A, 0x11, 0x9C, 0xB8, 0x80, 0xE8, 0x22, 0x88, 0x8A, 0x46, 0xFB, 0xA1, 0x95
|
||||
};
|
||||
|
||||
constexpr const u8 EsCommonKeySources[EsCommonKeyType_Count][AesKeySize] = {
|
||||
[EsCommonKeyType_TitleKey] = { 0x1E, 0xDC, 0x7B, 0x3B, 0x60, 0xE6, 0xB4, 0xD8, 0x78, 0xB8, 0x17, 0x15, 0x98, 0x5E, 0x62, 0x9B },
|
||||
[EsCommonKeyType_ArchiveKey] = { 0x3B, 0x78, 0xF2, 0x61, 0x0F, 0x9D, 0x5A, 0xE2, 0x7B, 0x4E, 0x45, 0xAF, 0xCB, 0x0B, 0x67, 0x4D },
|
||||
};
|
||||
|
||||
constexpr const u8 EsSealKeySource[AesKeySize] = {
|
||||
0xCB, 0xB7, 0x6E, 0x38, 0xA1, 0xCB, 0x77, 0x0F, 0xB2, 0xA5, 0xB2, 0x9D, 0xD8, 0x56, 0x9F, 0x76
|
||||
};
|
||||
|
||||
constexpr const u8 SecureDataSource[AesKeySize] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
|
||||
constexpr const u8 SecureDataCounters[][AesKeySize] = {
|
||||
[SecureData_Calibration] = { 0x3C, 0xD5, 0x92, 0xEC, 0x68, 0x31, 0x4A, 0x06, 0xD4, 0x1B, 0x0C, 0xD9, 0xF6, 0x2E, 0xD9, 0xE9 },
|
||||
[SecureData_SafeMode] = { 0x50, 0x81, 0xCF, 0x77, 0x18, 0x11, 0xD7, 0x0D, 0x13, 0x29, 0x60, 0xED, 0x4B, 0x21, 0x3E, 0xFC },
|
||||
[SecureData_UserSystemProperEncryption] = { 0x98, 0xCB, 0x4C, 0xEB, 0x15, 0xF1, 0x4A, 0x5A, 0x7A, 0x86, 0xB6, 0xF1, 0x94, 0x66, 0xF4, 0x9D },
|
||||
};
|
||||
|
||||
constexpr const u8 SecureDataTweaks[][AesKeySize] = {
|
||||
[SecureData_Calibration] = { 0xAC, 0xCA, 0x9A, 0xCA, 0xFF, 0x2E, 0xB9, 0x22, 0xCC, 0x1F, 0x4F, 0xAD, 0xDD, 0x77, 0x21, 0x1E },
|
||||
[SecureData_SafeMode] = { 0x6E, 0xF8, 0x2A, 0x1A, 0xE0, 0x4F, 0xC3, 0x20, 0x08, 0x7B, 0xBA, 0x50, 0xC0, 0xCD, 0x7B, 0x39 },
|
||||
[SecureData_UserSystemProperEncryption] = { 0x6D, 0x02, 0x56, 0x2D, 0xF4, 0x3D, 0x0A, 0x15, 0xB1, 0x34, 0x5C, 0xC2, 0x84, 0x4C, 0xD4, 0x28 },
|
||||
};
|
||||
|
||||
constexpr const u8 *GetSecureDataCounter(SecureData which) {
|
||||
switch (which) {
|
||||
case SecureData_Calibration:
|
||||
return SecureDataCounters[SecureData_Calibration];
|
||||
case SecureData_SafeMode:
|
||||
return SecureDataCounters[SecureData_SafeMode];
|
||||
case SecureData_UserSystem:
|
||||
case SecureData_UserSystemProperEncryption:
|
||||
return SecureDataCounters[SecureData_UserSystemProperEncryption];
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr const u8 *GetSecureDataTweak(SecureData which) {
|
||||
switch (which) {
|
||||
case SecureData_Calibration:
|
||||
return SecureDataTweaks[SecureData_Calibration];
|
||||
case SecureData_SafeMode:
|
||||
return SecureDataTweaks[SecureData_SafeMode];
|
||||
case SecureData_UserSystem:
|
||||
case SecureData_UserSystemProperEncryption:
|
||||
return SecureDataTweaks[SecureData_UserSystemProperEncryption];
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr uintptr_t LinkedListAddressMinimum = secmon::MemoryRegionDram.GetAddress();
|
||||
constexpr size_t LinkedListAddressRangeSize = 4_MB - 2_KB;
|
||||
constexpr uintptr_t LinkedListAddressMaximum = LinkedListAddressMinimum + LinkedListAddressRangeSize;
|
||||
|
||||
constexpr size_t LinkedListSize = 12;
|
||||
|
||||
constexpr bool IsValidLinkedListAddress(uintptr_t address) {
|
||||
return LinkedListAddressMinimum <= address && address <= (LinkedListAddressMaximum - LinkedListSize);
|
||||
}
|
||||
|
||||
constinit bool g_is_compute_aes_completed = false;
|
||||
|
||||
void SecurityEngineDoneHandler() {
|
||||
/* Check that the compute succeeded. */
|
||||
se::ValidateAesOperationResult();
|
||||
|
||||
/* End the asynchronous operation. */
|
||||
g_is_compute_aes_completed = true;
|
||||
EndAsyncOperation();
|
||||
}
|
||||
|
||||
SmcResult GetComputeAesResult(void *dst, size_t size) {
|
||||
/* Arguments are unused. */
|
||||
AMS_UNUSED(dst);
|
||||
AMS_UNUSED(size);
|
||||
|
||||
/* Check that the operation is completed. */
|
||||
SMC_R_UNLESS(g_is_compute_aes_completed, Busy);
|
||||
|
||||
/* Unlock the security engine and succeed. */
|
||||
UnlockSecurityEngine();
|
||||
return SmcResult::Success;
|
||||
}
|
||||
|
||||
int PrepareMasterKey(int generation) {
|
||||
if (generation == GetKeyGeneration()) {
|
||||
return pkg1::AesKeySlot_Master;
|
||||
}
|
||||
|
||||
constexpr int Slot = pkg1::AesKeySlot_Smc;
|
||||
LoadMasterKey(Slot, generation);
|
||||
|
||||
return Slot;
|
||||
}
|
||||
|
||||
int PrepareDeviceMasterKey(int generation) {
|
||||
if (generation == pkg1::KeyGeneration_1_0_0) {
|
||||
return pkg1::AesKeySlot_Device;
|
||||
}
|
||||
if (generation == GetKeyGeneration()) {
|
||||
return pkg1::AesKeySlot_DeviceMaster;
|
||||
}
|
||||
|
||||
constexpr int Slot = pkg1::AesKeySlot_Smc;
|
||||
LoadDeviceMasterKey(Slot, generation);
|
||||
|
||||
return Slot;
|
||||
}
|
||||
|
||||
void GetSecureDataImpl(u8 *dst, SecureData which, bool tweak) {
|
||||
/* Compute the appropriate AES-CTR. */
|
||||
se::ComputeAes128Ctr(dst, AesKeySize, pkg1::AesKeySlot_Device, SecureDataSource, AesKeySize, GetSecureDataCounter(which), AesKeySize);
|
||||
|
||||
/* Tweak, if we should. */
|
||||
if (tweak) {
|
||||
const u8 * const tweak = GetSecureDataTweak(which);
|
||||
|
||||
for (size_t i = 0; i < AesKeySize; ++i) {
|
||||
dst[i] ^= tweak[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SmcResult GenerateAesKekImpl(SmcArguments &args) {
|
||||
/* Decode arguments. */
|
||||
u8 kek_source[AesKeySize];
|
||||
std::memcpy(kek_source, std::addressof(args.r[1]), AesKeySize);
|
||||
|
||||
const int generation = std::max<int>(static_cast<int>(args.r[3]) - 1, pkg1::KeyGeneration_1_0_0);
|
||||
|
||||
const util::BitPack32 option = { static_cast<u32>(args.r[4]) };
|
||||
const bool is_device_unique = option.Get<GenerateAesKekOption::IsDeviceUnique>();
|
||||
const auto key_type = option.Get<GenerateAesKekOption::KeyTypeIndex>();
|
||||
const auto seal_key = option.Get<GenerateAesKekOption::SealKeyIndex>();
|
||||
const u32 reserved = option.Get<GenerateAesKekOption::Reserved>();
|
||||
|
||||
/* Validate arguments. */
|
||||
SMC_R_UNLESS(reserved == 0, InvalidArgument);
|
||||
|
||||
if (is_device_unique) {
|
||||
SMC_R_UNLESS(pkg1::IsValidDeviceUniqueKeyGeneration(generation), InvalidArgument);
|
||||
} else {
|
||||
SMC_R_UNLESS(pkg1::IsValidKeyGeneration(generation), InvalidArgument);
|
||||
SMC_R_UNLESS(generation <= GetKeyGeneration(), InvalidArgument);
|
||||
}
|
||||
|
||||
SMC_R_UNLESS(0 <= key_type && key_type < KeyType_Count, InvalidArgument);
|
||||
SMC_R_UNLESS(0 <= seal_key && seal_key < SealKey_Count, InvalidArgument);
|
||||
|
||||
switch (key_type) {
|
||||
case KeyType_NormalOnly: SMC_R_UNLESS(!IsRecoveryBoot(), InvalidArgument); break;
|
||||
case KeyType_RecoveryOnly: SMC_R_UNLESS( IsRecoveryBoot(), InvalidArgument); break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
/* Declare temporary data storage. */
|
||||
u8 static_source[AesKeySize];
|
||||
u8 generated_key[AesKeySize];
|
||||
u8 access_key[AesKeySize];
|
||||
|
||||
/* Derive the static source. */
|
||||
for (size_t i = 0; i < sizeof(static_source); ++i) {
|
||||
static_source[i] = KeyTypeSources[key_type][i] ^ SealKeyMasks[seal_key][i];
|
||||
}
|
||||
|
||||
/* Get the seal key source. */
|
||||
const u8 * const seal_key_source = SealKeySources[seal_key];
|
||||
|
||||
/* Get the key slot. */
|
||||
const int slot = is_device_unique ? PrepareDeviceMasterKey(generation) : PrepareMasterKey(generation);
|
||||
|
||||
/* Derive a static generation kek. */
|
||||
se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, slot, static_source, sizeof(static_source));
|
||||
|
||||
/* Decrypt the input with the static generation kek. */
|
||||
se::DecryptAes128(generated_key, sizeof(generated_key), pkg1::AesKeySlot_Smc, kek_source, sizeof(kek_source));
|
||||
|
||||
/* Generate the seal key. */
|
||||
se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_RandomForUserWrap, seal_key_source, AesKeySize);
|
||||
|
||||
/* Seal the generated key. */
|
||||
se::EncryptAes128(access_key, sizeof(access_key), pkg1::AesKeySlot_Smc, generated_key, sizeof(generated_key));
|
||||
|
||||
/* Copy the access key out. */
|
||||
std::memcpy(std::addressof(args.r[1]), access_key, sizeof(access_key));
|
||||
return SmcResult::Success;
|
||||
}
|
||||
|
||||
SmcResult LoadAesKeyImpl(SmcArguments &args) {
|
||||
/* Decode arguments. */
|
||||
const int slot = args.r[1];
|
||||
|
||||
u8 access_key[AesKeySize];
|
||||
std::memcpy(access_key, std::addressof(args.r[2]), sizeof(access_key));
|
||||
|
||||
u8 key_source[AesKeySize];
|
||||
std::memcpy(key_source, std::addressof(args.r[4]), sizeof(key_source));
|
||||
|
||||
/* Validate arguments. */
|
||||
SMC_R_UNLESS(pkg1::IsUserAesKeySlot(slot), InvalidArgument);
|
||||
|
||||
/* Get the seal key source. */
|
||||
constexpr const u8 * const SealKeySource = SealKeySources[SealKey_LoadAesKey];
|
||||
|
||||
/* Derive the seal key. */
|
||||
se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_RandomForUserWrap, SealKeySource, AesKeySize);
|
||||
|
||||
/* Unseal the access key. */
|
||||
se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_Smc, access_key, sizeof(access_key));
|
||||
|
||||
/* Derive the key. */
|
||||
se::SetEncryptedAesKey128(slot, pkg1::AesKeySlot_Smc, key_source, sizeof(key_source));
|
||||
|
||||
return SmcResult::Success;
|
||||
}
|
||||
|
||||
SmcResult ComputeAesImpl(SmcArguments &args) {
|
||||
/* Decode arguments. */
|
||||
u8 iv[se::AesBlockSize];
|
||||
|
||||
const util::BitPack32 option = { static_cast<u32>(args.r[1]) };
|
||||
std::memcpy(iv, std::addressof(args.r[2]), sizeof(iv));
|
||||
const u32 input_address = args.r[4];
|
||||
const u32 output_address = args.r[5];
|
||||
const u32 size = args.r[6];
|
||||
|
||||
const int slot = option.Get<ComputeAesOption::KeySlot>();
|
||||
const auto cipher_mode = option.Get<ComputeAesOption::CipherModeIndex>();
|
||||
|
||||
/* Validate arguments. */
|
||||
SMC_R_UNLESS(pkg1::IsUserAesKeySlot(slot), InvalidArgument);
|
||||
SMC_R_UNLESS(util::IsAligned(size, se::AesBlockSize), InvalidArgument);
|
||||
SMC_R_UNLESS(IsValidLinkedListAddress(input_address), InvalidArgument);
|
||||
SMC_R_UNLESS(IsValidLinkedListAddress(output_address), InvalidArgument);
|
||||
|
||||
/* We're starting an aes operation, so reset the completion status. */
|
||||
g_is_compute_aes_completed = false;
|
||||
|
||||
/* Dispatch the correct aes operation asynchronously. */
|
||||
switch (cipher_mode) {
|
||||
case CipherMode_CbcEncryption: se::EncryptAes128CbcAsync(output_address, slot, input_address, size, iv, sizeof(iv), SecurityEngineDoneHandler); break;
|
||||
case CipherMode_CbcDecryption: se::DecryptAes128CbcAsync(output_address, slot, input_address, size, iv, sizeof(iv), SecurityEngineDoneHandler); break;
|
||||
case CipherMode_Ctr: se::ComputeAes128CtrAsync(output_address, slot, input_address, size, iv, sizeof(iv), SecurityEngineDoneHandler); break;
|
||||
case CipherMode_Cmac:
|
||||
return SmcResult::NotImplemented;
|
||||
default:
|
||||
return SmcResult::InvalidArgument;
|
||||
}
|
||||
|
||||
return SmcResult::Success;
|
||||
}
|
||||
|
||||
SmcResult GenerateSpecificAesKeyImpl(SmcArguments &args) {
|
||||
/* Decode arguments. */
|
||||
u8 key_source[AesKeySize];
|
||||
std::memcpy(key_source, std::addressof(args.r[1]), sizeof(key_source));
|
||||
|
||||
const int generation = GetTargetFirmware() >= TargetFirmware_4_0_0 ? std::max<int>(static_cast<int>(args.r[3]) - 1, pkg1::KeyGeneration_1_0_0) : pkg1::KeyGeneration_1_0_0;
|
||||
const auto which = static_cast<SpecificAesKey>(args.r[4]);
|
||||
|
||||
/* Validate arguments. */
|
||||
SMC_R_UNLESS(pkg1::IsValidKeyGeneration(generation), InvalidArgument);
|
||||
SMC_R_UNLESS(which < SpecificAesKey_Count, InvalidArgument);
|
||||
|
||||
/* Generate the specific aes key. */
|
||||
u8 output_key[AesKeySize];
|
||||
if (fuse::GetPatchVersion() >= fuse::PatchVersion_Odnx02A2) {
|
||||
const int slot = PrepareDeviceMasterKey(generation);
|
||||
se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, slot, CalibrationKeySource, sizeof(CalibrationKeySource));
|
||||
se::DecryptAes128(output_key, sizeof(output_key), pkg1::AesKeySlot_Smc, key_source, sizeof(key_source));
|
||||
} else {
|
||||
GetSecureDataImpl(output_key, SecureData_Calibration, which == SpecificAesKey_CalibrationEncryption1);
|
||||
}
|
||||
|
||||
/* Copy the key to output. */
|
||||
std::memcpy(std::addressof(args.r[1]), output_key, sizeof(output_key));
|
||||
return SmcResult::Success;
|
||||
}
|
||||
|
||||
SmcResult ComputeCmacImpl(SmcArguments &args) {
|
||||
/* Decode arguments. */
|
||||
const int slot = args.r[1];
|
||||
const uintptr_t data_address = args.r[2];
|
||||
const size_t data_size = args.r[3];
|
||||
|
||||
/* Declare buffer for user data. */
|
||||
alignas(8) u8 user_data[CmacSizeMax];
|
||||
|
||||
/* Validate arguments. */
|
||||
SMC_R_UNLESS(pkg1::IsUserAesKeySlot(slot), InvalidArgument);
|
||||
SMC_R_UNLESS(data_size <= sizeof(user_data), InvalidArgument);
|
||||
|
||||
/* Map the user data, and copy to stack. */
|
||||
{
|
||||
UserPageMapper mapper(data_address);
|
||||
SMC_R_UNLESS(mapper.Map(), InvalidArgument);
|
||||
SMC_R_UNLESS(mapper.CopyFromUser(user_data, data_address, data_size), InvalidArgument);
|
||||
}
|
||||
|
||||
/* Ensure the SE sees consistent data. */
|
||||
hw::FlushDataCache(user_data, data_size);
|
||||
hw::DataSynchronizationBarrierInnerShareable();
|
||||
|
||||
/* Compute the mac. */
|
||||
{
|
||||
u8 mac[se::AesBlockSize];
|
||||
se::ComputeAes128Cmac(mac, sizeof(mac), slot, user_data, data_size);
|
||||
|
||||
std::memcpy(std::addressof(args.r[1]), mac, sizeof(mac));
|
||||
}
|
||||
|
||||
return SmcResult::Success;
|
||||
}
|
||||
|
||||
SmcResult LoadPreparedAesKeyImpl(SmcArguments &args) {
|
||||
/* Decode arguments. */
|
||||
u8 access_key[AesKeySize];
|
||||
|
||||
const int slot = args.r[1];
|
||||
std::memcpy(access_key, std::addressof(args.r[2]), sizeof(access_key));
|
||||
|
||||
/* Validate arguments. */
|
||||
SMC_R_UNLESS(pkg1::IsUserAesKeySlot(slot), InvalidArgument);
|
||||
|
||||
/* Derive the seal key. */
|
||||
se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_RandomForUserWrap, EsSealKeySource, sizeof(EsSealKeySource));
|
||||
|
||||
/* Unseal the key. */
|
||||
se::SetEncryptedAesKey128(slot, pkg1::AesKeySlot_Smc, access_key, sizeof(access_key));
|
||||
|
||||
return SmcResult::Success;
|
||||
}
|
||||
|
||||
SmcResult PrepareEsCommonTitleKeyImpl(SmcArguments &args) {
|
||||
/* Declare variables. */
|
||||
u8 key_source[se::AesBlockSize];
|
||||
u8 key[se::AesBlockSize];
|
||||
u8 access_key[se::AesBlockSize];
|
||||
|
||||
/* Decode arguments. */
|
||||
std::memcpy(key_source, std::addressof(args.r[1]), sizeof(key_source));
|
||||
const int generation = GetTargetFirmware() >= TargetFirmware_3_0_0 ? std::max(0, static_cast<int>(args.r[3]) - 1) : 0;
|
||||
|
||||
/* Validate arguments. */
|
||||
SMC_R_UNLESS(pkg1::IsValidKeyGeneration(generation), InvalidArgument);
|
||||
SMC_R_UNLESS(generation <= GetKeyGeneration(), InvalidArgument);
|
||||
|
||||
/* Derive the key. */
|
||||
DecryptWithEsCommonKey(key, sizeof(key), key_source, sizeof(key_source), EsCommonKeyType_TitleKey, generation);
|
||||
|
||||
/* Prepare the aes key. */
|
||||
PrepareEsAesKey(access_key, sizeof(access_key), key, sizeof(key));
|
||||
|
||||
/* Copy the access key to output. */
|
||||
std::memcpy(std::addressof(args.r[1]), access_key, sizeof(access_key));
|
||||
|
||||
return SmcResult::Success;
|
||||
}
|
||||
|
||||
SmcResult ValidateDeviceUniqueDataSize(DeviceUniqueData mode, size_t data_size) {
|
||||
switch (mode) {
|
||||
case DeviceUniqueData_DecryptDeviceUniqueData:
|
||||
{
|
||||
SMC_R_UNLESS(data_size < DeviceUniqueDataSizeMax, InvalidArgument);
|
||||
}
|
||||
break;
|
||||
case DeviceUniqueData_ImportLotusKey:
|
||||
case DeviceUniqueData_ImportEsDeviceKey:
|
||||
case DeviceUniqueData_ImportSslKey:
|
||||
case DeviceUniqueData_ImportEsClientCertKey:
|
||||
{
|
||||
SMC_R_UNLESS(DeviceUniqueDataSizeMin <= data_size && data_size <= DeviceUniqueDataSizeMax, InvalidArgument);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return SmcResult::InvalidArgument;
|
||||
}
|
||||
|
||||
return SmcResult::Success;
|
||||
}
|
||||
|
||||
SmcResult DecryptDeviceUniqueDataImpl(SmcArguments &args) {
|
||||
/* Decode arguments. */
|
||||
u8 access_key[se::AesBlockSize];
|
||||
u8 key_source[se::AesBlockSize];
|
||||
|
||||
std::memcpy(access_key, std::addressof(args.r[1]), sizeof(access_key));
|
||||
const util::BitPack32 option = { static_cast<u32>(args.r[3]) };
|
||||
const uintptr_t data_address = args.r[4];
|
||||
const size_t data_size = args.r[5];
|
||||
std::memcpy(key_source, std::addressof(args.r[6]), sizeof(key_source));
|
||||
|
||||
const auto mode = option.Get<DecryptDeviceUniqueDataOption::DeviceUniqueDataIndex>();
|
||||
const auto reserved = option.Get<DecryptDeviceUniqueDataOption::Reserved>();
|
||||
|
||||
/* Validate arguments. */
|
||||
SMC_R_UNLESS(reserved == 0, InvalidArgument);
|
||||
SMC_R_TRY(ValidateDeviceUniqueDataSize(mode, data_size));
|
||||
|
||||
/* Decrypt the device unique data. */
|
||||
alignas(8) u8 work_buffer[DeviceUniqueDataSizeMax];
|
||||
ON_SCOPE_EXIT { crypto::ClearMemory(work_buffer, sizeof(work_buffer)); };
|
||||
{
|
||||
/* Map and copy in the encrypted data. */
|
||||
UserPageMapper mapper(data_address);
|
||||
SMC_R_UNLESS(mapper.Map(), InvalidArgument);
|
||||
SMC_R_UNLESS(mapper.CopyFromUser(work_buffer, data_address, data_size), InvalidArgument);
|
||||
|
||||
/* Determine the seal key to use. */
|
||||
const auto seal_key_type = DeviceUniqueDataToSealKey[mode];
|
||||
const u8 * const seal_key_source = SealKeySources[seal_key_type];
|
||||
|
||||
/* Decrypt the data. */
|
||||
if (!DecryptDeviceUniqueData(work_buffer, data_size, nullptr, seal_key_source, se::AesBlockSize, access_key, sizeof(access_key), key_source, sizeof(key_source), work_buffer, data_size)) {
|
||||
return SmcResult::InvalidArgument;
|
||||
}
|
||||
|
||||
/* Either output the key, or import it. */
|
||||
switch (mode) {
|
||||
case DeviceUniqueData_DecryptDeviceUniqueData:
|
||||
{
|
||||
SMC_R_UNLESS(mapper.CopyToUser(data_address, work_buffer, data_size), InvalidArgument);
|
||||
}
|
||||
break;
|
||||
case DeviceUniqueData_ImportLotusKey:
|
||||
case DeviceUniqueData_ImportSslKey:
|
||||
ImportRsaKeyExponent(ConvertToImportRsaKey(mode), work_buffer, se::RsaSize);
|
||||
break;
|
||||
case DeviceUniqueData_ImportEsDeviceKey:
|
||||
case DeviceUniqueData_ImportEsClientCertKey:
|
||||
ImportRsaKeyExponent(ConvertToImportRsaKey(mode), work_buffer, se::RsaSize);
|
||||
ImportRsaKeyModulusProvisionally(ConvertToImportRsaKey(mode), work_buffer + se::RsaSize, se::RsaSize);
|
||||
CommitRsaKeyModulus(ConvertToImportRsaKey(mode));
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
}
|
||||
|
||||
return SmcResult::Success;
|
||||
}
|
||||
|
||||
SmcResult ReencryptDeviceUniqueDataImpl(SmcArguments &args) {
|
||||
/* Decode arguments. */
|
||||
u8 access_key_dec[se::AesBlockSize];
|
||||
u8 access_key_enc[se::AesBlockSize];
|
||||
u8 key_source_dec[se::AesBlockSize];
|
||||
u8 key_source_enc[se::AesBlockSize];
|
||||
|
||||
const uintptr_t access_key_dec_address = args.r[1];
|
||||
const uintptr_t access_key_enc_address = args.r[2];
|
||||
const util::BitPack32 option = { static_cast<u32>(args.r[3]) };
|
||||
const uintptr_t data_address = args.r[4];
|
||||
const size_t data_size = args.r[5];
|
||||
const uintptr_t key_source_dec_address = args.r[6];
|
||||
const uintptr_t key_source_enc_address = args.r[7];
|
||||
|
||||
const auto mode = option.Get<DecryptDeviceUniqueDataOption::DeviceUniqueDataIndex>();
|
||||
const auto reserved = option.Get<DecryptDeviceUniqueDataOption::Reserved>();
|
||||
|
||||
/* Validate arguments. */
|
||||
SMC_R_UNLESS(reserved == 0, InvalidArgument);
|
||||
SMC_R_TRY(ValidateDeviceUniqueDataSize(mode, data_size));
|
||||
|
||||
/* Decrypt the device unique data. */
|
||||
alignas(8) u8 work_buffer[DeviceUniqueDataSizeMax];
|
||||
ON_SCOPE_EXIT { crypto::ClearMemory(work_buffer, sizeof(work_buffer)); };
|
||||
{
|
||||
/* Map and copy in the encrypted data. */
|
||||
UserPageMapper mapper(data_address);
|
||||
SMC_R_UNLESS(mapper.Map(), InvalidArgument);
|
||||
SMC_R_UNLESS(mapper.CopyFromUser(work_buffer, data_address, data_size), InvalidArgument);
|
||||
SMC_R_UNLESS(mapper.CopyFromUser(access_key_dec, access_key_dec_address, sizeof(access_key_dec)), InvalidArgument);
|
||||
SMC_R_UNLESS(mapper.CopyFromUser(access_key_enc, access_key_enc_address, sizeof(access_key_enc)), InvalidArgument);
|
||||
SMC_R_UNLESS(mapper.CopyFromUser(key_source_dec, key_source_dec_address, sizeof(key_source_dec)), InvalidArgument);
|
||||
SMC_R_UNLESS(mapper.CopyFromUser(key_source_enc, key_source_enc_address, sizeof(key_source_enc)), InvalidArgument);
|
||||
|
||||
/* Decrypt the data. */
|
||||
u8 device_id_high;
|
||||
{
|
||||
/* Determine the seal key to use. */
|
||||
const u8 * const seal_key_source = SealKeySources[SealKey_ReencryptDeviceUniqueData];
|
||||
|
||||
if (!DecryptDeviceUniqueData(work_buffer, data_size, std::addressof(device_id_high), seal_key_source, se::AesBlockSize, access_key_dec, sizeof(access_key_dec), key_source_dec, sizeof(key_source_dec), work_buffer, data_size)) {
|
||||
return SmcResult::InvalidArgument;
|
||||
}
|
||||
}
|
||||
|
||||
/* Reencrypt the data. */
|
||||
{
|
||||
/* Determine the seal key to use. */
|
||||
const auto seal_key_type = DeviceUniqueDataToSealKey[mode];
|
||||
const u8 * const seal_key_source = SealKeySources[seal_key_type];
|
||||
|
||||
/* Encrypt the data. */
|
||||
EncryptDeviceUniqueData(work_buffer, data_size, seal_key_source, se::AesBlockSize, access_key_enc, sizeof(access_key_enc), key_source_enc, sizeof(key_source_enc), work_buffer, data_size - DeviceUniqueDataTotalMetaSize, device_id_high);
|
||||
}
|
||||
|
||||
/* Copy the reencrypted data back to user. */
|
||||
SMC_R_UNLESS(mapper.CopyToUser(data_address, work_buffer, data_size), InvalidArgument);
|
||||
}
|
||||
|
||||
return SmcResult::Success;
|
||||
}
|
||||
|
||||
SmcResult GetSecureDataImpl(SmcArguments &args) {
|
||||
/* Decode arguments. */
|
||||
const auto which = static_cast<SecureData>(args.r[1]);
|
||||
|
||||
/* Validate arguments/conditions. */
|
||||
SMC_R_UNLESS(fuse::GetPatchVersion() < fuse::PatchVersion_Odnx02A2, NotImplemented);
|
||||
SMC_R_UNLESS(which < SecureData_Count, NotImplemented);
|
||||
|
||||
/* Use a temporary buffer. */
|
||||
u8 secure_data[AesKeySize];
|
||||
GetSecureDataImpl(secure_data, which, false);
|
||||
|
||||
/* Copy out. */
|
||||
std::memcpy(std::addressof(args.r[1]), secure_data, sizeof(secure_data));
|
||||
return SmcResult::Success;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Aes functionality. */
|
||||
SmcResult SmcGenerateAesKek(SmcArguments &args) {
|
||||
return LockSecurityEngineAndInvoke(args, GenerateAesKekImpl);
|
||||
}
|
||||
|
||||
SmcResult SmcLoadAesKey(SmcArguments &args) {
|
||||
return LockSecurityEngineAndInvoke(args, LoadAesKeyImpl);
|
||||
}
|
||||
|
||||
SmcResult SmcComputeAes(SmcArguments &args) {
|
||||
return LockSecurityEngineAndInvokeAsync(args, ComputeAesImpl, GetComputeAesResult);
|
||||
}
|
||||
|
||||
SmcResult SmcGenerateSpecificAesKey(SmcArguments &args) {
|
||||
return LockSecurityEngineAndInvoke(args, GenerateSpecificAesKeyImpl);
|
||||
}
|
||||
|
||||
SmcResult SmcComputeCmac(SmcArguments &args) {
|
||||
return LockSecurityEngineAndInvoke(args, ComputeCmacImpl);
|
||||
}
|
||||
|
||||
SmcResult SmcLoadPreparedAesKey(SmcArguments &args) {
|
||||
return LockSecurityEngineAndInvoke(args, LoadPreparedAesKeyImpl);
|
||||
}
|
||||
|
||||
SmcResult SmcPrepareEsCommonTitleKey(SmcArguments &args) {
|
||||
return LockSecurityEngineAndInvoke(args, PrepareEsCommonTitleKeyImpl);
|
||||
}
|
||||
|
||||
/* Device unique data functionality. */
|
||||
SmcResult SmcDecryptDeviceUniqueData(SmcArguments &args) {
|
||||
return LockSecurityEngineAndInvoke(args, DecryptDeviceUniqueDataImpl);
|
||||
}
|
||||
|
||||
SmcResult SmcReencryptDeviceUniqueData(SmcArguments &args) {
|
||||
return LockSecurityEngineAndInvoke(args, ReencryptDeviceUniqueDataImpl);
|
||||
}
|
||||
|
||||
/* Legacy APIs. */
|
||||
SmcResult SmcDecryptAndImportEsDeviceKey(SmcArguments &args) {
|
||||
/* TODO */
|
||||
return SmcResult::NotImplemented;
|
||||
}
|
||||
|
||||
SmcResult SmcDecryptAndImportLotusKey(SmcArguments &args) {
|
||||
/* TODO */
|
||||
return SmcResult::NotImplemented;
|
||||
}
|
||||
|
||||
/* Es encryption utilities. */
|
||||
void DecryptWithEsCommonKey(void *dst, size_t dst_size, const void *src, size_t src_size, EsCommonKeyType type, int generation) {
|
||||
/* Validate pre-conditions. */
|
||||
AMS_ABORT_UNLESS(dst_size == AesKeySize);
|
||||
AMS_ABORT_UNLESS(src_size == AesKeySize);
|
||||
AMS_ABORT_UNLESS(0 <= type && type < EsCommonKeyType_Count);
|
||||
|
||||
/* Prepare the master key for the generation. */
|
||||
const int slot = PrepareMasterKey(generation);
|
||||
|
||||
/* Derive the es common key. */
|
||||
se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, slot, EsCommonKeySources[type], AesKeySize);
|
||||
|
||||
/* Decrypt the input using the common key. */
|
||||
se::DecryptAes128(dst, dst_size, pkg1::AesKeySlot_Smc, src, src_size);
|
||||
}
|
||||
|
||||
void PrepareEsAesKey(void *dst, size_t dst_size, const void *src, size_t src_size) {
|
||||
/* Validate pre-conditions. */
|
||||
AMS_ABORT_UNLESS(dst_size == AesKeySize);
|
||||
AMS_ABORT_UNLESS(src_size == AesKeySize);
|
||||
|
||||
/* Derive the seal key. */
|
||||
se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_RandomForUserWrap, EsSealKeySource, sizeof(EsSealKeySource));
|
||||
|
||||
/* Seal the key. */
|
||||
se::EncryptAes128(dst, dst_size, pkg1::AesKeySlot_Smc, src, src_size);
|
||||
}
|
||||
|
||||
/* 'Tis the last rose of summer, / Left blooming alone; */
|
||||
/* Oh! who would inhabit / This bleak world alone? */
|
||||
SmcResult SmcGetSecureData(SmcArguments &args) {
|
||||
return LockSecurityEngineAndInvoke(args, GetSecureDataImpl);
|
||||
}
|
||||
|
||||
}
|
53
exosphere/program/source/smc/secmon_smc_aes.hpp
Normal file
53
exosphere/program/source/smc/secmon_smc_aes.hpp
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <exosphere.hpp>
|
||||
#include "secmon_smc_common.hpp"
|
||||
|
||||
namespace ams::secmon::smc {
|
||||
|
||||
enum EsCommonKeyType {
|
||||
EsCommonKeyType_TitleKey = 0,
|
||||
EsCommonKeyType_ArchiveKey = 1,
|
||||
|
||||
EsCommonKeyType_Count,
|
||||
};
|
||||
|
||||
/* General Aes functionality. */
|
||||
SmcResult SmcGenerateAesKek(SmcArguments &args);
|
||||
SmcResult SmcLoadAesKey(SmcArguments &args);
|
||||
SmcResult SmcComputeAes(SmcArguments &args);
|
||||
SmcResult SmcGenerateSpecificAesKey(SmcArguments &args);
|
||||
SmcResult SmcComputeCmac(SmcArguments &args);
|
||||
SmcResult SmcLoadPreparedAesKey(SmcArguments &args);
|
||||
SmcResult SmcPrepareEsCommonTitleKey(SmcArguments &args);
|
||||
|
||||
/* Device unique data functionality. */
|
||||
SmcResult SmcDecryptDeviceUniqueData(SmcArguments &args);
|
||||
SmcResult SmcReencryptDeviceUniqueData(SmcArguments &args);
|
||||
|
||||
/* Legacy device unique data functionality. */
|
||||
SmcResult SmcDecryptAndImportEsDeviceKey(SmcArguments &args);
|
||||
SmcResult SmcDecryptAndImportLotusKey(SmcArguments &args);
|
||||
|
||||
/* Es encryption utilities. */
|
||||
void DecryptWithEsCommonKey(void *dst, size_t dst_size, const void *src, size_t src_size, EsCommonKeyType type, int generation);
|
||||
void PrepareEsAesKey(void *dst, size_t dst_size, const void *src, size_t src_size);
|
||||
|
||||
/* The last rose of summer. */
|
||||
SmcResult SmcGetSecureData(SmcArguments &args);
|
||||
|
||||
}
|
41
exosphere/program/source/smc/secmon_smc_carveout.cpp
Normal file
41
exosphere/program/source/smc/secmon_smc_carveout.cpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include "../secmon_error.hpp"
|
||||
#include "../secmon_setup.hpp"
|
||||
#include "secmon_smc_carveout.hpp"
|
||||
|
||||
namespace ams::secmon::smc {
|
||||
|
||||
SmcResult SmcSetKernelCarveoutRegion(SmcArguments &args) {
|
||||
/* Decode arguments. */
|
||||
const int index = args.r[1];
|
||||
const uintptr_t address = args.r[2];
|
||||
const size_t size = args.r[3];
|
||||
|
||||
/* Validate arguments. */
|
||||
SMC_R_UNLESS(0 <= index && index < KernelCarveoutCount, InvalidArgument);
|
||||
SMC_R_UNLESS(util::IsAligned(address, 128_KB), InvalidArgument);
|
||||
SMC_R_UNLESS(util::IsAligned(size, 128_KB), InvalidArgument);
|
||||
SMC_R_UNLESS(size <= CarveoutSizeMax, InvalidArgument);
|
||||
|
||||
/* Set the carveout. */
|
||||
SetKernelCarveoutRegion(index, address, size);
|
||||
|
||||
return SmcResult::Success;
|
||||
}
|
||||
|
||||
}
|
24
exosphere/program/source/smc/secmon_smc_carveout.hpp
Normal file
24
exosphere/program/source/smc/secmon_smc_carveout.hpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <exosphere.hpp>
|
||||
#include "secmon_smc_common.hpp"
|
||||
|
||||
namespace ams::secmon::smc {
|
||||
|
||||
SmcResult SmcSetKernelCarveoutRegion(SmcArguments &args);
|
||||
|
||||
}
|
51
exosphere/program/source/smc/secmon_smc_common.hpp
Normal file
51
exosphere/program/source/smc/secmon_smc_common.hpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <exosphere.hpp>
|
||||
|
||||
namespace ams::secmon::smc {
|
||||
|
||||
enum class SmcResult : u32 {
|
||||
Success = 0,
|
||||
NotImplemented = 1,
|
||||
InvalidArgument = 2,
|
||||
Busy = 3,
|
||||
NoAsyncOperation = 4,
|
||||
InvalidAsyncOperation = 5,
|
||||
NotPermitted = 6,
|
||||
NotInitialized = 7,
|
||||
|
||||
PsciSuccess = 0,
|
||||
PsciNotSupported = static_cast<u32>(-1),
|
||||
PsciInvalidParameters = static_cast<u32>(-2),
|
||||
PsciDenied = static_cast<u32>(-3),
|
||||
PsciAlreadyOn = static_cast<u32>(-4),
|
||||
};
|
||||
|
||||
#define SMC_R_SUCCEEEDED(res) (res == SmcResult::Success)
|
||||
#define SMC_R_FAILED(res) (res != SmcResult::Success)
|
||||
|
||||
#define SMC_R_TRY(res_expr) ({ const auto _tmp_r_try_rc = (res_expr); if (SMC_R_FAILED(_tmp_r_try_rc)) { return _tmp_r_try_rc; } })
|
||||
#define SMC_R_UNLESS(cond, RES) ({ if (!(cond)) { return SmcResult::RES; }})
|
||||
|
||||
struct SmcArguments {
|
||||
u64 r[8];
|
||||
};
|
||||
|
||||
constexpr inline int SecurityEngineUserInterruptId = 44;
|
||||
constexpr inline u64 InvalidAsyncKey = 0;
|
||||
|
||||
}
|
94
exosphere/program/source/smc/secmon_smc_cpu_asm.s
Normal file
94
exosphere/program/source/smc/secmon_smc_cpu_asm.s
Normal file
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* For some reason GAS doesn't know about it, even with .cpu cortex-a57 */
|
||||
#define cpuactlr_el1 s3_1_c15_c2_0
|
||||
#define cpuectlr_el1 s3_1_c15_c2_1
|
||||
|
||||
.section .text._ZN3ams6secmon3smc19PivotStackAndInvokeEPvPFvvE, "ax", %progbits
|
||||
.align 4
|
||||
.global _ZN3ams6secmon3smc19PivotStackAndInvokeEPvPFvvE
|
||||
_ZN3ams6secmon3smc19PivotStackAndInvokeEPvPFvvE:
|
||||
/* Pivot to use the provided stack pointer. */
|
||||
mov sp, x0
|
||||
|
||||
/* Release our lock on the common smc stack. */
|
||||
mov x19, x1
|
||||
bl _ZN3ams6secmon25ReleaseCommonSmcStackLockEv
|
||||
|
||||
/* Invoke the function with the new stack. */
|
||||
br x19
|
||||
|
||||
.section .text._ZN3ams6secmon3smc16FinalizePowerOffEv, "ax", %progbits
|
||||
.align 4
|
||||
.global _ZN3ams6secmon3smc16FinalizePowerOffEv
|
||||
_ZN3ams6secmon3smc16FinalizePowerOffEv:
|
||||
/* Disable all caches by clearing sctlr_el1.C. */
|
||||
mrs x0, sctlr_el1
|
||||
and x0, x0, #~(1 << 2)
|
||||
msr sctlr_el1, x0
|
||||
isb
|
||||
|
||||
/* Disable all caches by clearing sctlr_el3.C. */
|
||||
mrs x0, sctlr_el3
|
||||
and x0, x0, #~(1 << 2)
|
||||
msr sctlr_el3, x0
|
||||
isb
|
||||
|
||||
/* Disable prefetching of page table walking descriptors. */
|
||||
mrs x0, cpuectlr_el1
|
||||
orr x0, x0, #(1 << 38)
|
||||
|
||||
/* Disable prefetching of instructions. */
|
||||
and x0, x0, #~(3 << 35)
|
||||
|
||||
/* Disable prefetching of data. */
|
||||
and x0, x0, #~(3 << 32)
|
||||
msr cpuectlr_el1, x0
|
||||
isb
|
||||
|
||||
/* Ensure that all data prefetching prior to our configuration change completes. */
|
||||
dsb sy
|
||||
|
||||
/* Flush the entire data cache (local). */
|
||||
bl _ZN3ams6secmon25FlushEntireDataCacheLocalEv
|
||||
|
||||
/* Disable receiving instruction cache/TLB maintenance operations. */
|
||||
mrs x0, cpuectlr_el1
|
||||
and x0, x0, #~(1 << 6)
|
||||
msr cpuectlr_el1, x0
|
||||
|
||||
/* Configure the gic to not send interrupts to the current core. */
|
||||
ldr x1, =0x1F0043000
|
||||
mov w0, #0x1E0 /* Set FIQBypDisGrp1, IRQBypDisGrp1, reserved bits 7/8. */
|
||||
str w0, [x1]
|
||||
|
||||
/* Lock the OS Double Lock. */
|
||||
mrs x0, osdlr_el1
|
||||
orr x0, x0, #(1 << 0)
|
||||
msr osdlr_el1, x0
|
||||
|
||||
/* Ensure that our configuration takes. */
|
||||
isb
|
||||
dsb sy
|
||||
|
||||
/* Wait for interrupts, infinitely. */
|
||||
0:
|
||||
wfi
|
||||
b 0b
|
||||
|
||||
|
||||
|
201
exosphere/program/source/smc/secmon_smc_device_unique_data.cpp
Normal file
201
exosphere/program/source/smc/secmon_smc_device_unique_data.cpp
Normal file
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include "../secmon_error.hpp"
|
||||
#include "secmon_smc_device_unique_data.hpp"
|
||||
|
||||
namespace ams::secmon::smc {
|
||||
|
||||
namespace {
|
||||
|
||||
void GenerateIv(void *dst, size_t dst_size) {
|
||||
/* Flush the region we're about to fill to ensure consistency with the SE. */
|
||||
hw::FlushDataCache(dst, dst_size);
|
||||
hw::DataSynchronizationBarrierInnerShareable();
|
||||
|
||||
/* Generate random bytes. */
|
||||
se::GenerateRandomBytes(dst, dst_size);
|
||||
hw::DataSynchronizationBarrierInnerShareable();
|
||||
|
||||
/* Flush to ensure the CPU sees consistent data for the region. */
|
||||
hw::FlushDataCache(dst, dst_size);
|
||||
hw::DataSynchronizationBarrierInnerShareable();
|
||||
}
|
||||
|
||||
void PrepareDeviceUniqueDataKey(const void *seal_key_source, size_t seal_key_source_size, const void *access_key, size_t access_key_size, const void *key_source, size_t key_source_size) {
|
||||
/* Derive the seal key. */
|
||||
se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_RandomForUserWrap, seal_key_source, seal_key_source_size);
|
||||
|
||||
/* Derive the device unique data kek. */
|
||||
se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_Smc, access_key, access_key_size);
|
||||
|
||||
/* Derive the actual device unique data key. */
|
||||
se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_Smc, key_source, key_source_size);
|
||||
}
|
||||
|
||||
void ComputeAes128Ctr(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, const void *iv, size_t iv_size) {
|
||||
/* Ensure that the SE sees consistent data. */
|
||||
hw::FlushDataCache(src, src_size);
|
||||
hw::FlushDataCache(dst, dst_size);
|
||||
hw::DataSynchronizationBarrierInnerShareable();
|
||||
|
||||
/* Use the security engine to transform the data. */
|
||||
se::ComputeAes128Ctr(dst, dst_size, slot, src, src_size, iv, iv_size);
|
||||
hw::DataSynchronizationBarrierInnerShareable();
|
||||
|
||||
/* Ensure the CPU sees consistent data. */
|
||||
hw::FlushDataCache(dst, dst_size);
|
||||
hw::DataSynchronizationBarrierInnerShareable();
|
||||
}
|
||||
|
||||
void ComputeGmac(void *dst, size_t dst_size, const void *data, size_t data_size, const void *iv, size_t iv_size) {
|
||||
/* Declare keyslot (as encryptor will need to take it by pointer/reference). */
|
||||
constexpr int Slot = pkg1::AesKeySlot_Smc;
|
||||
|
||||
/* Calculate the mac. */
|
||||
crypto::Aes128GcmEncryptor gcm;
|
||||
gcm.Initialize(std::addressof(Slot), sizeof(Slot), iv, iv_size);
|
||||
gcm.UpdateAad(data, data_size);
|
||||
gcm.GetMac(dst, dst_size);
|
||||
}
|
||||
|
||||
constexpr u64 GetDeviceIdLow(u64 device_id) {
|
||||
/* Mask out the top byte. */
|
||||
constexpr u64 ByteMask = (static_cast<u64>(1) << BITSIZEOF(u8)) - 1;
|
||||
constexpr u64 LowMask = ~(ByteMask << (BITSIZEOF(u64) - BITSIZEOF(u8)));
|
||||
return device_id & LowMask;
|
||||
}
|
||||
|
||||
constexpr u8 GetDeviceIdHigh(u64 device_id) {
|
||||
/* Get the top byte. */
|
||||
return static_cast<u8>(device_id >> (BITSIZEOF(u64) - BITSIZEOF(u8)));
|
||||
}
|
||||
|
||||
constexpr u64 EncodeDeviceId(u8 device_id_high, u64 device_id_low) {
|
||||
return (static_cast<u64>(device_id_high) << (BITSIZEOF(u64) - BITSIZEOF(u8))) | device_id_low;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool DecryptDeviceUniqueData(void *dst, size_t dst_size, u8 *out_device_id_high, const void *seal_key_source, size_t seal_key_source_size, const void *access_key, size_t access_key_size, const void *key_source, size_t key_source_size, const void *src, size_t src_size) {
|
||||
/* Determine how much decrypted data there will be. */
|
||||
const size_t enc_size = src_size - DeviceUniqueDataOuterMetaSize;
|
||||
const size_t dec_size = enc_size - DeviceUniqueDataInnerMetaSize;
|
||||
|
||||
/* Ensure that our sizes are allowed. */
|
||||
AMS_ABORT_UNLESS(src_size > DeviceUniqueDataTotalMetaSize);
|
||||
AMS_ABORT_UNLESS(dst_size >= enc_size);
|
||||
|
||||
/* Determine the extents of the data. */
|
||||
const u8 * const iv = static_cast<const u8 *>(src);
|
||||
const u8 * const enc = iv + DeviceUniqueDataIvSize;
|
||||
const u8 * const mac = enc + enc_size;
|
||||
|
||||
/* Decrypt the data. */
|
||||
{
|
||||
/* Declare temporaries. */
|
||||
u8 temp_iv[DeviceUniqueDataIvSize];
|
||||
u8 calc_mac[DeviceUniqueDataMacSize];
|
||||
ON_SCOPE_EXIT { crypto::ClearMemory(temp_iv, sizeof(temp_iv)); crypto::ClearMemory(calc_mac, sizeof(calc_mac)); };
|
||||
|
||||
/* Prepare the key used to decrypt the data. */
|
||||
PrepareDeviceUniqueDataKey(seal_key_source, seal_key_source_size, access_key, access_key_size, key_source, key_source_size);
|
||||
|
||||
/* Copy the iv to stack. */
|
||||
std::memcpy(temp_iv, iv, sizeof(temp_iv));
|
||||
|
||||
/* Decrypt the data. */
|
||||
ComputeAes128Ctr(dst, dst_size, pkg1::AesKeySlot_Smc, enc, enc_size, temp_iv, DeviceUniqueDataIvSize);
|
||||
|
||||
/* Compute the gmac. */
|
||||
ComputeGmac(calc_mac, DeviceUniqueDataMacSize, dst, enc_size, temp_iv, DeviceUniqueDataIvSize);
|
||||
|
||||
/* Validate the gmac. */
|
||||
if (!crypto::IsSameBytes(mac, calc_mac, sizeof(calc_mac))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Validate device id, output device id if needed. */
|
||||
{
|
||||
/* Locate the device id in the decryption output. */
|
||||
const u8 * const padding = static_cast<const u8 *>(dst) + dec_size;
|
||||
const u8 * const device_id = padding + DeviceUniqueDataPaddingSize;
|
||||
|
||||
/* Load the big endian device id. */
|
||||
const u64 device_id_val = util::LoadBigEndian(static_cast<const u64 *>(static_cast<const void *>(device_id)));
|
||||
|
||||
/* Validate that the device id low matches the value in fuses. */
|
||||
if (GetDeviceIdLow(device_id_val) != fuse::GetDeviceId()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Set the output device id high, if needed. */
|
||||
if (out_device_id_high != nullptr) {
|
||||
*out_device_id_high = GetDeviceIdHigh(device_id_val);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EncryptDeviceUniqueData(void *dst, size_t dst_size, const void *seal_key_source, size_t seal_key_source_size, const void *access_key, size_t access_key_size, const void *key_source, size_t key_source_size, const void *src, size_t src_size, u8 device_id_high) {
|
||||
/* Determine metadata locations. */
|
||||
u8 * const dst_iv = static_cast<u8 *>(dst);
|
||||
u8 * const dst_data = dst_iv + DeviceUniqueDataIvSize;
|
||||
u8 * const dst_pad = dst_data + src_size;
|
||||
u8 * const dst_did = dst_pad + DeviceUniqueDataPaddingSize;
|
||||
u8 * const dst_mac = dst_did + DeviceUniqueDataDeviceIdSize;
|
||||
|
||||
/* Verify that our sizes are okay. */
|
||||
const size_t enc_size = src_size + DeviceUniqueDataInnerMetaSize;
|
||||
const size_t res_size = src_size + DeviceUniqueDataTotalMetaSize;
|
||||
AMS_ABORT_UNLESS(res_size <= dst_size);
|
||||
|
||||
/* Layout the image as expected. */
|
||||
{
|
||||
/* Generate a random iv. */
|
||||
util::AlignedBuffer<hw::DataCacheLineSize, DeviceUniqueDataIvSize> iv;
|
||||
GenerateIv(iv, DeviceUniqueDataIvSize);
|
||||
|
||||
/* Move the data to the output image. */
|
||||
std::memmove(dst_data, src, src_size);
|
||||
|
||||
/* Copy the iv. */
|
||||
std::memcpy(dst_iv, iv, DeviceUniqueDataIvSize);
|
||||
|
||||
/* Clear the padding. */
|
||||
std::memset(dst_pad, 0, DeviceUniqueDataPaddingSize);
|
||||
|
||||
/* Store the device id. */
|
||||
util::StoreBigEndian(reinterpret_cast<u64 *>(dst_did), EncodeDeviceId(device_id_high, fuse::GetDeviceId()));
|
||||
}
|
||||
|
||||
/* Encrypt and mac. */
|
||||
{
|
||||
|
||||
/* Prepare the key used to encrypt the data. */
|
||||
PrepareDeviceUniqueDataKey(seal_key_source, seal_key_source_size, access_key, access_key_size, key_source, key_source_size);
|
||||
|
||||
/* Compute the gmac. */
|
||||
ComputeGmac(dst_mac, DeviceUniqueDataMacSize, dst_data, enc_size, dst_iv, DeviceUniqueDataIvSize);
|
||||
|
||||
/* Encrypt the data. */
|
||||
ComputeAes128Ctr(dst_data, enc_size, pkg1::AesKeySlot_Smc, dst_data, enc_size, dst_iv, DeviceUniqueDataIvSize);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <exosphere.hpp>
|
||||
#include "secmon_smc_common.hpp"
|
||||
|
||||
namespace ams::secmon::smc {
|
||||
|
||||
constexpr inline size_t DeviceUniqueDataIvSize = se::AesBlockSize;
|
||||
constexpr inline size_t DeviceUniqueDataMacSize = se::AesBlockSize;
|
||||
constexpr inline size_t DeviceUniqueDataDeviceIdSize = sizeof(u64);
|
||||
constexpr inline size_t DeviceUniqueDataPaddingSize = se::AesBlockSize - DeviceUniqueDataDeviceIdSize;
|
||||
|
||||
constexpr inline size_t DeviceUniqueDataOuterMetaSize = DeviceUniqueDataIvSize + DeviceUniqueDataMacSize;
|
||||
constexpr inline size_t DeviceUniqueDataInnerMetaSize = DeviceUniqueDataPaddingSize + DeviceUniqueDataDeviceIdSize;
|
||||
constexpr inline size_t DeviceUniqueDataTotalMetaSize = DeviceUniqueDataOuterMetaSize + DeviceUniqueDataInnerMetaSize;
|
||||
|
||||
bool DecryptDeviceUniqueData(void *dst, size_t dst_size, u8 *out_device_id_high, const void *seal_key_source, size_t seal_key_source_size, const void *access_key, size_t access_key_size, const void *key_source, size_t key_source_size, const void *src, size_t src_size);
|
||||
void EncryptDeviceUniqueData(void *dst, size_t dst_size, const void *seal_key_source, size_t seal_key_source_size, const void *access_key, size_t access_key_size, const void *key_source, size_t key_source_size, const void *src, size_t src_size, u8 device_id_high);
|
||||
|
||||
}
|
27
exosphere/program/source/smc/secmon_smc_error.cpp
Normal file
27
exosphere/program/source/smc/secmon_smc_error.cpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include "../secmon_error.hpp"
|
||||
#include "secmon_smc_error.hpp"
|
||||
|
||||
namespace ams::secmon::smc {
|
||||
|
||||
SmcResult SmcShowError(SmcArguments &args) {
|
||||
/* TODO */
|
||||
return SmcResult::NotImplemented;
|
||||
}
|
||||
|
||||
}
|
24
exosphere/program/source/smc/secmon_smc_error.hpp
Normal file
24
exosphere/program/source/smc/secmon_smc_error.hpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <exosphere.hpp>
|
||||
#include "secmon_smc_common.hpp"
|
||||
|
||||
namespace ams::secmon::smc {
|
||||
|
||||
SmcResult SmcShowError(SmcArguments &args);
|
||||
|
||||
}
|
314
exosphere/program/source/smc/secmon_smc_handler.cpp
Normal file
314
exosphere/program/source/smc/secmon_smc_handler.cpp
Normal file
|
@ -0,0 +1,314 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include "../secmon_error.hpp"
|
||||
#include "../secmon_misc.hpp"
|
||||
#include "secmon_smc_common.hpp"
|
||||
#include "secmon_smc_handler.hpp"
|
||||
#include "secmon_smc_aes.hpp"
|
||||
#include "secmon_smc_carveout.hpp"
|
||||
#include "secmon_smc_device_unique_data.hpp"
|
||||
#include "secmon_smc_error.hpp"
|
||||
#include "secmon_smc_info.hpp"
|
||||
#include "secmon_smc_memory_access.hpp"
|
||||
#include "secmon_smc_power_management.hpp"
|
||||
#include "secmon_smc_random.hpp"
|
||||
#include "secmon_smc_register_access.hpp"
|
||||
#include "secmon_smc_result.hpp"
|
||||
#include "secmon_smc_rsa.hpp"
|
||||
|
||||
namespace ams::secmon::smc {
|
||||
|
||||
namespace {
|
||||
|
||||
struct HandlerInfo {
|
||||
u32 function_id;
|
||||
u32 restriction_mask;
|
||||
SmcHandler handler;
|
||||
};
|
||||
|
||||
struct HandlerTable {
|
||||
const HandlerInfo *entries;
|
||||
size_t count;
|
||||
};
|
||||
|
||||
enum HandlerType : int {
|
||||
HandlerType_User = 0,
|
||||
HandlerType_Kern = 1,
|
||||
HandlerType_Count = 2,
|
||||
};
|
||||
|
||||
enum Restriction {
|
||||
Restriction_None = (0 << 0),
|
||||
Restriction_Normal = (1 << 0),
|
||||
Restriction_DeviceUniqueDataNotAllowed = (1 << 1),
|
||||
Restriction_SafeModeNotAllowed = (1 << 2),
|
||||
};
|
||||
|
||||
enum SmcCallRange {
|
||||
SmcCallRange_ArmArch = 0,
|
||||
SmcCallRange_Cpu = 1,
|
||||
SmcCallRange_Sip = 2,
|
||||
SmcCallRange_Oem = 3,
|
||||
SmcCallRange_Standard = 4,
|
||||
|
||||
SmcCallRange_TrustedApp = 0x30,
|
||||
};
|
||||
|
||||
enum SmcArgumentType {
|
||||
ArgumentType_Integer = 0,
|
||||
ArgumentType_Pointer = 1,
|
||||
};
|
||||
|
||||
enum SmcConvention {
|
||||
Convention_Smc32 = 0,
|
||||
Convention_Smc64 = 1,
|
||||
};
|
||||
|
||||
enum SmcCallType {
|
||||
SmcCallType_YieldingCall = 0,
|
||||
SmcCallType_FastCall = 1,
|
||||
};
|
||||
|
||||
struct SmcFunctionId {
|
||||
using FunctionId = util::BitPack64::Field< 0, 8, u32>;
|
||||
using ArgumentType0 = util::BitPack64::Field< 8, 1, SmcArgumentType>;
|
||||
using ArgumentType1 = util::BitPack64::Field< 9, 1, SmcArgumentType>;
|
||||
using ArgumentType2 = util::BitPack64::Field<10, 1, SmcArgumentType>;
|
||||
using ArgumentType3 = util::BitPack64::Field<11, 1, SmcArgumentType>;
|
||||
using ArgumentType4 = util::BitPack64::Field<12, 1, SmcArgumentType>;
|
||||
using ArgumentType5 = util::BitPack64::Field<13, 1, SmcArgumentType>;
|
||||
using ArgumentType6 = util::BitPack64::Field<14, 1, SmcArgumentType>;
|
||||
using ArgumentType7 = util::BitPack64::Field<15, 1, SmcArgumentType>;
|
||||
using Reserved = util::BitPack64::Field<16, 8, u32>;
|
||||
using CallRange = util::BitPack64::Field<24, 6, SmcCallRange>;
|
||||
using Convention = util::BitPack64::Field<30, 1, SmcConvention>;
|
||||
using CallType = util::BitPack64::Field<31, 1, SmcCallType>;
|
||||
using Reserved2 = util::BitPack64::Field<32, 32, u32>;
|
||||
};
|
||||
|
||||
constinit HandlerInfo g_user_handlers[] = {
|
||||
{ 0x00000000, Restriction_SafeModeNotAllowed, nullptr },
|
||||
{ 0xC3000401, Restriction_SafeModeNotAllowed, SmcSetConfig },
|
||||
{ 0xC3000002, Restriction_Normal, SmcGetConfigUser },
|
||||
{ 0xC3000003, Restriction_Normal, SmcGetResult },
|
||||
{ 0xC3000404, Restriction_Normal, SmcGetResultData },
|
||||
{ 0xC3000E05, Restriction_SafeModeNotAllowed, SmcModularExponentiate },
|
||||
{ 0xC3000006, Restriction_Normal, SmcGenerateRandomBytes },
|
||||
{ 0xC3000007, Restriction_Normal, SmcGenerateAesKek },
|
||||
{ 0xC3000008, Restriction_Normal, SmcLoadAesKey },
|
||||
{ 0xC3000009, Restriction_Normal, SmcComputeAes },
|
||||
{ 0xC300000A, Restriction_Normal, SmcGenerateSpecificAesKey },
|
||||
{ 0xC300040B, Restriction_Normal, SmcComputeCmac },
|
||||
{ 0xC300D60C, Restriction_Normal, SmcReencryptDeviceUniqueData },
|
||||
{ 0xC300100D, Restriction_DeviceUniqueDataNotAllowed, SmcDecryptDeviceUniqueData },
|
||||
{ 0xC300000E, Restriction_SafeModeNotAllowed, nullptr },
|
||||
{ 0xC300060F, Restriction_DeviceUniqueDataNotAllowed, SmcModularExponentiateByStorageKey },
|
||||
{ 0xC3000610, Restriction_SafeModeNotAllowed, SmcPrepareEsDeviceUniqueKey },
|
||||
{ 0xC3000011, Restriction_SafeModeNotAllowed, SmcLoadPreparedAesKey },
|
||||
{ 0xC3000012, Restriction_SafeModeNotAllowed, SmcPrepareEsCommonTitleKey }
|
||||
};
|
||||
|
||||
constinit HandlerInfo g_kern_handlers[] = {
|
||||
{ 0x00000000, Restriction_SafeModeNotAllowed, nullptr },
|
||||
{ 0xC4000001, Restriction_SafeModeNotAllowed, SmcSuspendCpu },
|
||||
{ 0x84000002, Restriction_SafeModeNotAllowed, SmcPowerOffCpu },
|
||||
{ 0xC4000003, Restriction_SafeModeNotAllowed, SmcPowerOnCpu },
|
||||
{ 0xC3000004, Restriction_Normal, SmcGetConfigKern },
|
||||
{ 0xC3000005, Restriction_Normal, SmcGenerateRandomBytesNonBlocking },
|
||||
{ 0xC3000006, Restriction_Normal, SmcShowError },
|
||||
{ 0xC3000007, Restriction_Normal, SmcSetKernelCarveoutRegion },
|
||||
{ 0xC3000008, Restriction_Normal, SmcReadWriteRegister },
|
||||
};
|
||||
|
||||
constinit HandlerInfo g_ams_handlers[] = {
|
||||
{ 0x00000000, Restriction_SafeModeNotAllowed, nullptr },
|
||||
{ 0xF0000201, Restriction_None, SmcIramCopy },
|
||||
{ 0xF0000002, Restriction_None, SmcReadWriteRegister },
|
||||
{ 0xF0000003, Restriction_None, SmcWriteAddress },
|
||||
{ 0xF0000404, Restriction_None, SmcGetEmummcConfig },
|
||||
};
|
||||
|
||||
constexpr const HandlerInfo GetSecureDataHandlerInfo = {
|
||||
0x67891234, Restriction_None, SmcGetSecureData
|
||||
};
|
||||
|
||||
constinit HandlerTable g_handler_tables[] = {
|
||||
{ g_user_handlers, util::size(g_user_handlers) },
|
||||
{ g_kern_handlers, util::size(g_kern_handlers) },
|
||||
};
|
||||
|
||||
constinit HandlerTable g_ams_handler_table = {
|
||||
g_ams_handlers, util::size(g_ams_handlers)
|
||||
};
|
||||
|
||||
NORETURN void InvalidSmcError(u64 id) {
|
||||
SetError(pkg1::ErrorInfo_UnknownSmc);
|
||||
AMS_ABORT("Invalid SMC: %lx", id);
|
||||
}
|
||||
|
||||
const HandlerTable &GetHandlerTable(HandlerType type, u64 id) {
|
||||
/* Ensure we have a valid handler type. */
|
||||
if (AMS_UNLIKELY(!(0 <= type && type < HandlerType_Count))) {
|
||||
InvalidSmcError(id);
|
||||
}
|
||||
|
||||
/* Provide support for legacy SmcGetSecureData. */
|
||||
if (id == GetSecureDataHandlerInfo.function_id) {
|
||||
return g_handler_tables[HandlerType_User];
|
||||
}
|
||||
|
||||
/* Check if we're a user SMC. */
|
||||
if (type == HandlerType_User) {
|
||||
/* Nintendo uses OEM SMCs. */
|
||||
/* We will assign Atmosphere extension SMCs the TrustedApplication range. */
|
||||
if (util::BitPack64{id}.Get<SmcFunctionId::CallRange>() == SmcCallRange_TrustedApp) {
|
||||
return g_ams_handler_table;
|
||||
}
|
||||
|
||||
/* If we're not performing an atmosphere extension smc, require that we're being invoked by spl on core 3. */
|
||||
if (AMS_UNLIKELY(hw::GetCurrentCoreId() != 3)) {
|
||||
InvalidSmcError(id);
|
||||
}
|
||||
}
|
||||
|
||||
return g_handler_tables[type];
|
||||
}
|
||||
|
||||
const HandlerInfo &GetHandlerInfo(const HandlerTable &table, u64 id) {
|
||||
/* Provide support for legacy SmcGetSecureData. */
|
||||
if (id == GetSecureDataHandlerInfo.function_id) {
|
||||
return GetSecureDataHandlerInfo;
|
||||
}
|
||||
|
||||
/* Get and check the index. */
|
||||
const auto index = util::BitPack64{id}.Get<SmcFunctionId::FunctionId>();
|
||||
if (AMS_UNLIKELY(index >= table.count)) {
|
||||
InvalidSmcError(id);
|
||||
}
|
||||
|
||||
/* Get and check the handler info. */
|
||||
const auto &handler_info = table.entries[index];
|
||||
|
||||
/* Check that the handler isn't null. */
|
||||
if (AMS_UNLIKELY(handler_info.handler == nullptr)) {
|
||||
InvalidSmcError(id);
|
||||
}
|
||||
|
||||
/* Check that the handler's id matches. */
|
||||
if (AMS_UNLIKELY(handler_info.function_id != id)) {
|
||||
InvalidSmcError(id);
|
||||
}
|
||||
|
||||
return handler_info;
|
||||
}
|
||||
|
||||
bool IsHandlerRestricted(const HandlerInfo &info) {
|
||||
return (info.restriction_mask & secmon::GetRestrictedSmcMask()) != 0;
|
||||
}
|
||||
|
||||
SmcResult InvokeSmcHandler(const HandlerInfo &info, SmcArguments &args) {
|
||||
/* Check if the smc is restricted. */
|
||||
if (AMS_UNLIKELY(IsHandlerRestricted(info))) {
|
||||
return SmcResult::NotPermitted;
|
||||
}
|
||||
|
||||
/* Invoke the smc. */
|
||||
return info.handler(args);
|
||||
}
|
||||
|
||||
constinit std::atomic<int> g_logged = 0;
|
||||
|
||||
constexpr int LogMin = 0x1000000;
|
||||
constexpr int LogMax = 0x1000000;
|
||||
|
||||
constexpr size_t LogBufSize = 0x5000;
|
||||
|
||||
void DebugLog(SmcArguments &args) {
|
||||
const int current = g_logged.fetch_add(1);
|
||||
|
||||
if (current == 0) {
|
||||
std::memset(MemoryRegionVirtualDebug.GetPointer<void>(), 0xCC, LogBufSize);
|
||||
}
|
||||
|
||||
if (current < LogMin) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int ind = current - LogMin;
|
||||
const int ofs = (ind * sizeof(args)) % LogBufSize;
|
||||
|
||||
for (size_t i = 0; i < sizeof(args) / sizeof(u32); ++i) {
|
||||
((volatile u32 *)(MemoryRegionVirtualDebug.GetAddress() + ofs))[i] = reinterpret_cast<u32 *>(std::addressof(args))[i];
|
||||
}
|
||||
|
||||
if (current >= LogMax) {
|
||||
*(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x50) = 0x02;
|
||||
*(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x00) = 0x10;
|
||||
|
||||
util::WaitMicroSeconds(1000);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void HandleSmc(int type, SmcArguments &args) {
|
||||
/* Get the table. */
|
||||
const auto &table = GetHandlerTable(static_cast<HandlerType>(type), args.r[0]);
|
||||
|
||||
if (std::addressof(table) == std::addressof(g_handler_tables[HandlerType_User])) {
|
||||
DebugLog(args);
|
||||
}
|
||||
|
||||
/* Get the handler info. */
|
||||
const auto &info = GetHandlerInfo(table, args.r[0]);
|
||||
|
||||
/* Set the invocation result. */
|
||||
args.r[0] = static_cast<u64>(InvokeSmcHandler(info, args));
|
||||
|
||||
if (std::addressof(table) == std::addressof(g_handler_tables[HandlerType_User])) {
|
||||
DebugLog(args);
|
||||
}
|
||||
|
||||
/* TODO: For debugging. Remove this when exo2 is complete. */
|
||||
#if 1
|
||||
if (args.r[0] == static_cast<u64>(SmcResult::NotImplemented)) {
|
||||
*(volatile u32 *)(MemoryRegionVirtualDebug.GetAddress()) = 0xBBBBBBBB;
|
||||
*(volatile u32 *)(MemoryRegionVirtualDebug.GetAddress() + 0x10) = static_cast<u32>(info.function_id);
|
||||
for (size_t i = 0; i < sizeof(args) / sizeof(u32); ++i) {
|
||||
((volatile u32 *)(MemoryRegionVirtualDebug.GetAddress() + 0x20))[i] = reinterpret_cast<u32 *>(std::addressof(args))[i];
|
||||
}
|
||||
*(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x50) = 0x02;
|
||||
*(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x00) = 0x10;
|
||||
|
||||
util::WaitMicroSeconds(1000);
|
||||
}
|
||||
if (args.r[0] != static_cast<u64>(SmcResult::Success) && info.function_id != 0xC3000007 /* generate aes key fails during SetupKekAccessKeys */) {
|
||||
*(volatile u32 *)(MemoryRegionVirtualDebug.GetAddress()) = 0xCCCCCCCC;
|
||||
*(volatile u32 *)(MemoryRegionVirtualDebug.GetAddress() + 0x10) = static_cast<u32>(info.function_id);
|
||||
for (size_t i = 0; i < sizeof(args) / sizeof(u32); ++i) {
|
||||
((volatile u32 *)(MemoryRegionVirtualDebug.GetAddress() + 0x20))[i] = reinterpret_cast<u32 *>(std::addressof(args))[i];
|
||||
}
|
||||
*(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x50) = 0x02;
|
||||
*(volatile u32 *)(MemoryRegionVirtualDevicePmc.GetAddress() + 0x00) = 0x10;
|
||||
|
||||
util::WaitMicroSeconds(1000);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
24
exosphere/program/source/smc/secmon_smc_handler.hpp
Normal file
24
exosphere/program/source/smc/secmon_smc_handler.hpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <exosphere.hpp>
|
||||
#include "secmon_smc_common.hpp"
|
||||
|
||||
namespace ams::secmon::smc {
|
||||
|
||||
using SmcHandler = SmcResult (*)(SmcArguments &args);
|
||||
|
||||
}
|
391
exosphere/program/source/smc/secmon_smc_info.cpp
Normal file
391
exosphere/program/source/smc/secmon_smc_info.cpp
Normal file
|
@ -0,0 +1,391 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include "../secmon_error.hpp"
|
||||
#include "../secmon_misc.hpp"
|
||||
#include "../secmon_page_mapper.hpp"
|
||||
#include "../secmon_user_power_management.hpp"
|
||||
#include "secmon_smc_info.hpp"
|
||||
#include "secmon_smc_power_management.hpp"
|
||||
|
||||
namespace ams::secmon::smc {
|
||||
|
||||
namespace {
|
||||
|
||||
struct KernelConfiguration {
|
||||
/* Secure Monitor view. */
|
||||
using Flags1 = util::BitPack32::Field< 0, 8>;
|
||||
using Flags0 = util::BitPack32::Field< 8, 8>;
|
||||
using PhysicalMemorySize = util::BitPack32::Field<16, 2>;
|
||||
|
||||
/* Kernel view, from libmesosphere. */
|
||||
using DebugFillMemory = util::BitPack32::Field<0, 1, bool>;
|
||||
using EnableUserExceptionHandlers = util::BitPack32::Field<DebugFillMemory::Next, 1, bool>;
|
||||
using EnableUserPmuAccess = util::BitPack32::Field<EnableUserExceptionHandlers::Next, 1, bool>;
|
||||
using IncreaseThreadResourceLimit = util::BitPack32::Field<EnableUserPmuAccess::Next, 1, bool>;
|
||||
using Reserved4 = util::BitPack32::Field<IncreaseThreadResourceLimit::Next, 4, u32>;
|
||||
using UseSecureMonitorPanicCall = util::BitPack32::Field<Reserved4::Next, 1, bool>;
|
||||
using Reserved9 = util::BitPack32::Field<UseSecureMonitorPanicCall::Next, 7, u32>;
|
||||
using MemorySize = util::BitPack32::Field<Reserved9::Next, 2, u32>; /* smc::MemorySize = pkg1::MemorySize */
|
||||
};
|
||||
|
||||
constexpr const pkg1::MemorySize DramIdToMemorySize[fuse::DramId_Count] = {
|
||||
[fuse::DramId_IcosaSamsung4GB] = pkg1::MemorySize_4GB,
|
||||
[fuse::DramId_IcosaHynix4GB] = pkg1::MemorySize_4GB,
|
||||
[fuse::DramId_IcosaMicron4GB] = pkg1::MemorySize_4GB,
|
||||
[fuse::DramId_CopperSamsung4GB] = pkg1::MemorySize_4GB,
|
||||
[fuse::DramId_IcosaSamsung6GB] = pkg1::MemorySize_6GB,
|
||||
[fuse::DramId_CopperHynix4GB] = pkg1::MemorySize_4GB,
|
||||
[fuse::DramId_CopperMicron4GB] = pkg1::MemorySize_4GB,
|
||||
[fuse::DramId_IowaX1X2Samsung4GB] = pkg1::MemorySize_4GB,
|
||||
[fuse::DramId_IowaSansung4GB] = pkg1::MemorySize_4GB,
|
||||
[fuse::DramId_IowaSamsung8GB] = pkg1::MemorySize_8GB,
|
||||
[fuse::DramId_IowaHynix4GB] = pkg1::MemorySize_4GB,
|
||||
[fuse::DramId_IowaMicron4GB] = pkg1::MemorySize_4GB,
|
||||
[fuse::DramId_HoagSamsung4GB] = pkg1::MemorySize_4GB,
|
||||
[fuse::DramId_HoagSamsung8GB] = pkg1::MemorySize_8GB,
|
||||
[fuse::DramId_HoagHynix4GB] = pkg1::MemorySize_4GB,
|
||||
[fuse::DramId_HoagMicron4GB] = pkg1::MemorySize_4GB,
|
||||
[fuse::DramId_IowaSamsung4GBY] = pkg1::MemorySize_4GB,
|
||||
[fuse::DramId_IowaSamsung1y4GBX] = pkg1::MemorySize_4GB,
|
||||
[fuse::DramId_IowaSamsung1y8GBX] = pkg1::MemorySize_8GB,
|
||||
[fuse::DramId_HoagSamsung1y4GBX] = pkg1::MemorySize_4GB,
|
||||
[fuse::DramId_IowaSamsung1y4GBY] = pkg1::MemorySize_4GB,
|
||||
[fuse::DramId_IowaSamsung1y8GBY] = pkg1::MemorySize_8GB,
|
||||
[fuse::DramId_IowaSamsung1y4GBA] = pkg1::MemorySize_4GB,
|
||||
[fuse::DramId_FiveSamsung1y8GBX] = pkg1::MemorySize_8GB,
|
||||
[fuse::DramId_FiveSamsung1y4GBX] = pkg1::MemorySize_4GB,
|
||||
};
|
||||
|
||||
constexpr const pkg1::MemoryMode MemoryModes[] = {
|
||||
pkg1::MemoryMode_Auto,
|
||||
|
||||
pkg1::MemoryMode_4GB,
|
||||
pkg1::MemoryMode_4GBAppletDev,
|
||||
pkg1::MemoryMode_4GBSystemDev,
|
||||
|
||||
pkg1::MemoryMode_6GB,
|
||||
pkg1::MemoryMode_6GBAppletDev,
|
||||
|
||||
pkg1::MemoryMode_8GB,
|
||||
};
|
||||
|
||||
constexpr bool IsValidMemoryMode(pkg1::MemoryMode mode) {
|
||||
for (const auto known_mode : MemoryModes) {
|
||||
if (mode == known_mode) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
pkg1::MemoryMode SanitizeMemoryMode(pkg1::MemoryMode mode) {
|
||||
if (IsValidMemoryMode(mode)) {
|
||||
return mode;
|
||||
}
|
||||
return pkg1::MemoryMode_Auto;
|
||||
}
|
||||
|
||||
pkg1::MemorySize GetAvailableMemorySize(pkg1::MemorySize size) {
|
||||
return std::min(GetPhysicalMemorySize(), size);
|
||||
}
|
||||
|
||||
pkg1::MemoryMode GetMemoryMode(pkg1::MemoryMode mode) {
|
||||
/* Sanitize the mode. */
|
||||
mode = SanitizeMemoryMode(mode);
|
||||
|
||||
/* If the mode is auto, construct the memory mode. */
|
||||
if (mode == pkg1::MemoryMode_Auto) {
|
||||
return pkg1::MakeMemoryMode(GetPhysicalMemorySize(), pkg1::MemoryArrange_Normal);
|
||||
} else {
|
||||
const auto mode_size = GetMemorySize(mode);
|
||||
const auto mode_arrange = GetMemoryArrange(mode);
|
||||
const auto size = GetAvailableMemorySize(mode_size);
|
||||
const auto arrange = (size == mode_size) ? mode_arrange : pkg1::MemoryArrange_Normal;
|
||||
return pkg1::MakeMemoryMode(size, arrange);
|
||||
}
|
||||
}
|
||||
|
||||
u32 GetMemoryMode() {
|
||||
/* Unless development function is enabled, we're 4 GB. */
|
||||
u32 memory_mode = pkg1::MemoryMode_4GB;
|
||||
|
||||
if (const auto &bcd = GetBootConfig().data; bcd.IsDevelopmentFunctionEnabled()) {
|
||||
memory_mode = GetMemoryMode(bcd.GetMemoryMode());
|
||||
}
|
||||
|
||||
return memory_mode;
|
||||
}
|
||||
|
||||
u32 GetKernelConfiguration() {
|
||||
pkg1::MemorySize memory_size = pkg1::MemorySize_4GB;
|
||||
util::BitPack32 value = {};
|
||||
|
||||
if (const auto &bcd = GetBootConfig().data; bcd.IsDevelopmentFunctionEnabled()) {
|
||||
memory_size = GetMemorySize(GetMemoryMode(bcd.GetMemoryMode()));
|
||||
|
||||
value.Set<KernelConfiguration::Flags1>(bcd.GetKernelFlags1());
|
||||
value.Set<KernelConfiguration::Flags0>(bcd.GetKernelFlags0());
|
||||
}
|
||||
|
||||
value.Set<KernelConfiguration::PhysicalMemorySize>(memory_size);
|
||||
|
||||
/* Exosphere extensions. */
|
||||
const auto &sc = GetSecmonConfiguration();
|
||||
|
||||
if (!sc.DisableUserModeExceptionHandlers()) {
|
||||
value.Set<KernelConfiguration::EnableUserExceptionHandlers>(true);
|
||||
}
|
||||
|
||||
if (sc.EnableUserModePerformanceCounterAccess()) {
|
||||
value.Set<KernelConfiguration::EnableUserPmuAccess>(true);
|
||||
}
|
||||
|
||||
return value.value;
|
||||
}
|
||||
|
||||
SmcResult GetConfig(SmcArguments &args, bool kern) {
|
||||
switch (static_cast<ConfigItem>(args.r[1])) {
|
||||
case ConfigItem::DisableProgramVerification:
|
||||
args.r[1] = GetBootConfig().signed_data.IsProgramVerificationDisabled();
|
||||
break;
|
||||
case ConfigItem::DramId:
|
||||
args.r[1] = fuse::GetDramId();
|
||||
break;
|
||||
case ConfigItem::SecurityEngineInterruptNumber:
|
||||
args.r[1] = SecurityEngineUserInterruptId;
|
||||
break;
|
||||
case ConfigItem::FuseVersion:
|
||||
args.r[1] = fuse::GetExpectedFuseVersion(GetTargetFirmware());
|
||||
break;
|
||||
case ConfigItem::HardwareType:
|
||||
args.r[1] = fuse::GetHardwareType();
|
||||
break;
|
||||
case ConfigItem::HardwareState:
|
||||
args.r[1] = fuse::GetHardwareState();
|
||||
break;
|
||||
case ConfigItem::IsRecoveryBoot:
|
||||
args.r[1] = IsRecoveryBoot();
|
||||
break;
|
||||
case ConfigItem::DeviceId:
|
||||
args.r[1] = fuse::GetDeviceId();
|
||||
break;
|
||||
case ConfigItem::BootReason:
|
||||
{
|
||||
/* This was removed in firmware 4.0.0. */
|
||||
if (GetTargetFirmware() >= TargetFirmware_4_0_0) {
|
||||
return SmcResult::InvalidArgument;
|
||||
}
|
||||
|
||||
args.r[1] = GetDeprecatedBootReason();
|
||||
}
|
||||
break;
|
||||
case ConfigItem::MemoryMode:
|
||||
args.r[1] = GetMemoryMode();
|
||||
break;
|
||||
case ConfigItem::IsDevelopmentFunctionEnabled:
|
||||
args.r[1] = GetSecmonConfiguration().IsDevelopmentFunctionEnabled(kern) || GetBootConfig().data.IsDevelopmentFunctionEnabled();
|
||||
break;
|
||||
case ConfigItem::KernelConfiguration:
|
||||
args.r[1] = GetKernelConfiguration();
|
||||
break;
|
||||
case ConfigItem::IsChargerHiZModeEnabled:
|
||||
args.r[1] = IsChargerHiZModeEnabled();
|
||||
break;
|
||||
case ConfigItem::QuestState:
|
||||
args.r[1] = fuse::GetQuestState();
|
||||
break;
|
||||
case ConfigItem::RegulatorType:
|
||||
args.r[1] = fuse::GetRegulator();
|
||||
break;
|
||||
case ConfigItem::DeviceUniqueKeyGeneration:
|
||||
args.r[1] = fuse::GetDeviceUniqueKeyGeneration();
|
||||
break;
|
||||
case ConfigItem::Package2Hash:
|
||||
{
|
||||
/* Only allow getting the package2 hash in recovery boot. */
|
||||
if (!IsRecoveryBoot()) {
|
||||
return SmcResult::InvalidArgument;
|
||||
}
|
||||
|
||||
/* Get the hash. */
|
||||
se::Sha256Hash tmp_hash;
|
||||
GetPackage2Hash(std::addressof(tmp_hash));
|
||||
|
||||
/* Copy it out. */
|
||||
static_assert(sizeof(args) - sizeof(args.r[0]) >= sizeof(tmp_hash));
|
||||
std::memcpy(std::addressof(args.r[1]), std::addressof(tmp_hash), sizeof(tmp_hash));
|
||||
}
|
||||
break;
|
||||
case ConfigItem::ExosphereApiVersion:
|
||||
/* Get information about the current exosphere version. */
|
||||
args.r[1] = (static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MAJOR & 0xFF) << 56) |
|
||||
(static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MINOR & 0xFF) << 48) |
|
||||
(static_cast<u64>(ATMOSPHERE_RELEASE_VERSION_MICRO & 0xFF) << 40) |
|
||||
(static_cast<u64>(GetKeyGeneration()) << 32) |
|
||||
(static_cast<u64>(GetTargetFirmware()) << 00);
|
||||
break;
|
||||
case ConfigItem::ExosphereNeedsReboot:
|
||||
/* We are executing, so we aren't in the process of rebooting. */
|
||||
args.r[1] = 0;
|
||||
break;
|
||||
case ConfigItem::ExosphereNeedsShutdown:
|
||||
/* We are executing, so we aren't in the process of shutting down. */
|
||||
args.r[1] = 0;
|
||||
break;
|
||||
case ConfigItem::ExosphereGitCommitHash:
|
||||
/* Get information about the current exosphere git commit hash. */
|
||||
args.r[1] = ATMOSPHERE_GIT_HASH;
|
||||
break;
|
||||
case ConfigItem::ExosphereHasRcmBugPatch:
|
||||
/* Get information about whether this unit has the RCM bug patched. */
|
||||
args.r[1] = fuse::HasRcmVulnerabilityPatch();
|
||||
break;
|
||||
case ConfigItem::ExosphereBlankProdInfo:
|
||||
/* Get whether this unit should simulate a "blanked" PRODINFO. */
|
||||
args.r[1] = GetSecmonConfiguration().ShouldUseBlankCalibrationBinary();
|
||||
break;
|
||||
case ConfigItem::ExosphereAllowCalWrites:
|
||||
/* Get whether this unit should allow writing to the calibration partition. */
|
||||
args.r[1] = (GetEmummcConfiguration().IsEmummcActive() || GetSecmonConfiguration().AllowWritingToCalibrationBinarySysmmc());
|
||||
break;
|
||||
default:
|
||||
return SmcResult::InvalidArgument;
|
||||
}
|
||||
|
||||
return SmcResult::Success;
|
||||
}
|
||||
|
||||
SmcResult SetConfig(SmcArguments &args) {
|
||||
const auto soc_type = GetSocType();
|
||||
|
||||
switch (static_cast<ConfigItem>(args.r[1])) {
|
||||
case ConfigItem::IsChargerHiZModeEnabled:
|
||||
/* Configure the HiZ mode. */
|
||||
SetChargerHiZModeEnabled(static_cast<bool>(args.r[3]));
|
||||
break;
|
||||
case ConfigItem::ExosphereNeedsReboot:
|
||||
if (soc_type == fuse::SocType_Erista) {
|
||||
switch (static_cast<UserRebootType>(args.r[3])) {
|
||||
case UserRebootType_None:
|
||||
break;
|
||||
case UserRebootType_ToRcm:
|
||||
PerformUserRebootToRcm();
|
||||
break;
|
||||
case UserRebootType_ToPayload:
|
||||
PerformUserRebootToPayload();
|
||||
break;
|
||||
default:
|
||||
return SmcResult::InvalidArgument;
|
||||
}
|
||||
} else /* if (soc_type == fuse::SocType_Mariko) */ {
|
||||
return SmcResult::NotImplemented;
|
||||
}
|
||||
break;
|
||||
case ConfigItem::ExosphereNeedsShutdown:
|
||||
if (soc_type == fuse::SocType_Erista) {
|
||||
if (args.r[3] != 0) {
|
||||
PerformUserShutDown();
|
||||
}
|
||||
} else /* if (soc_type == fuse::SocType_Mariko) */ {
|
||||
return SmcResult::NotImplemented;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return SmcResult::InvalidArgument;
|
||||
}
|
||||
|
||||
return SmcResult::Success;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SmcResult SmcGetConfigUser(SmcArguments &args) {
|
||||
return GetConfig(args, false);
|
||||
}
|
||||
|
||||
SmcResult SmcGetConfigKern(SmcArguments &args) {
|
||||
return GetConfig(args, true);
|
||||
}
|
||||
|
||||
SmcResult SmcSetConfig(SmcArguments &args) {
|
||||
return SetConfig(args);
|
||||
}
|
||||
|
||||
/* This is an atmosphere extension smc. */
|
||||
SmcResult SmcGetEmummcConfig(SmcArguments &args) {
|
||||
/* Decode arguments. */
|
||||
const auto mmc = static_cast<EmummcMmc>(args.r[1]);
|
||||
const uintptr_t user_address = args.r[2];
|
||||
const uintptr_t user_offset = user_address % 4_KB;
|
||||
|
||||
/* Validate arguments. */
|
||||
/* NOTE: In the future, configuration for non-NAND storage may be implemented. */
|
||||
SMC_R_UNLESS(mmc == EmummcMmc_Nand, NotImplemented);
|
||||
SMC_R_UNLESS(user_offset + 2 * sizeof(EmummcFilePath) <= 4_KB, InvalidArgument);
|
||||
|
||||
/* Get the emummc config. */
|
||||
const auto &cfg = GetEmummcConfiguration();
|
||||
static_assert(sizeof(cfg.file_cfg) == sizeof(EmummcFilePath));
|
||||
static_assert(sizeof(cfg.emu_dir_path) == sizeof(EmummcFilePath));
|
||||
|
||||
/* Clear the output. */
|
||||
constexpr size_t InlineOutputSize = sizeof(args) - sizeof(args.r[0]);
|
||||
u8 * const inline_output = static_cast<u8 *>(static_cast<void *>(std::addressof(args.r[1])));
|
||||
std::memset(inline_output, 0, InlineOutputSize);
|
||||
|
||||
/* Copy out the configuration. */
|
||||
{
|
||||
/* Map the user output page. */
|
||||
AtmosphereUserPageMapper mapper(user_address);
|
||||
SMC_R_UNLESS(mapper.Map(), InvalidArgument);
|
||||
|
||||
/* Copy the base configuration. */
|
||||
static_assert(sizeof(cfg.base_cfg) <= InlineOutputSize);
|
||||
std::memcpy(inline_output, std::addressof(cfg.base_cfg), sizeof(cfg.base_cfg));
|
||||
|
||||
/* Copy out type-specific data. */
|
||||
switch (cfg.base_cfg.type) {
|
||||
case EmummcType_None:
|
||||
/* No additional configuration needs to be copied. */
|
||||
break;
|
||||
case EmummcType_Partition:
|
||||
/* Copy the partition config. */
|
||||
static_assert(sizeof(cfg.base_cfg) + sizeof(cfg.partition_cfg) <= InlineOutputSize);
|
||||
std::memcpy(inline_output + sizeof(cfg.base_cfg), std::addressof(cfg.partition_cfg), sizeof(cfg.partition_cfg));
|
||||
break;
|
||||
case EmummcType_File:
|
||||
/* Copy the file config. */
|
||||
SMC_R_UNLESS(mapper.CopyToUser(user_address, std::addressof(cfg.file_cfg), sizeof(cfg.file_cfg)), InvalidArgument);
|
||||
break;
|
||||
AMS_UNREACHABLE_DEFAULT_CASE();
|
||||
}
|
||||
|
||||
/* Copy the redirection directory path to the user page. */
|
||||
SMC_R_UNLESS(mapper.CopyToUser(user_address + sizeof(EmummcFilePath), std::addressof(cfg.emu_dir_path), sizeof(cfg.emu_dir_path)), InvalidArgument);
|
||||
}
|
||||
|
||||
return SmcResult::Success;
|
||||
}
|
||||
|
||||
/* For exosphere's usage. */
|
||||
pkg1::MemorySize GetPhysicalMemorySize() {
|
||||
const auto dram_id = fuse::GetDramId();
|
||||
AMS_ABORT_UNLESS(dram_id < fuse::DramId_Count);
|
||||
return DramIdToMemorySize[dram_id];
|
||||
}
|
||||
|
||||
}
|
62
exosphere/program/source/smc/secmon_smc_info.hpp
Normal file
62
exosphere/program/source/smc/secmon_smc_info.hpp
Normal file
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <exosphere.hpp>
|
||||
#include "secmon_smc_common.hpp"
|
||||
|
||||
namespace ams::secmon::smc {
|
||||
|
||||
enum class ConfigItem : u32 {
|
||||
/* Standard config items. */
|
||||
DisableProgramVerification = 1,
|
||||
DramId = 2,
|
||||
SecurityEngineInterruptNumber = 3,
|
||||
FuseVersion = 4,
|
||||
HardwareType = 5,
|
||||
HardwareState = 6,
|
||||
IsRecoveryBoot = 7,
|
||||
DeviceId = 8,
|
||||
BootReason = 9,
|
||||
MemoryMode = 10,
|
||||
IsDevelopmentFunctionEnabled = 11,
|
||||
KernelConfiguration = 12,
|
||||
IsChargerHiZModeEnabled = 13,
|
||||
QuestState = 14,
|
||||
RegulatorType = 15,
|
||||
DeviceUniqueKeyGeneration = 16,
|
||||
Package2Hash = 17,
|
||||
|
||||
/* Extension config items for exosphere. */
|
||||
ExosphereApiVersion = 65000,
|
||||
ExosphereNeedsReboot = 65001,
|
||||
ExosphereNeedsShutdown = 65002,
|
||||
ExosphereGitCommitHash = 65003,
|
||||
ExosphereHasRcmBugPatch = 65004,
|
||||
ExosphereBlankProdInfo = 65005,
|
||||
ExosphereAllowCalWrites = 65006,
|
||||
};
|
||||
|
||||
SmcResult SmcGetConfigUser(SmcArguments &args);
|
||||
SmcResult SmcGetConfigKern(SmcArguments &args);
|
||||
SmcResult SmcSetConfig(SmcArguments &args);
|
||||
|
||||
/* This is an atmosphere extension smc. */
|
||||
SmcResult SmcGetEmummcConfig(SmcArguments &args);
|
||||
|
||||
/* For other parts of exosphere. */
|
||||
pkg1::MemorySize GetPhysicalMemorySize();
|
||||
|
||||
}
|
75
exosphere/program/source/smc/secmon_smc_memory_access.cpp
Normal file
75
exosphere/program/source/smc/secmon_smc_memory_access.cpp
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include "../secmon_error.hpp"
|
||||
#include "../secmon_page_mapper.hpp"
|
||||
#include "secmon_smc_memory_access.hpp"
|
||||
|
||||
namespace ams::secmon::smc {
|
||||
|
||||
namespace {
|
||||
|
||||
enum IramCopyType {
|
||||
IramCopyType_FromIramToDram = 0,
|
||||
IramCopyType_FromDramToIram = 1,
|
||||
IramCopyType_Count,
|
||||
};
|
||||
|
||||
struct IramCopyOption {
|
||||
using CopyType = util::BitPack32::Field<0, 1, IramCopyType>;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/* This is an atmosphere extension smc. */
|
||||
SmcResult SmcIramCopy(SmcArguments &args) {
|
||||
/* Decode arguments. */
|
||||
const uintptr_t dram_address = args.r[1];
|
||||
const uintptr_t iram_address = args.r[2];
|
||||
const size_t size = args.r[3];
|
||||
const util::BitPack32 option = { static_cast<u32>(args.r[4]) };
|
||||
|
||||
const auto copy_type = option.Get<IramCopyOption::CopyType>();
|
||||
|
||||
/* Validate arguments. */
|
||||
SMC_R_UNLESS(copy_type < IramCopyType_Count, InvalidArgument);
|
||||
|
||||
{
|
||||
/* Map the pages. */
|
||||
AtmosphereUserPageMapper dram_mapper(dram_address);
|
||||
AtmosphereIramPageMapper iram_mapper(iram_address);
|
||||
SMC_R_UNLESS(dram_mapper.Map(), InvalidArgument);
|
||||
SMC_R_UNLESS(iram_mapper.Map(), InvalidArgument);
|
||||
|
||||
/* Get the ranges we're copying. */
|
||||
const void * const src = (copy_type == IramCopyType_FromIramToDram) ? iram_mapper.GetPointerTo(iram_address, size) : dram_mapper.GetPointerTo(dram_address, size);
|
||||
void * const dst = (copy_type == IramCopyType_FromIramToDram) ? dram_mapper.GetPointerTo(dram_address, size) : iram_mapper.GetPointerTo(iram_address, size);
|
||||
SMC_R_UNLESS(src != nullptr, InvalidArgument);
|
||||
SMC_R_UNLESS(dst != nullptr, InvalidArgument);
|
||||
|
||||
/* Copy the data. */
|
||||
std::memcpy(dst, src, size);
|
||||
}
|
||||
|
||||
return SmcResult::Success;
|
||||
}
|
||||
|
||||
SmcResult SmcWriteAddress(SmcArguments &args) {
|
||||
/* NOTE: This smc was deprecated in Atmosphère 0.13.0. */
|
||||
return SmcResult::NotImplemented;
|
||||
}
|
||||
|
||||
}
|
26
exosphere/program/source/smc/secmon_smc_memory_access.hpp
Normal file
26
exosphere/program/source/smc/secmon_smc_memory_access.hpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <exosphere.hpp>
|
||||
#include "secmon_smc_common.hpp"
|
||||
|
||||
namespace ams::secmon::smc {
|
||||
|
||||
/* This is an atmosphere extension smc. */
|
||||
SmcResult SmcIramCopy(SmcArguments &args);
|
||||
SmcResult SmcWriteAddress(SmcArguments &args);
|
||||
|
||||
}
|
501
exosphere/program/source/smc/secmon_smc_power_management.cpp
Normal file
501
exosphere/program/source/smc/secmon_smc_power_management.cpp
Normal file
|
@ -0,0 +1,501 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include "../secmon_cache.hpp"
|
||||
#include "../secmon_cpu_context.hpp"
|
||||
#include "../secmon_error.hpp"
|
||||
#include "../secmon_misc.hpp"
|
||||
#include "secmon_smc_power_management.hpp"
|
||||
#include "secmon_smc_se_lock.hpp"
|
||||
|
||||
#include "sc7fw_bin.h"
|
||||
|
||||
namespace ams::secmon {
|
||||
|
||||
/* Declare assembly functionality. */
|
||||
void *GetCoreExceptionStackVirtual();
|
||||
|
||||
}
|
||||
|
||||
namespace ams::secmon::smc {
|
||||
|
||||
/* Declare assembly power-management functionality. */
|
||||
void PivotStackAndInvoke(void *stack, void (*function)());
|
||||
void FinalizePowerOff();
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr inline const uintptr_t PMC = MemoryRegionVirtualDevicePmc.GetAddress();
|
||||
constexpr inline const uintptr_t APB_MISC = MemoryRegionVirtualDeviceApbMisc.GetAddress();
|
||||
constexpr inline const uintptr_t GPIO = MemoryRegionVirtualDeviceGpio.GetAddress();
|
||||
constexpr inline const uintptr_t CLK_RST = MemoryRegionVirtualDeviceClkRst.GetAddress();
|
||||
constexpr inline const uintptr_t EVP = secmon::MemoryRegionVirtualDeviceExceptionVectors.GetAddress();
|
||||
constexpr inline const uintptr_t FLOW_CTLR = MemoryRegionVirtualDeviceFlowController.GetAddress();
|
||||
|
||||
constexpr inline uintptr_t CommonSmcStackTop = MemoryRegionVirtualTzramVolatileData.GetEndAddress() - (0x80 * (NumCores - 1));
|
||||
|
||||
enum PowerStateType {
|
||||
PowerStateType_StandBy = 0,
|
||||
PowerStateType_PowerDown = 1,
|
||||
};
|
||||
|
||||
enum PowerStateId {
|
||||
PowerStateId_Sc7 = 27,
|
||||
};
|
||||
|
||||
/* http://infocenter.arm.com/help/topic/com.arm.doc.den0022d/Power_State_Coordination_Interface_PDD_v1_1_DEN0022D.pdf Page 46 */
|
||||
struct SuspendCpuPowerState {
|
||||
using StateId = util::BitPack32::Field< 0, 16, PowerStateId>;
|
||||
using StateType = util::BitPack32::Field<16, 1, PowerStateType>;
|
||||
using PowerLevel = util::BitPack32::Field<24, 2, u32>;
|
||||
};
|
||||
|
||||
constinit bool g_charger_hi_z_mode_enabled = false;
|
||||
|
||||
constinit const reg::BitsMask CpuPowerGateStatusMasks[NumCores] = {
|
||||
PMC_REG_BITS_MASK(PWRGATE_STATUS_CE0),
|
||||
PMC_REG_BITS_MASK(PWRGATE_STATUS_CE1),
|
||||
PMC_REG_BITS_MASK(PWRGATE_STATUS_CE2),
|
||||
PMC_REG_BITS_MASK(PWRGATE_STATUS_CE3),
|
||||
};
|
||||
|
||||
constinit const APBDEV_PMC_PWRGATE_TOGGLE_PARTID CpuPowerGateTogglePartitionIds[NumCores] = {
|
||||
APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE0,
|
||||
APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE1,
|
||||
APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE2,
|
||||
APBDEV_PMC_PWRGATE_TOGGLE_PARTID_CE3,
|
||||
};
|
||||
|
||||
bool IsCpuPoweredOn(const reg::BitsMask mask) {
|
||||
return reg::HasValue(PMC + APBDEV_PMC_PWRGATE_STATUS, REG_BITS_VALUE_FROM_MASK(mask, APBDEV_PMC_PWRGATE_STATUS_STATUS_ON));
|
||||
}
|
||||
|
||||
void PowerOnCpu(const reg::BitsMask mask, u32 toggle_partid) {
|
||||
/* If the cpu is already on, we have nothing to do. */
|
||||
if (IsCpuPoweredOn(mask)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Wait until nothing is being powergated. */
|
||||
int timeout = 5000;
|
||||
while (true) {
|
||||
if (reg::HasValue(PMC + APBDEV_PMC_PWRGATE_TOGGLE, PMC_REG_BITS_ENUM(PWRGATE_TOGGLE_START, DISABLE))) {
|
||||
break;
|
||||
}
|
||||
|
||||
util::WaitMicroSeconds(1);
|
||||
if ((--timeout) < 0) {
|
||||
/* NOTE: Nintendo doesn't do any error handling here... */
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Toggle on the cpu partition. */
|
||||
reg::Write(PMC + APBDEV_PMC_PWRGATE_TOGGLE, PMC_REG_BITS_ENUM (PWRGATE_TOGGLE_START, ENABLE),
|
||||
PMC_REG_BITS_VALUE(PWRGATE_TOGGLE_PARTID, toggle_partid));
|
||||
|
||||
/* Wait up to 5000 us for the powergate to complete. */
|
||||
timeout = 5000;
|
||||
while (true) {
|
||||
if (IsCpuPoweredOn(mask)) {
|
||||
break;
|
||||
}
|
||||
|
||||
util::WaitMicroSeconds(1);
|
||||
if ((--timeout) < 0) {
|
||||
/* NOTE: Nintendo doesn't do any error handling here... */
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ResetCpu(int which_core) {
|
||||
reg::Write(CLK_RST + CLK_RST_CONTROLLER_RST_CPUG_CMPLX_SET, REG_BITS_VALUE(which_core + 0x00, 1, 1), /* CPURESETn */
|
||||
REG_BITS_VALUE(which_core + 0x10, 1, 1)); /* CORERESETn */
|
||||
}
|
||||
|
||||
void StartCpu(int which_core) {
|
||||
reg::Write(CLK_RST + CLK_RST_CONTROLLER_RST_CPUG_CMPLX_CLR, REG_BITS_VALUE(which_core + 0x00, 1, 1), /* CPURESETn */
|
||||
REG_BITS_VALUE(which_core + 0x10, 1, 1)); /* CORERESETn */
|
||||
}
|
||||
|
||||
void PowerOffCpu() {
|
||||
/* Get the current core id. */
|
||||
const auto core_id = hw::GetCurrentCoreId();
|
||||
|
||||
/* Configure the flow controller to prepare for shutting down the current core. */
|
||||
flow::SetCpuCsr(core_id, FLOW_CTLR_CPUN_CSR_ENABLE_EXT_POWERGATE_CPU_ONLY);
|
||||
flow::SetHaltCpuEvents(core_id, false);
|
||||
flow::SetCc4Ctrl(core_id, 0);
|
||||
|
||||
/* Save the core's context for restoration on next power-on. */
|
||||
SaveDebugRegisters();
|
||||
SetCoreOff();
|
||||
|
||||
/* Ensure there are no pending memory transactions prior to our power-down. */
|
||||
FlushEntireDataCache();
|
||||
|
||||
/* Finalize our powerdown and wait for an interrupt. */
|
||||
FinalizePowerOff();
|
||||
}
|
||||
|
||||
void ValidateSocStateForSuspend() {
|
||||
/* Validate that all other cores are off. */
|
||||
AMS_ABORT_UNLESS(reg::HasValue(PMC + APBDEV_PMC_PWRGATE_STATUS, PMC_REG_BITS_VALUE(PWRGATE_STATUS_CE123, 0)));
|
||||
|
||||
/* Validate that the bpmp is appropriately halted. */
|
||||
AMS_ABORT_UNLESS(reg::Read(FLOW_CTLR + FLOW_CTLR_HALT_COP_EVENTS) == reg::Encode(FLOW_REG_BITS_ENUM (HALT_COP_EVENTS_MODE, FLOW_MODE_STOP),
|
||||
FLOW_REG_BITS_ENUM_SEL(HALT_COP_EVENTS_JTAG, IsJtagEnabled(), ENABLED, DISABLED)));
|
||||
|
||||
/* TODO */
|
||||
}
|
||||
|
||||
void GenerateCryptographicallyRandomBytes(void * const dst, int size) {
|
||||
/* Flush the region we're about to fill to ensure consistency with the SE. */
|
||||
hw::FlushDataCache(dst, size);
|
||||
hw::DataSynchronizationBarrierInnerShareable();
|
||||
|
||||
/* Generate random bytes. */
|
||||
se::GenerateRandomBytes(dst, size);
|
||||
hw::DataSynchronizationBarrierInnerShareable();
|
||||
|
||||
/* Flush to ensure the CPU sees consistent data for the region. */
|
||||
hw::FlushDataCache(dst, size);
|
||||
hw::DataSynchronizationBarrierInnerShareable();
|
||||
}
|
||||
|
||||
void SaveSecureContextForErista() {
|
||||
/* Generate a random key source. */
|
||||
util::AlignedBuffer<hw::DataCacheLineSize, se::AesBlockSize> key_source;
|
||||
GenerateCryptographicallyRandomBytes(key_source, se::AesBlockSize);
|
||||
|
||||
const u32 * const key_source_32 = reinterpret_cast<const u32 *>(static_cast<u8 *>(key_source));
|
||||
|
||||
/* Ensure that the key source registers are not locked. */
|
||||
AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_KeySourceReadWrite) != pmc::LockState::Locked);
|
||||
|
||||
/* Write the key source, lock writes to the key source, and verify that the key source is write-locked. */
|
||||
reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH24, key_source_32[0]);
|
||||
reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH25, key_source_32[1]);
|
||||
reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH26, key_source_32[2]);
|
||||
reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH27, key_source_32[3]);
|
||||
pmc::LockSecureRegister(pmc::SecureRegister_KeySourceWrite);
|
||||
AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_KeySourceWrite) == pmc::LockState::Locked);
|
||||
|
||||
/* Verify the key source is correct in registers, and read-lock the key source registers. */
|
||||
AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH24) == key_source_32[0]);
|
||||
AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH25) == key_source_32[1]);
|
||||
AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH26) == key_source_32[2]);
|
||||
AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH27) == key_source_32[3]);
|
||||
pmc::LockSecureRegister(pmc::SecureRegister_KeySourceRead);
|
||||
|
||||
/* Ensure that the key source registers are locked. */
|
||||
AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_KeySourceReadWrite) == pmc::LockState::Locked);
|
||||
|
||||
/* Generate a random kek into keyslot 2. */
|
||||
se::SetRandomKey(pkg1::AesKeySlot_TzramSaveKek);
|
||||
|
||||
/* Verify that the se is in a validate state, context save, and validate again. */
|
||||
{
|
||||
se::ValidateErrStatus();
|
||||
ON_SCOPE_EXIT { se::ValidateErrStatus(); };
|
||||
|
||||
{
|
||||
/* Transition to non-secure mode for the duration of the context save operation. */
|
||||
se::SetSecure(false);
|
||||
ON_SCOPE_EXIT { se::SetSecure(true); };
|
||||
|
||||
/* Get a pointer to the context storage. */
|
||||
se::Context * const context = MemoryRegionVirtualDramSecureDataStoreSecurityEngineState.GetPointer<se::Context>();
|
||||
static_assert(MemoryRegionVirtualDramSecureDataStoreSecurityEngineState.GetSize() == sizeof(*context));
|
||||
|
||||
/* Save the context. */
|
||||
se::SaveContext(context);
|
||||
|
||||
/* Ensure that the cpu sees consistent data. */
|
||||
hw::FlushDataCache(context, sizeof(*context));
|
||||
hw::DataSynchronizationBarrierInnerShareable();
|
||||
|
||||
/* Write the context pointer to pmc scratch, so that the bootrom will restore it on wake. */
|
||||
reg::Write(PMC + APBDEV_PMC_SCRATCH43, MemoryRegionPhysicalDramSecureDataStoreSecurityEngineState.GetAddress());
|
||||
}
|
||||
}
|
||||
|
||||
/* Clear keyslot 3, and then derive the save key. */
|
||||
se::ClearAesKeySlot(pkg1::AesKeySlot_TzramSaveKey);
|
||||
se::SetEncryptedAesKey256(pkg1::AesKeySlot_TzramSaveKey, pkg1::AesKeySlot_TzramSaveKek, key_source, se::AesBlockSize);
|
||||
|
||||
/* Declare a temporary block to be used as both iv and mac. */
|
||||
u32 temp_block[se::AesBlockSize / sizeof(u32)] = {};
|
||||
|
||||
/* Ensure that the SE sees consistent data for tzram. */
|
||||
const void * const tzram_save_src = MemoryRegionVirtualTzramReadOnlyAlias.GetPointer<u8>() + MemoryRegionVirtualTzramVolatileData.GetSize() + MemoryRegionVirtualTzramVolatileStack.GetSize();
|
||||
void * const tzram_save_dst = MemoryRegionVirtualIramSc7Work.GetPointer<void>();
|
||||
constexpr size_t TzramSaveSize = MemoryRegionVirtualDramSecureDataStoreTzram.GetSize();
|
||||
|
||||
hw::FlushDataCache(tzram_save_src, TzramSaveSize);
|
||||
hw::FlushDataCache(tzram_save_dst, TzramSaveSize);
|
||||
hw::DataSynchronizationBarrierInnerShareable();
|
||||
|
||||
/* Encrypt tzram using our random key. */
|
||||
se::EncryptAes256Cbc(tzram_save_dst, TzramSaveSize, pkg1::AesKeySlot_TzramSaveKey, tzram_save_src, TzramSaveSize, temp_block, se::AesBlockSize);
|
||||
hw::FlushDataCache(tzram_save_dst, TzramSaveSize);
|
||||
hw::DataSynchronizationBarrierInnerShareable();
|
||||
|
||||
/* Copy the data from work space to the secure storage destination. */
|
||||
void * const tzram_store_dst = MemoryRegionVirtualDramSecureDataStoreTzram.GetPointer<void>();
|
||||
std::memcpy(tzram_store_dst, tzram_save_dst, TzramSaveSize);
|
||||
hw::FlushDataCache(tzram_store_dst, TzramSaveSize);
|
||||
hw::DataSynchronizationBarrierInnerShareable();
|
||||
|
||||
/* Compute cmac of tzram into our temporary block. */
|
||||
se::ComputeAes256Cmac(temp_block, se::AesBlockSize, pkg1::AesKeySlot_TzramSaveKey, tzram_save_src, TzramSaveSize);
|
||||
|
||||
/* Ensure that the cmac registers are not locked. */
|
||||
AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_CmacReadWrite) != pmc::LockState::Locked);
|
||||
|
||||
/* Write the cmac, lock writes to the cmac, and verify that the cmac is write-locked. */
|
||||
reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH112, temp_block[0]);
|
||||
reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH113, temp_block[1]);
|
||||
reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH114, temp_block[2]);
|
||||
reg::Write(PMC + APBDEV_PMC_SECURE_SCRATCH115, temp_block[3]);
|
||||
pmc::LockSecureRegister(pmc::SecureRegister_CmacWrite);
|
||||
AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_CmacWrite) == pmc::LockState::Locked);
|
||||
|
||||
/* Verify the key source is correct in registers, and read-lock the key source registers. */
|
||||
AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH112) == temp_block[0]);
|
||||
AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH113) == temp_block[1]);
|
||||
AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH114) == temp_block[2]);
|
||||
AMS_ABORT_UNLESS(reg::Read(PMC + APBDEV_PMC_SECURE_SCRATCH115) == temp_block[3]);
|
||||
pmc::LockSecureRegister(pmc::SecureRegister_CmacRead);
|
||||
|
||||
/* Ensure that the key source registers are locked. */
|
||||
AMS_ABORT_UNLESS(pmc::GetSecureRegisterLockState(pmc::SecureRegister_CmacReadWrite) == pmc::LockState::Locked);
|
||||
}
|
||||
|
||||
void SaveSecureContextForMariko() {
|
||||
/* TODO */
|
||||
}
|
||||
|
||||
void SaveSecureContext() {
|
||||
const auto soc_type = GetSocType();
|
||||
if (soc_type == fuse::SocType_Erista) {
|
||||
SaveSecureContextForErista();
|
||||
} else /* if (soc_type == fuse::SocType_Mariko) */ {
|
||||
SaveSecureContextForMariko();
|
||||
}
|
||||
}
|
||||
|
||||
void LoadAndStartSc7BpmpFirmware() {
|
||||
/* Set the PMC as insecure, so that the BPMP firmware can access it. */
|
||||
reg::ReadWrite(APB_MISC + APB_MISC_SECURE_REGS_APB_SLAVE_SECURITY_ENABLE_REG0_0, SLAVE_SECURITY_REG_BITS_ENUM(0, PMC, DISABLE));
|
||||
|
||||
/* Set the exception vectors for the bpmp. RESET should point to RESET, all others should point to generic exception/panic. */
|
||||
constexpr const u32 Sc7FirmwareResetVector = static_cast<u32>(MemoryRegionPhysicalIramSc7Firmware.GetAddress() + 0x0);
|
||||
constexpr const u32 Sc7FirmwarePanicVector = static_cast<u32>(MemoryRegionPhysicalIramSc7Firmware.GetAddress() + 0x4);
|
||||
|
||||
reg::Write(EVP + EVP_COP_RESET_VECTOR, Sc7FirmwareResetVector);
|
||||
reg::Write(EVP + EVP_COP_UNDEF_VECTOR, Sc7FirmwarePanicVector);
|
||||
reg::Write(EVP + EVP_COP_SWI_VECTOR, Sc7FirmwarePanicVector);
|
||||
reg::Write(EVP + EVP_COP_PREFETCH_ABORT_VECTOR, Sc7FirmwarePanicVector);
|
||||
reg::Write(EVP + EVP_COP_DATA_ABORT_VECTOR, Sc7FirmwarePanicVector);
|
||||
reg::Write(EVP + EVP_COP_RSVD_VECTOR, Sc7FirmwarePanicVector);
|
||||
reg::Write(EVP + EVP_COP_IRQ_VECTOR, Sc7FirmwarePanicVector);
|
||||
reg::Write(EVP + EVP_COP_FIQ_VECTOR, Sc7FirmwarePanicVector);
|
||||
|
||||
/* Disable activity monitor bpmp monitoring, so that we don't panic upon bpmp wake. */
|
||||
actmon::StopMonitoringBpmp();
|
||||
|
||||
/* Set BPMP reset. */
|
||||
reg::Write(CLK_RST + CLK_RST_CONTROLLER_RST_DEV_L_SET, CLK_RST_REG_BITS_ENUM(RST_DEV_L_SET_SET_COP_RST, ENABLE));
|
||||
|
||||
/* Load the bpmp firmware. */
|
||||
void * const sc7fw_load_address = MemoryRegionVirtualIramSc7Firmware.GetPointer<void>();
|
||||
std::memcpy(sc7fw_load_address, sc7fw_bin, sc7fw_bin_size);
|
||||
hw::FlushDataCache(sc7fw_load_address, sc7fw_bin_size);
|
||||
hw::DataSynchronizationBarrierInnerShareable();
|
||||
|
||||
/* Ensure that the bpmp firmware was loaded. */
|
||||
AMS_ABORT_UNLESS(crypto::IsSameBytes(sc7fw_load_address, sc7fw_bin, sc7fw_bin_size));
|
||||
|
||||
/* Clear BPMP reset. */
|
||||
reg::Write(CLK_RST + CLK_RST_CONTROLLER_RST_DEV_L_CLR, CLK_RST_REG_BITS_ENUM(RST_DEV_L_CLR_CLR_COP_RST, ENABLE));
|
||||
|
||||
/* Start the bpmp. */
|
||||
reg::Write(FLOW_CTLR + FLOW_CTLR_HALT_COP_EVENTS, FLOW_REG_BITS_ENUM(HALT_COP_EVENTS_MODE, FLOW_MODE_NONE));
|
||||
}
|
||||
|
||||
void SaveSecureContextAndSuspend() {
|
||||
/* Ensure there are no pending memory transactions before we continue */
|
||||
FlushEntireDataCache();
|
||||
hw::DataSynchronizationBarrierInnerShareable();
|
||||
|
||||
/* Save all secure context (security engine state + tzram). */
|
||||
SaveSecureContext();
|
||||
|
||||
/* Load and start the sc7 firmware on the bpmp. */
|
||||
LoadAndStartSc7BpmpFirmware();
|
||||
|
||||
/* Log our suspension. */
|
||||
/* NOTE: Nintendo only does this on dev, but we will always do it. */
|
||||
if (true /* !pkg1::IsProduction() */) {
|
||||
log::SendText("OYASUMI\n", 8);
|
||||
}
|
||||
|
||||
/* If we're on erista, configure the bootrom to allow our custom warmboot firmware. */
|
||||
if (GetSocType() == fuse::SocType_Erista) {
|
||||
reg::Write(PMC + APBDEV_PMC_SCRATCH31, 0x2202E012);
|
||||
reg::Write(PMC + APBDEV_PMC_SCRATCH32, 0x6001DC28);
|
||||
}
|
||||
|
||||
/* Finalize our powerdown and wait for an interrupt. */
|
||||
FinalizePowerOff();
|
||||
}
|
||||
|
||||
SmcResult SuspendCpuImpl(SmcArguments &args) {
|
||||
/* Decode arguments. */
|
||||
const util::BitPack32 power_state = { static_cast<u32>(args.r[1]) };
|
||||
const uintptr_t entry_point = args.r[2];
|
||||
const uintptr_t context_id = args.r[3];
|
||||
|
||||
const auto state_type = power_state.Get<SuspendCpuPowerState::StateType>();
|
||||
const auto state_id = power_state.Get<SuspendCpuPowerState::StateId>();
|
||||
|
||||
const auto core_id = hw::GetCurrentCoreId();
|
||||
|
||||
/* Validate arguments. */
|
||||
SMC_R_UNLESS(state_type == PowerStateType_PowerDown, PsciDenied);
|
||||
SMC_R_UNLESS(state_id == PowerStateId_Sc7, PsciDenied);
|
||||
|
||||
/* Orchestrate charger transition to Hi-Z mode if needed. */
|
||||
if (IsChargerHiZModeEnabled()) {
|
||||
/* Ensure we can do comms over i2c-1. */
|
||||
clkrst::EnableI2c1Clock();
|
||||
|
||||
/* If the charger isn't in hi-z mode, perform a transition. */
|
||||
if (!charger::IsHiZMode()) {
|
||||
charger::EnterHiZMode();
|
||||
|
||||
/* Wait up to 50ms for the transition to complete. */
|
||||
const auto start_time = util::GetMicroSeconds();
|
||||
auto current_time = start_time;
|
||||
while ((current_time - start_time) <= 50'000) {
|
||||
if (auto intr_status = reg::Read(GPIO + 0x634); (intr_status & 1) == 0) {
|
||||
/* Wait 256 us to ensure the transition completes. */
|
||||
util::WaitMicroSeconds(256);
|
||||
break;
|
||||
}
|
||||
current_time = util::GetMicroSeconds();
|
||||
}
|
||||
}
|
||||
|
||||
/* Disable i2c-1, since we're done communicating over it. */
|
||||
clkrst::DisableI2c1Clock();
|
||||
}
|
||||
|
||||
/* Enable wake event detection. */
|
||||
pmc::EnableWakeEventDetection();
|
||||
|
||||
/* Ensure that i2c-5 is usable for communicating with the pmic. */
|
||||
clkrst::EnableI2c5Clock();
|
||||
i2c::Initialize(i2c::Port_5);
|
||||
|
||||
/* Orchestrate sleep entry with the pmic. */
|
||||
pmic::EnableSleep();
|
||||
|
||||
/* Ensure that the soc is in a state valid for us to suspend. */
|
||||
ValidateSocStateForSuspend();
|
||||
|
||||
/* Configure the pmc for sc7 entry. */
|
||||
pmc::ConfigureForSc7Entry();
|
||||
|
||||
/* Configure the flow controller for sc7 entry. */
|
||||
flow::SetCc4Ctrl(core_id, 0);
|
||||
flow::SetHaltCpuEvents(core_id, false);
|
||||
flow::ClearL2FlushControl();
|
||||
flow::SetCpuCsr(core_id, FLOW_CTLR_CPUN_CSR_ENABLE_EXT_POWERGATE_CPU_TURNOFF_CPURAIL);
|
||||
|
||||
/* Save the entry context. */
|
||||
SetEntryContext(core_id, entry_point, context_id);
|
||||
|
||||
/* Configure the cpu context for reset. */
|
||||
SaveDebugRegisters();
|
||||
SetCoreOff();
|
||||
SetResetExpected(true);
|
||||
|
||||
/* Switch to use the common smc stack (all other cores are off), and perform suspension. */
|
||||
PivotStackAndInvoke(reinterpret_cast<void *>(CommonSmcStackTop), SaveSecureContextAndSuspend);
|
||||
|
||||
/* This code will never be reached. */
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SmcResult SmcPowerOffCpu(SmcArguments &args) {
|
||||
/* Get the current core id. */
|
||||
const auto core_id = hw::GetCurrentCoreId();
|
||||
|
||||
/* Note that we're expecting a reset for the current core. */
|
||||
SetResetExpected(true);
|
||||
|
||||
/* If we're on the final core, shut down directly. Otherwise, invoke with special stack. */
|
||||
if (core_id == NumCores - 1) {
|
||||
PowerOffCpu();
|
||||
} else {
|
||||
PivotStackAndInvoke(GetCoreExceptionStackVirtual(), PowerOffCpu);
|
||||
}
|
||||
|
||||
/* This code will never be reached. */
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
SmcResult SmcPowerOnCpu(SmcArguments &args) {
|
||||
/* Get and validate the core to power on. */
|
||||
const int which_core = args.r[1];
|
||||
SMC_R_UNLESS(0 <= which_core && which_core < NumCores, PsciInvalidParameters);
|
||||
|
||||
/* Ensure the core isn't already on. */
|
||||
SMC_R_UNLESS(!IsCoreOn(which_core), PsciAlreadyOn);
|
||||
|
||||
/* Save the entry context. */
|
||||
SetEntryContext(which_core, args.r[2], args.r[3]);
|
||||
|
||||
/* Reset the cpu. */
|
||||
ResetCpu(which_core);
|
||||
|
||||
/* Turn on the core. */
|
||||
PowerOnCpu(CpuPowerGateStatusMasks[which_core], CpuPowerGateTogglePartitionIds[which_core]);
|
||||
|
||||
/* Start the core. */
|
||||
StartCpu(which_core);
|
||||
|
||||
return SmcResult::PsciSuccess;
|
||||
}
|
||||
|
||||
SmcResult SmcSuspendCpu(SmcArguments &args) {
|
||||
return LockSecurityEngineAndInvoke(args, SuspendCpuImpl);
|
||||
}
|
||||
|
||||
bool IsChargerHiZModeEnabled() {
|
||||
return g_charger_hi_z_mode_enabled;
|
||||
}
|
||||
|
||||
void SetChargerHiZModeEnabled(bool en) {
|
||||
g_charger_hi_z_mode_enabled = en;
|
||||
}
|
||||
|
||||
}
|
30
exosphere/program/source/smc/secmon_smc_power_management.hpp
Normal file
30
exosphere/program/source/smc/secmon_smc_power_management.hpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <exosphere.hpp>
|
||||
#include "secmon_smc_common.hpp"
|
||||
|
||||
namespace ams::secmon::smc {
|
||||
|
||||
SmcResult SmcPowerOffCpu(SmcArguments &args);
|
||||
SmcResult SmcPowerOnCpu(SmcArguments &args);
|
||||
|
||||
SmcResult SmcSuspendCpu(SmcArguments &args);
|
||||
|
||||
bool IsChargerHiZModeEnabled();
|
||||
void SetChargerHiZModeEnabled(bool en);
|
||||
|
||||
}
|
77
exosphere/program/source/smc/secmon_smc_random.cpp
Normal file
77
exosphere/program/source/smc/secmon_smc_random.cpp
Normal file
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include "../secmon_error.hpp"
|
||||
#include "secmon_smc_random.hpp"
|
||||
#include "secmon_random_cache.hpp"
|
||||
#include "secmon_smc_se_lock.hpp"
|
||||
|
||||
namespace ams::secmon::smc {
|
||||
|
||||
namespace {
|
||||
|
||||
SmcResult GenerateRandomBytesImpl(SmcArguments &args) {
|
||||
/* Validate the input size. */
|
||||
const size_t size = args.r[1];
|
||||
SMC_R_UNLESS(size <= MaxRandomBytes, InvalidArgument);
|
||||
|
||||
/* Create a buffer that the se can generate bytes into. */
|
||||
util::AlignedBuffer<hw::DataCacheLineSize, MaxRandomBytes> buffer;
|
||||
hw::FlushDataCache(buffer, size);
|
||||
hw::DataSynchronizationBarrierInnerShareable();
|
||||
|
||||
/* Generate random bytes into the buffer. */
|
||||
se::GenerateRandomBytes(buffer, size);
|
||||
|
||||
/* Ensure that the cpu sees consistent data. */
|
||||
hw::DataSynchronizationBarrierInnerShareable();
|
||||
hw::FlushDataCache(buffer, size);
|
||||
hw::DataSynchronizationBarrierInnerShareable();
|
||||
|
||||
/* Copy the bytes to output. */
|
||||
std::memcpy(std::addressof(args.r[1]), buffer, size);
|
||||
return SmcResult::Success;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SmcResult SmcGenerateRandomBytes(SmcArguments &args) {
|
||||
return LockSecurityEngineAndInvoke(args, GenerateRandomBytesImpl);
|
||||
}
|
||||
|
||||
SmcResult SmcGenerateRandomBytesNonBlocking(SmcArguments &args) {
|
||||
/* Try to lock the security engine, so that we can call the standard impl. */
|
||||
if (TryLockSecurityEngine()) {
|
||||
/* Ensure we unlock the security engine when done. */
|
||||
ON_SCOPE_EXIT { UnlockSecurityEngine(); };
|
||||
|
||||
/* Take advantage of our lock to refill lthe random cache. */
|
||||
ON_SCOPE_EXIT { RefillRandomCache(); };
|
||||
|
||||
/* If we lock it successfully, we can just call the blocking impl. */
|
||||
return GenerateRandomBytesImpl(args);
|
||||
} else {
|
||||
/* Otherwise, we'll retrieve some bytes from the cache. */
|
||||
const size_t size = args.r[1];
|
||||
SMC_R_UNLESS(size <= MaxRandomBytes, InvalidArgument);
|
||||
|
||||
/* Get random bytes from the cache. */
|
||||
GetRandomFromCache(std::addressof(args.r[1]), size);
|
||||
return SmcResult::Success;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
25
exosphere/program/source/smc/secmon_smc_random.hpp
Normal file
25
exosphere/program/source/smc/secmon_smc_random.hpp
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <exosphere.hpp>
|
||||
#include "secmon_smc_common.hpp"
|
||||
|
||||
namespace ams::secmon::smc {
|
||||
|
||||
SmcResult SmcGenerateRandomBytes(SmcArguments &args);
|
||||
SmcResult SmcGenerateRandomBytesNonBlocking(SmcArguments &args);
|
||||
|
||||
}
|
184
exosphere/program/source/smc/secmon_smc_register_access.cpp
Normal file
184
exosphere/program/source/smc/secmon_smc_register_access.cpp
Normal file
|
@ -0,0 +1,184 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include "../secmon_error.hpp"
|
||||
#include "secmon_smc_register_access.hpp"
|
||||
|
||||
namespace ams::secmon::smc {
|
||||
|
||||
namespace {
|
||||
|
||||
template<size_t N>
|
||||
constexpr void SetRegisterTableAllowed(std::array<u8, N> &arr, uintptr_t reg) {
|
||||
/* All registers should be four byte aligned. */
|
||||
AMS_ASSUME(reg % sizeof(u32) == 0);
|
||||
|
||||
/* Reduce the register to an index. */
|
||||
reg /= sizeof(u32);
|
||||
|
||||
/* Get the index and mask. */
|
||||
const auto index = reg / BITSIZEOF(u8);
|
||||
const auto mask = (1u << (reg % BITSIZEOF(u8)));
|
||||
|
||||
/* Check that the permission bit isn't already set. */
|
||||
AMS_ASSUME((arr[index] & mask) == 0);
|
||||
|
||||
/* Set the permission bit. */
|
||||
arr[index] |= mask;
|
||||
|
||||
/* Ensure that indices are set in sorted order. */
|
||||
for (auto i = (reg % BITSIZEOF(u8)) + 1; i < 8; ++i) {
|
||||
AMS_ASSUME((arr[index] & (1u << i)) == 0);
|
||||
}
|
||||
|
||||
for (auto i = index + 1; i < arr.size(); ++i) {
|
||||
AMS_ASSUME(arr[i] == 0);
|
||||
}
|
||||
}
|
||||
|
||||
template<size_t N>
|
||||
consteval std::pair<size_t, size_t> GetReducedAccessTableInfo(const std::array<u8, N> &arr) {
|
||||
for (int last = arr.size() - 1; last >= 0; --last) {
|
||||
if (arr[last] != 0) {
|
||||
const int end = last + 1;
|
||||
for (int start = 0; start < end; ++start) {
|
||||
if (arr[start] != 0) {
|
||||
return std::make_pair(static_cast<size_t>(start), static_cast<size_t>(end));
|
||||
}
|
||||
}
|
||||
return std::make_pair(static_cast<size_t>(0), static_cast<size_t>(end));
|
||||
}
|
||||
}
|
||||
|
||||
/* All empty perm table is disallowed. */
|
||||
AMS_ASSUME(false);
|
||||
}
|
||||
|
||||
|
||||
template<u32 _Address, auto RawTable>
|
||||
struct AccessTable {
|
||||
static constexpr inline auto ReducedAccessTableInfo = GetReducedAccessTableInfo(RawTable);
|
||||
static constexpr inline size_t ReducedAccessTableSize = ReducedAccessTableInfo.second - ReducedAccessTableInfo.first;
|
||||
static constexpr inline auto ReducedAccessTable = []() -> std::array<u8, ReducedAccessTableSize> {
|
||||
std::array<u8, ReducedAccessTableSize> reduced = {};
|
||||
|
||||
for (size_t i = ReducedAccessTableInfo.first; i < ReducedAccessTableInfo.second; ++i) {
|
||||
reduced[i - ReducedAccessTableInfo.first] = RawTable[i];
|
||||
}
|
||||
|
||||
return reduced;
|
||||
}();
|
||||
|
||||
static constexpr u32 Address = _Address + (ReducedAccessTableInfo.first * sizeof(u32) * BITSIZEOF(u8));
|
||||
static constexpr u32 Size = static_cast<u32>(ReducedAccessTableSize * sizeof(u32) * BITSIZEOF(u8));
|
||||
|
||||
static_assert(Size <= 0x1000);
|
||||
};
|
||||
|
||||
struct AccessTableEntry {
|
||||
const u8 * const table;
|
||||
uintptr_t virtual_address;
|
||||
u32 address;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
/* Include the access tables. */
|
||||
#include "secmon_define_pmc_access_table.inc"
|
||||
#include "secmon_define_mc_access_table.inc"
|
||||
#include "secmon_define_mc01_access_table.inc"
|
||||
|
||||
constexpr const AccessTableEntry AccessTables[] = {
|
||||
{ PmcAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDevicePmc.GetAddress(), PmcAccessTable::Address, PmcAccessTable::Size, },
|
||||
{ McAccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDeviceMemoryController.GetAddress(), McAccessTable::Address, McAccessTable::Size, },
|
||||
{ Mc01AccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDeviceMemoryController0.GetAddress(), Mc01AccessTable::Address + MemoryRegionPhysicalDeviceMemoryController0.GetAddress(), Mc01AccessTable::Size, },
|
||||
{ Mc01AccessTable::ReducedAccessTable.data(), MemoryRegionVirtualDeviceMemoryController1.GetAddress(), Mc01AccessTable::Address + MemoryRegionPhysicalDeviceMemoryController1.GetAddress(), Mc01AccessTable::Size, },
|
||||
};
|
||||
|
||||
constexpr bool IsAccessAllowed(const AccessTableEntry &entry, uintptr_t address) {
|
||||
/* Check if the address is within range. */
|
||||
if (!(entry.address <= address && address < entry.address + entry.size)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Get the offset. */
|
||||
const auto offset = address - entry.address;
|
||||
|
||||
/* Convert it to an index. */
|
||||
const auto reg_index = offset / sizeof(u32);
|
||||
|
||||
/* Get the bit fields. */
|
||||
const auto index = reg_index / BITSIZEOF(u8);
|
||||
const auto mask = (1u << (reg_index % BITSIZEOF(u8)));
|
||||
|
||||
/* Validate that we're not going out of bounds. */
|
||||
if (index >= entry.size / sizeof(u32)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (entry.table[index] & mask) != 0;
|
||||
}
|
||||
|
||||
constexpr const AccessTableEntry *GetAccessTableEntry(uintptr_t address) {
|
||||
for (const auto &entry : AccessTables) {
|
||||
if (IsAccessAllowed(entry, address)) {
|
||||
return std::addressof(entry);
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SmcResult SmcReadWriteRegister(SmcArguments &args) {
|
||||
/* Get the arguments. */
|
||||
const uintptr_t address = args.r[1];
|
||||
const u32 mask = args.r[2];
|
||||
const u32 value = args.r[3];
|
||||
|
||||
/* Validate that the address is aligned. */
|
||||
SMC_R_UNLESS(util::IsAligned(address, alignof(u32)), InvalidArgument);
|
||||
|
||||
/* Find the access table. */
|
||||
const AccessTableEntry * const entry = GetAccessTableEntry(address);
|
||||
|
||||
/* If we have a table, perform the write. */
|
||||
if (entry != nullptr) {
|
||||
/* Get the address to read or write. */
|
||||
const uintptr_t virtual_address = entry->virtual_address + (address - entry->address);
|
||||
u32 out = 0;
|
||||
|
||||
if (mask != ~static_cast<u32>(0)) {
|
||||
out = reg::Read(virtual_address);
|
||||
}
|
||||
if (mask != static_cast<u32>(0)) {
|
||||
reg::Write(virtual_address, (out & ~mask) | (value & mask));
|
||||
}
|
||||
|
||||
args.r[1] = out;
|
||||
} else {
|
||||
/* For no clearly discernable reason, SmcReadWriteRegister returns success despite not doing the read/write */
|
||||
/* when accessing the SMMU controls for the BPMP and for APB-DMA. */
|
||||
/* This is "probably" to fuck with hackers who got access to the SMC and are trying to get control of the */
|
||||
/* BPMP to exploit jamais vu, deja vu, or other related DMA/wake-from-sleep vulnerabilities. */
|
||||
constexpr uintptr_t MC = MemoryRegionPhysicalDeviceMemoryController.GetAddress();
|
||||
SMC_R_UNLESS((address == (MC + MC_SMMU_AVPC_ASID) || address == (MC + MC_SMMU_PPCS1_ASID)), InvalidArgument);
|
||||
}
|
||||
|
||||
return SmcResult::Success;
|
||||
}
|
||||
|
||||
}
|
24
exosphere/program/source/smc/secmon_smc_register_access.hpp
Normal file
24
exosphere/program/source/smc/secmon_smc_register_access.hpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <exosphere.hpp>
|
||||
#include "secmon_smc_common.hpp"
|
||||
|
||||
namespace ams::secmon::smc {
|
||||
|
||||
SmcResult SmcReadWriteRegister(SmcArguments &args);
|
||||
|
||||
}
|
106
exosphere/program/source/smc/secmon_smc_result.cpp
Normal file
106
exosphere/program/source/smc/secmon_smc_result.cpp
Normal file
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include "../secmon_error.hpp"
|
||||
#include "../secmon_page_mapper.hpp"
|
||||
#include "secmon_smc_result.hpp"
|
||||
|
||||
namespace ams::secmon::smc {
|
||||
|
||||
namespace {
|
||||
|
||||
constinit u64 g_async_key = InvalidAsyncKey;
|
||||
constinit GetResultHandler g_async_handler = nullptr;
|
||||
|
||||
u64 GenerateRandomU64() {
|
||||
/* NOTE: This is one of the only places where Nintendo does not do data flushing. */
|
||||
/* to ensure coherency when doing random byte generation. */
|
||||
/* It is not clear why it is necessary elsewhere but not here. */
|
||||
/* TODO: Figure out why. */
|
||||
u64 v;
|
||||
se::GenerateRandomBytes(std::addressof(v), sizeof(v));
|
||||
return v;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
u64 BeginAsyncOperation(GetResultHandler handler) {
|
||||
/* Only allow one async operation at a time. */
|
||||
if (g_async_key != InvalidAsyncKey) {
|
||||
return InvalidAsyncKey;
|
||||
}
|
||||
|
||||
/* Generate a random async key. */
|
||||
g_async_key = GenerateRandomU64();
|
||||
g_async_handler = handler;
|
||||
|
||||
return g_async_key;
|
||||
}
|
||||
|
||||
void CancelAsyncOperation(u64 async_key) {
|
||||
if (async_key == g_async_key) {
|
||||
g_async_key = InvalidAsyncKey;
|
||||
}
|
||||
}
|
||||
|
||||
void EndAsyncOperation() {
|
||||
gic::SetPending(SecurityEngineUserInterruptId);
|
||||
}
|
||||
|
||||
SmcResult SmcGetResult(SmcArguments &args) {
|
||||
/* Decode arguments. */
|
||||
const u64 async_key = args.r[1];
|
||||
|
||||
/* Validate arguments. */
|
||||
SMC_R_UNLESS(g_async_key != InvalidAsyncKey, NoAsyncOperation);
|
||||
SMC_R_UNLESS(g_async_key == async_key, InvalidAsyncOperation);
|
||||
|
||||
/* Call the handler. */
|
||||
args.r[1] = static_cast<u64>(g_async_handler(nullptr, 0));
|
||||
g_async_key = InvalidAsyncKey;
|
||||
|
||||
return SmcResult::Success;
|
||||
}
|
||||
|
||||
SmcResult SmcGetResultData(SmcArguments &args) {
|
||||
/* Decode arguments. */
|
||||
const u64 async_key = args.r[1];
|
||||
const uintptr_t user_phys_addr = args.r[2];
|
||||
const size_t user_size = args.r[3];
|
||||
|
||||
/* Allocate a work buffer on the stack. */
|
||||
alignas(8) u8 work_buffer[1_KB];
|
||||
|
||||
/* Validate arguments. */
|
||||
SMC_R_UNLESS(g_async_key != InvalidAsyncKey, NoAsyncOperation);
|
||||
SMC_R_UNLESS(g_async_key == async_key, InvalidAsyncOperation);
|
||||
SMC_R_UNLESS(user_size <= sizeof(work_buffer), InvalidArgument);
|
||||
|
||||
/* Call the handler. */
|
||||
args.r[1] = static_cast<u64>(g_async_handler(work_buffer, user_size));
|
||||
g_async_key = InvalidAsyncKey;
|
||||
|
||||
/* Map the user buffer. */
|
||||
{
|
||||
UserPageMapper mapper(user_phys_addr);
|
||||
SMC_R_UNLESS(mapper.Map(), InvalidArgument);
|
||||
SMC_R_UNLESS(mapper.CopyToUser(user_phys_addr, work_buffer, user_size), InvalidArgument);
|
||||
}
|
||||
|
||||
return SmcResult::Success;
|
||||
}
|
||||
|
||||
}
|
31
exosphere/program/source/smc/secmon_smc_result.hpp
Normal file
31
exosphere/program/source/smc/secmon_smc_result.hpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <exosphere.hpp>
|
||||
#include "secmon_smc_common.hpp"
|
||||
|
||||
namespace ams::secmon::smc {
|
||||
|
||||
using GetResultHandler = SmcResult (*)(void *dst, size_t dst_size);
|
||||
|
||||
u64 BeginAsyncOperation(GetResultHandler handler);
|
||||
void CancelAsyncOperation(u64 async_key);
|
||||
void EndAsyncOperation();
|
||||
|
||||
SmcResult SmcGetResult(SmcArguments &args);
|
||||
SmcResult SmcGetResultData(SmcArguments &args);
|
||||
|
||||
}
|
357
exosphere/program/source/smc/secmon_smc_rsa.cpp
Normal file
357
exosphere/program/source/smc/secmon_smc_rsa.cpp
Normal file
|
@ -0,0 +1,357 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include "../secmon_error.hpp"
|
||||
#include "../secmon_key_storage.hpp"
|
||||
#include "../secmon_page_mapper.hpp"
|
||||
#include "secmon_smc_aes.hpp"
|
||||
#include "secmon_smc_rsa.hpp"
|
||||
#include "secmon_smc_se_lock.hpp"
|
||||
|
||||
namespace ams::secmon::smc {
|
||||
|
||||
namespace {
|
||||
|
||||
struct ModularExponentiateByStorageKeyOption {
|
||||
using Mode = util::BitPack32::Field<0, 2, u32>;
|
||||
using Reserved = util::BitPack32::Field<2, 30, u32>;
|
||||
};
|
||||
|
||||
struct PrepareEsDeviceUniqueKeyOption {
|
||||
using KeyGeneration = util::BitPack32::Field<0, 6, int>;
|
||||
using Type = util::BitPack32::Field<6, 1, EsCommonKeyType>;
|
||||
using Reserved = util::BitPack32::Field<7, 25, u32>;
|
||||
};
|
||||
|
||||
constexpr const u8 ModularExponentiateByStorageKeyTable[] = {
|
||||
static_cast<u8>(ImportRsaKey_Lotus),
|
||||
static_cast<u8>(ImportRsaKey_Ssl),
|
||||
static_cast<u8>(ImportRsaKey_EsClientCert),
|
||||
};
|
||||
constexpr size_t ModularExponentiateByStorageKeyTableSize = util::size(ModularExponentiateByStorageKeyTable);
|
||||
|
||||
class PrepareEsDeviceUniqueKeyAsyncArguments {
|
||||
private:
|
||||
int generation;
|
||||
EsCommonKeyType type;
|
||||
u8 label_digest[crypto::Sha256Generator::HashSize];
|
||||
public:
|
||||
void Set(int gen, EsCommonKeyType t, const u8 ld[crypto::Sha256Generator::HashSize]) {
|
||||
this->generation = gen;
|
||||
this->type = t;
|
||||
std::memcpy(this->label_digest, ld, sizeof(this->label_digest));
|
||||
}
|
||||
|
||||
int GetKeyGeneration() const { return this->generation; }
|
||||
EsCommonKeyType GetCommonKeyType() const { return this->type; }
|
||||
void GetLabelDigest(u8 dst[crypto::Sha256Generator::HashSize]) const { std::memcpy(dst, this->label_digest, sizeof(this->label_digest)); }
|
||||
};
|
||||
|
||||
class ModularExponentiateByStorageKeyAsyncArguments {
|
||||
private:
|
||||
u8 msg[se::RsaSize];
|
||||
public:
|
||||
void Set(const void *m, size_t m_size) {
|
||||
std::memcpy(this->msg, m, sizeof(this->msg));
|
||||
}
|
||||
|
||||
const u8 *GetMessage() const { return this->msg; }
|
||||
};
|
||||
|
||||
constinit SmcResult g_exp_mod_result = SmcResult::Success;
|
||||
|
||||
constinit bool g_test_exp_mod_public = false;
|
||||
constinit int g_test_exp_mod_slot = pkg1::RsaKeySlot_Temporary;
|
||||
constinit ImportRsaKey g_test_exp_mod_key = {};
|
||||
|
||||
constinit union {
|
||||
ModularExponentiateByStorageKeyAsyncArguments modular_exponentiate_by_storage_key;
|
||||
PrepareEsDeviceUniqueKeyAsyncArguments prepare_es_device_unique_key;
|
||||
} g_async_arguments;
|
||||
|
||||
ALWAYS_INLINE ModularExponentiateByStorageKeyAsyncArguments &GetModularExponentiateByStorageKeyAsyncArguments() {
|
||||
return g_async_arguments.modular_exponentiate_by_storage_key;
|
||||
}
|
||||
|
||||
ALWAYS_INLINE PrepareEsDeviceUniqueKeyAsyncArguments &GetPrepareEsDeviceUniqueKeyAsyncArguments() {
|
||||
return g_async_arguments.prepare_es_device_unique_key;
|
||||
}
|
||||
|
||||
void SecurityEngineDoneHandler() {
|
||||
/* End the asynchronous operation. */
|
||||
g_exp_mod_result = SmcResult::Success;
|
||||
EndAsyncOperation();
|
||||
}
|
||||
|
||||
void TestRsaPublicKey(ImportRsaKey which, int slot, const void *mod, size_t mod_size, se::DoneHandler handler) {
|
||||
/* Declare a buffer for our test message. */
|
||||
u8 msg[se::RsaSize];
|
||||
std::memset(msg, 'D', sizeof(msg));
|
||||
|
||||
/* Provisionally import the modulus. */
|
||||
ImportRsaKeyModulusProvisionally(which, mod, mod_size);
|
||||
|
||||
/* Load the provisional public key into the slot. */
|
||||
LoadProvisionalRsaPublicKey(slot, which);
|
||||
|
||||
/* Perform the test exponentiation. */
|
||||
se::ModularExponentiateAsync(slot, msg, sizeof(msg), handler);
|
||||
}
|
||||
|
||||
void TestRsaPrivateKey(ImportRsaKey which, int slot, se::DoneHandler handler) {
|
||||
/* Get the result of the public key test. */
|
||||
u8 msg[se::RsaSize];
|
||||
se::GetRsaResult(msg, sizeof(msg));
|
||||
|
||||
/* Load the provisional private key into the slot. */
|
||||
LoadProvisionalRsaKey(slot, which);
|
||||
|
||||
/* Perform the test exponentiation. */
|
||||
se::ModularExponentiateAsync(slot, msg, sizeof(msg), handler);
|
||||
}
|
||||
|
||||
void VerifyTestRsaKeyResult(ImportRsaKey which) {
|
||||
/* Get the result of the test. */
|
||||
u8 msg[se::RsaSize];
|
||||
se::GetRsaResult(msg, sizeof(msg));
|
||||
|
||||
/* Validate the result. */
|
||||
const bool is_valid = (msg[0] == 'D') & (crypto::IsSameBytes(msg, msg + 1, sizeof(msg) - 1));
|
||||
|
||||
/* If the test passes, the key is no longer provisional. */
|
||||
if (is_valid) {
|
||||
CommitRsaKeyModulus(which);
|
||||
}
|
||||
}
|
||||
|
||||
void TestRsaKeyDoneHandler() {
|
||||
if (g_test_exp_mod_public) {
|
||||
/* If we're testing the public key, we still have another exponentiation to do to test the private key. */
|
||||
g_test_exp_mod_public = false;
|
||||
|
||||
/* Test the private key. */
|
||||
TestRsaPrivateKey(g_test_exp_mod_key, g_test_exp_mod_slot, TestRsaKeyDoneHandler);
|
||||
} else {
|
||||
/* We're testing the private key, so validate the result. */
|
||||
VerifyTestRsaKeyResult(g_test_exp_mod_key);
|
||||
|
||||
/* If the test passed, we can proceed to perform the intended exponentiation. */
|
||||
if (LoadRsaKey(g_test_exp_mod_slot, g_test_exp_mod_key)) {
|
||||
se::ModularExponentiateAsync(pkg1::RsaKeySlot_Temporary, GetModularExponentiateByStorageKeyAsyncArguments().GetMessage(), se::RsaSize, SecurityEngineDoneHandler);
|
||||
} else {
|
||||
/* The test failed, so end the asynchronous operation. */
|
||||
g_exp_mod_result = SmcResult::InvalidArgument;
|
||||
EndAsyncOperation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SmcResult ModularExponentiateImpl(SmcArguments &args) {
|
||||
/* Decode arguments. */
|
||||
const uintptr_t msg_address = args.r[1];
|
||||
const uintptr_t exp_address = args.r[2];
|
||||
const uintptr_t mod_address = args.r[3];
|
||||
const size_t exp_size = args.r[4];
|
||||
|
||||
/* Validate arguments. */
|
||||
SMC_R_UNLESS(util::IsAligned(exp_size, sizeof(u32)), InvalidArgument);
|
||||
SMC_R_UNLESS(exp_size <= se::RsaSize, InvalidArgument);
|
||||
|
||||
/* Copy the message and modulus from the user. */
|
||||
alignas(8) u8 msg[se::RsaSize];
|
||||
alignas(8) u8 exp[se::RsaSize];
|
||||
alignas(8) u8 mod[se::RsaSize];
|
||||
{
|
||||
UserPageMapper mapper(msg_address);
|
||||
SMC_R_UNLESS(mapper.Map(), InvalidArgument);
|
||||
SMC_R_UNLESS(mapper.CopyFromUser(msg, msg_address, sizeof(msg)), InvalidArgument);
|
||||
SMC_R_UNLESS(mapper.CopyFromUser(exp, exp_address, exp_size), InvalidArgument);
|
||||
SMC_R_UNLESS(mapper.CopyFromUser(mod, mod_address, sizeof(mod)), InvalidArgument);
|
||||
}
|
||||
|
||||
/* We're performing an operation, so set the result to busy. */
|
||||
g_exp_mod_result = SmcResult::Busy;
|
||||
|
||||
/* Load the key into the temporary keyslot. */
|
||||
se::SetRsaKey(pkg1::RsaKeySlot_Temporary, mod, sizeof(mod), exp, exp_size);
|
||||
|
||||
/* Begin the asynchronous exponentiation. */
|
||||
se::ModularExponentiateAsync(pkg1::RsaKeySlot_Temporary, msg, sizeof(msg), SecurityEngineDoneHandler);
|
||||
|
||||
return SmcResult::Success;
|
||||
}
|
||||
|
||||
SmcResult ModularExponentiateByStorageKeyImpl(SmcArguments &args) {
|
||||
/* Decode arguments. */
|
||||
const uintptr_t msg_address = args.r[1];
|
||||
const uintptr_t mod_address = args.r[2];
|
||||
const util::BitPack32 option = { static_cast<u32>(args.r[3]) };
|
||||
|
||||
const auto mode = option.Get<ModularExponentiateByStorageKeyOption::Mode>();
|
||||
const auto reserved = option.Get<PrepareEsDeviceUniqueKeyOption::Reserved>();
|
||||
|
||||
/* Validate arguments. */
|
||||
SMC_R_UNLESS(reserved == 0, InvalidArgument);
|
||||
SMC_R_UNLESS(mode < ModularExponentiateByStorageKeyTableSize, InvalidArgument);
|
||||
|
||||
/* Convert the mode to an import key. */
|
||||
const auto import_key = static_cast<ImportRsaKey>(ModularExponentiateByStorageKeyTable[mode]);
|
||||
|
||||
/* Copy the message and modulus from the user. */
|
||||
alignas(8) u8 msg[se::RsaSize];
|
||||
alignas(8) u8 mod[se::RsaSize];
|
||||
{
|
||||
UserPageMapper mapper(msg_address);
|
||||
SMC_R_UNLESS(mapper.Map(), InvalidArgument);
|
||||
SMC_R_UNLESS(mapper.CopyFromUser(msg, msg_address, sizeof(msg)), InvalidArgument);
|
||||
SMC_R_UNLESS(mapper.CopyFromUser(mod, mod_address, sizeof(mod)), InvalidArgument);
|
||||
}
|
||||
|
||||
/* We're performing an operation, so set the result to busy. */
|
||||
g_exp_mod_result = SmcResult::Busy;
|
||||
|
||||
/* In the ideal case, the key pair is already verified. If it is, we can use it directly. */
|
||||
if (LoadRsaKey(pkg1::RsaKeySlot_Temporary, import_key)) {
|
||||
se::ModularExponentiateAsync(pkg1::RsaKeySlot_Temporary, msg, sizeof(msg), SecurityEngineDoneHandler);
|
||||
} else {
|
||||
/* Set the async arguments. */
|
||||
GetModularExponentiateByStorageKeyAsyncArguments().Set(msg, sizeof(msg));
|
||||
|
||||
/* Test the rsa key. */
|
||||
g_test_exp_mod_slot = pkg1::RsaKeySlot_Temporary;
|
||||
g_test_exp_mod_key = import_key;
|
||||
g_test_exp_mod_public = true;
|
||||
|
||||
TestRsaPublicKey(import_key, pkg1::RsaKeySlot_Temporary, mod, sizeof(mod), TestRsaKeyDoneHandler);
|
||||
}
|
||||
|
||||
return SmcResult::Success;
|
||||
}
|
||||
|
||||
SmcResult PrepareEsDeviceUniqueKeyImpl(SmcArguments &args) {
|
||||
/* Decode arguments. */
|
||||
u8 label_digest[crypto::Sha256Generator::HashSize];
|
||||
|
||||
const uintptr_t msg_address = args.r[1];
|
||||
const uintptr_t mod_address = args.r[2];
|
||||
std::memcpy(label_digest, std::addressof(args.r[3]), sizeof(label_digest));
|
||||
const util::BitPack32 option = { static_cast<u32>(args.r[7]) };
|
||||
|
||||
const auto generation = GetTargetFirmware() >= TargetFirmware_3_0_0 ? std::max(0, option.Get<PrepareEsDeviceUniqueKeyOption::KeyGeneration>() - 1) : 0;
|
||||
const auto type = option.Get<PrepareEsDeviceUniqueKeyOption::Type>();
|
||||
const auto reserved = option.Get<PrepareEsDeviceUniqueKeyOption::Reserved>();
|
||||
|
||||
/* Validate arguments. */
|
||||
SMC_R_UNLESS(reserved == 0, InvalidArgument);
|
||||
SMC_R_UNLESS(pkg1::IsValidKeyGeneration(generation), InvalidArgument);
|
||||
SMC_R_UNLESS(generation <= GetKeyGeneration(), InvalidArgument);
|
||||
SMC_R_UNLESS(type < EsCommonKeyType_Count, InvalidArgument);
|
||||
|
||||
/* Copy the message and modulus from the user. */
|
||||
alignas(8) u8 msg[se::RsaSize];
|
||||
alignas(8) u8 mod[se::RsaSize];
|
||||
{
|
||||
UserPageMapper mapper(msg_address);
|
||||
SMC_R_UNLESS(mapper.Map(), InvalidArgument);
|
||||
SMC_R_UNLESS(mapper.CopyFromUser(msg, msg_address, sizeof(msg)), InvalidArgument);
|
||||
SMC_R_UNLESS(mapper.CopyFromUser(mod, mod_address, sizeof(mod)), InvalidArgument);
|
||||
}
|
||||
|
||||
/* We're performing an operation, so set the result to busy. */
|
||||
g_exp_mod_result = SmcResult::Busy;
|
||||
|
||||
/* Set the async arguments. */
|
||||
GetPrepareEsDeviceUniqueKeyAsyncArguments().Set(generation, type, label_digest);
|
||||
|
||||
/* Load the es drm key into the security engine. */
|
||||
SMC_R_UNLESS(LoadRsaKey(pkg1::RsaKeySlot_Temporary, ImportRsaKey_EsDrmCert), NotInitialized);
|
||||
|
||||
/* Trigger the asynchronous modular exponentiation. */
|
||||
se::ModularExponentiateAsync(pkg1::RsaKeySlot_Temporary, msg, sizeof(msg), SecurityEngineDoneHandler);
|
||||
|
||||
return SmcResult::Success;
|
||||
}
|
||||
|
||||
SmcResult GetModularExponentiateResult(void *dst, size_t dst_size) {
|
||||
/* Validate state. */
|
||||
SMC_R_TRY(g_exp_mod_result);
|
||||
SMC_R_UNLESS(dst_size == se::RsaSize, InvalidArgument);
|
||||
|
||||
/* We want to relinquish our security engine lock at the end of scope. */
|
||||
ON_SCOPE_EXIT { UnlockSecurityEngine(); };
|
||||
|
||||
/* Get the result of the exponentiation. */
|
||||
se::GetRsaResult(dst, se::RsaSize);
|
||||
|
||||
return SmcResult::Success;
|
||||
}
|
||||
|
||||
SmcResult GetPrepareEsDeviceUniqueKeyResult(void *dst, size_t dst_size) {
|
||||
/* Declare variables. */
|
||||
u8 key_source[se::AesBlockSize];
|
||||
u8 key[se::AesBlockSize];
|
||||
u8 access_key[se::AesBlockSize];
|
||||
|
||||
/* Validate state. */
|
||||
SMC_R_TRY(g_exp_mod_result);
|
||||
SMC_R_UNLESS(dst_size == sizeof(access_key), InvalidArgument);
|
||||
|
||||
/* We want to relinquish our security engine lock at the end of scope. */
|
||||
ON_SCOPE_EXIT { UnlockSecurityEngine(); };
|
||||
|
||||
/* Get the async args. */
|
||||
const auto &async_args = GetPrepareEsDeviceUniqueKeyAsyncArguments();
|
||||
|
||||
/* Get the exponentiation output. */
|
||||
alignas(8) u8 msg[se::RsaSize];
|
||||
se::GetRsaResult(msg, sizeof(msg));
|
||||
|
||||
/* Decode the key. */
|
||||
{
|
||||
/* Get the label digest. */
|
||||
u8 label_digest[crypto::Sha256Generator::HashSize];
|
||||
async_args.GetLabelDigest(label_digest);
|
||||
|
||||
/* Decode the key source. */
|
||||
const size_t key_source_size = se::DecodeRsaOaepSha256(key_source, sizeof(key_source), msg, sizeof(msg), label_digest, sizeof(label_digest));
|
||||
SMC_R_UNLESS(key_source_size == sizeof(key_source), InvalidArgument);
|
||||
}
|
||||
|
||||
/* Decrypt the key. */
|
||||
DecryptWithEsCommonKey(key, sizeof(key), key_source, sizeof(key_source), async_args.GetCommonKeyType(), async_args.GetKeyGeneration());
|
||||
PrepareEsAesKey(access_key, sizeof(access_key), key, sizeof(key));
|
||||
|
||||
/* Copy the access key to output. */
|
||||
std::memcpy(dst, access_key, sizeof(access_key));
|
||||
|
||||
return SmcResult::Success;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SmcResult SmcModularExponentiate(SmcArguments &args) {
|
||||
return LockSecurityEngineAndInvokeAsync(args, ModularExponentiateImpl, GetModularExponentiateResult);
|
||||
}
|
||||
|
||||
SmcResult SmcModularExponentiateByStorageKey(SmcArguments &args) {
|
||||
return LockSecurityEngineAndInvokeAsync(args, ModularExponentiateByStorageKeyImpl, GetModularExponentiateResult);
|
||||
}
|
||||
|
||||
SmcResult SmcPrepareEsDeviceUniqueKey(SmcArguments &args) {
|
||||
return LockSecurityEngineAndInvokeAsync(args, PrepareEsDeviceUniqueKeyImpl, GetPrepareEsDeviceUniqueKeyResult);
|
||||
}
|
||||
|
||||
}
|
27
exosphere/program/source/smc/secmon_smc_rsa.hpp
Normal file
27
exosphere/program/source/smc/secmon_smc_rsa.hpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <exosphere.hpp>
|
||||
#include "secmon_smc_common.hpp"
|
||||
|
||||
namespace ams::secmon::smc {
|
||||
|
||||
SmcResult SmcModularExponentiate(SmcArguments &args);
|
||||
SmcResult SmcModularExponentiateByStorageKey(SmcArguments &args);
|
||||
|
||||
SmcResult SmcPrepareEsDeviceUniqueKey(SmcArguments &args);
|
||||
|
||||
}
|
70
exosphere/program/source/smc/secmon_smc_se_lock.cpp
Normal file
70
exosphere/program/source/smc/secmon_smc_se_lock.cpp
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <exosphere.hpp>
|
||||
#include "../secmon_error.hpp"
|
||||
#include "secmon_smc_se_lock.hpp"
|
||||
|
||||
namespace ams::secmon::smc {
|
||||
|
||||
namespace {
|
||||
|
||||
constinit std::atomic_bool g_is_locked = false;
|
||||
|
||||
}
|
||||
|
||||
bool TryLockSecurityEngine() {
|
||||
bool value = false;
|
||||
return g_is_locked.compare_exchange_strong(value, true);
|
||||
}
|
||||
|
||||
void UnlockSecurityEngine() {
|
||||
g_is_locked = false;
|
||||
}
|
||||
|
||||
bool IsSecurityEngineLocked() {
|
||||
return g_is_locked;
|
||||
}
|
||||
|
||||
SmcResult LockSecurityEngineAndInvoke(SmcArguments &args, SmcHandler impl) {
|
||||
/* Try to lock the security engine. */
|
||||
SMC_R_UNLESS(TryLockSecurityEngine(), Busy);
|
||||
ON_SCOPE_EXIT { UnlockSecurityEngine(); };
|
||||
|
||||
return impl(args);
|
||||
}
|
||||
|
||||
SmcResult LockSecurityEngineAndInvokeAsync(SmcArguments &args, SmcHandler impl, GetResultHandler result_handler) {
|
||||
/* Try to lock the security engine. */
|
||||
SMC_R_UNLESS(TryLockSecurityEngine(), Busy);
|
||||
auto se_guard = SCOPE_GUARD { UnlockSecurityEngine(); };
|
||||
|
||||
/* Try to start an async operation. */
|
||||
const u64 async_key = BeginAsyncOperation(result_handler);
|
||||
SMC_R_UNLESS(async_key != InvalidAsyncKey, Busy);
|
||||
auto async_guard = SCOPE_GUARD { CancelAsyncOperation(async_key); };
|
||||
|
||||
/* Try to invoke the operation. */
|
||||
SMC_R_TRY(impl(args));
|
||||
|
||||
/* We succeeded! Cancel our guards, and return the async key to our caller. */
|
||||
async_guard.Cancel();
|
||||
se_guard.Cancel();
|
||||
|
||||
args.r[1] = async_key;
|
||||
return SmcResult::Success;
|
||||
}
|
||||
|
||||
}
|
31
exosphere/program/source/smc/secmon_smc_se_lock.hpp
Normal file
31
exosphere/program/source/smc/secmon_smc_se_lock.hpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2020 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <exosphere.hpp>
|
||||
#include "secmon_smc_common.hpp"
|
||||
#include "secmon_smc_handler.hpp"
|
||||
#include "secmon_smc_result.hpp"
|
||||
|
||||
namespace ams::secmon::smc {
|
||||
|
||||
bool TryLockSecurityEngine();
|
||||
void UnlockSecurityEngine();
|
||||
bool IsSecurityEngineLocked();
|
||||
|
||||
SmcResult LockSecurityEngineAndInvoke(SmcArguments &args, SmcHandler impl);
|
||||
SmcResult LockSecurityEngineAndInvokeAsync(SmcArguments &args, SmcHandler impl, GetResultHandler result_handler);
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue