From 18d73ff3743ec70127e03f7a28947d586298bd30 Mon Sep 17 00:00:00 2001 From: Tharo Date: Wed, 6 May 2026 08:25:34 +0100 Subject: [PATCH] Document CIC6105 (#2739) * Document CIC6105 * Changes, AUDIOMGR_DEBUG_LEVEL -> AUDIOMGR_ACTIVITY_LEVEL --- data/cic6105.text.s | 2 +- include/audiomgr.h | 10 +- include/cic6105.h | 10 +- include/regs.h | 2 +- src/boot/boot_main.c | 2 +- src/boot/cic6105.c | 100 +++++++++++++------- src/code/audio_thread_manager.c | 6 +- src/code/main.c | 2 +- src/overlays/actors/ovl_Fishing/z_fishing.c | 2 +- src/overlays/gamestates/ovl_title/z_title.c | 2 +- 10 files changed, 86 insertions(+), 52 deletions(-) diff --git a/data/cic6105.text.s b/data/cic6105.text.s index 387beeded5..5020669e2e 100644 --- a/data/cic6105.text.s +++ b/data/cic6105.text.s @@ -12,7 +12,7 @@ /* RSP code for cic6105.c, used only in N64 versions. */ glabel cic6105TextStart .word 0xE80C2001 # sqv $v12[0], 0x10($zero) - .word 0x34014000 # li $1, 0x4000 + .word 0x34014000 # li $1, SP_SET_SIG2 .word 0x40812000 # mtc0 $1, SP_STATUS .word 0x0000000D # break .word 0x00000000 # nop diff --git a/include/audiomgr.h b/include/audiomgr.h index 66a9050266..10dee7faba 100644 --- a/include/audiomgr.h +++ b/include/audiomgr.h @@ -4,11 +4,11 @@ #include "sched.h" #include "audio.h" -typedef enum AudioMgrDebugLevel { - /* 0 */ AUDIOMGR_DEBUG_LEVEL_NONE, - /* 1 */ AUDIOMGR_DEBUG_LEVEL_NO_RSP, - /* 2 */ AUDIOMGR_DEBUG_LEVEL_NO_UPDATE -} AudioMgrDebugLevel; +typedef enum AudioMgrActivityLevel { + /* 0 */ AUDIOMGR_ACTIVITY_LEVEL_ALL, + /* 1 */ AUDIOMGR_ACTIVITY_LEVEL_NO_RSP, + /* 2 */ AUDIOMGR_ACTIVITY_LEVEL_NO_UPDATE +} AudioMgrActivityLevel; typedef struct AudioMgr { /* 0x0000 */ IrqMgr* irqMgr; diff --git a/include/cic6105.h b/include/cic6105.h index c4983db8f5..16d1c86e53 100644 --- a/include/cic6105.h +++ b/include/cic6105.h @@ -3,12 +3,14 @@ #include "ultra64.h" -extern u32 B_80008EE0; +extern u32 gCICBootMagic0; -void func_800014E8(void); +#define CIC_BOOT_MAGIC0_IS_CORRECT() (gCICBootMagic0 == 0xAD090010) + +void CIC6105_EnableAudio(void); void CIC6105_AddFaultClient(void); void CIC6105_RemoveFaultClient(void); -void func_80001640(void); -void func_80001720(void); +void CIC6105_RunBootTask(void); +void CIC6105_SaveBootMagicValues(void); #endif diff --git a/include/regs.h b/include/regs.h index a59e059ab8..8d67563d29 100644 --- a/include/regs.h +++ b/include/regs.h @@ -54,7 +54,7 @@ struct PlayState; #define R_DECELERATE_RATE REG(43) #define R_RUN_SPEED_LIMIT REG(45) #define R_ENABLE_ARENA_DBG SREG(0) -#define R_AUDIOMGR_DEBUG_LEVEL SREG(20) +#define R_AUDIOMGR_ACTIVITY_LEVEL SREG(20) #define R_ROOM_IMAGE_NODRAW_FLAGS SREG(25) #define R_ROOM_BG2D_FORCE_SCALEBG SREG(26) #define R_UPDATE_RATE SREG(30) diff --git a/src/boot/boot_main.c b/src/boot/boot_main.c index 91b74cbe9f..ba95a33bf8 100644 --- a/src/boot/boot_main.c +++ b/src/boot/boot_main.c @@ -29,7 +29,7 @@ void bootproc(void) { osMemSize = osGetMemSize(); #if PLATFORM_N64 - func_80001720(); + CIC6105_SaveBootMagicValues(); #endif bootclear(); osInitialize(); diff --git a/src/boot/cic6105.c b/src/boot/cic6105.c index fadae3a181..8f8ba0d0e2 100644 --- a/src/boot/cic6105.c +++ b/src/boot/cic6105.c @@ -1,5 +1,19 @@ +/** + * @file cic6105.c + * + * This file implements routines relating to the CIC X105 anti-piracy measures present in N64 releases. + * + * The "authentication" chain begins in IPL3, which deposits specific expected values into RAM and runs an RSP task in + * parallel with loading the boot segment into RAM. This RSP task leaves data in the RSP's registers which rspboot and + * the CIC6105 RSP task later read according to routines in this file. Their security model relied on CICs being + * uncloneable with scarcely many donor CIC options from other games available at the time, in which case IPL3 would be + * unmodifiable. The rest of the chain is designed with a "security through obscurity" mindset, storing later antipiracy + * checks intermixed with other game code or RSP code that was expected to be non-trivial to analyze in a timely + * fashion. Notably this effort did little to curb emulation, almost wholly due to early emulators being insufficiently + * accurate to run RSP code or even IPL3 at a low level, sidestepping much of the early setup in favor of providing a + * known-good post-boot state to begin emulation from. + */ #pragma increment_block_number "ntsc-1.0:132 ntsc-1.1:132 ntsc-1.2:132 pal-1.0:132 pal-1.1:132" - #include "audiomgr.h" #include "build.h" #include "cic6105.h" @@ -7,35 +21,47 @@ #include "regs.h" #include "sched.h" -s32 func_80001714(void); +s32 CIC6105_Stub(void); -OSTask D_800067C0_unknown = { - 4, 0, rspbootTextStart, 0x3E8, cic6105TextStart, 0x20, (u64*)gBuildCreator, 8, NULL, 0, NULL, 0, NULL, 0, NULL, 0, +OSTask sCIC6105Task = { + // clang-format off + 4, + 0, + rspbootTextStart, 0x3E8, + cic6105TextStart, 0x20, + (u64*)gBuildCreator, 8, + NULL, 0, + NULL, NULL, + NULL, 0, + NULL, 0, + // clang-format on }; -u32 B_80008EE0; -u32 B_80008EE4; +u32 gCICBootMagic0; +u32 gCICBootMagic1; FaultClient sCIC6105FaultClient; -u32 B_80008EF8; -u32 B_80008EFC; +u32 sCICTaskResult0; +u32 sCICTaskResult1; -void func_800014D0(void) { - R_AUDIOMGR_DEBUG_LEVEL = AUDIOMGR_DEBUG_LEVEL_NO_RSP; +void CIC6105_DisableAudio(void) { + R_AUDIOMGR_ACTIVITY_LEVEL = AUDIOMGR_ACTIVITY_LEVEL_NO_RSP; } -void func_800014E8(void) { - R_AUDIOMGR_DEBUG_LEVEL = AUDIOMGR_DEBUG_LEVEL_NONE; +void CIC6105_EnableAudio(void) { + R_AUDIOMGR_ACTIVITY_LEVEL = AUDIOMGR_ACTIVITY_LEVEL_ALL; } void CIC6105_FaultClient(void) { - s32 spStatus; + u32 spStatus = IO_READ(SP_STATUS_REG); - spStatus = IO_READ(SP_STATUS_REG); Fault_SetCursor(48, 200); + // Signal 7 is set by rspboot when it is first executed, corresponding to + // whether rspboot's antipiracy checks passed. Signal 7 is expected to + // stay set for the entire duration of the game running. if (spStatus & SP_STATUS_SIG7) { - Fault_Printf("OCARINA %08x %08x", B_80008EF8, B_80008EFC); + Fault_Printf("OCARINA %08x %08x", sCICTaskResult0, sCICTaskResult1); } else { - Fault_Printf("LEGEND %08x %08x", B_80008EF8, B_80008EFC); + Fault_Printf("LEGEND %08x %08x", sCICTaskResult0, sCICTaskResult1); } Fault_SetCursor(40, 184); Fault_Printf("ROM_F"); @@ -47,7 +73,7 @@ void CIC6105_FaultClient(void) { #else Fault_SetCursor(96, 32); #endif - Fault_Printf("I LOVE YOU %08x", func_80001714()); + Fault_Printf("I LOVE YOU %08x", CIC6105_Stub()); } void CIC6105_AddFaultClient(void) { @@ -58,31 +84,37 @@ void CIC6105_RemoveFaultClient(void) { Fault_RemoveClient(&sCIC6105FaultClient); } -void func_80001640(void) { - OSScTask sp38; +void CIC6105_RunBootTask(void) { + OSScTask scTask; OSMesgQueue queue; OSMesg msg; + // Prepare the CIC6105 task osCreateMesgQueue(&queue, &msg, 1); - sp38.next = NULL; - sp38.flags = OS_SC_NEEDS_RSP; - sp38.msgQueue = &queue; - sp38.msg = (OSMesg)0; - sp38.framebuffer = 0; - sp38.list = D_800067C0_unknown; - osSendMesg(&gScheduler.cmdQueue, &sp38, OS_MESG_BLOCK); + scTask.next = NULL; + scTask.flags = OS_SC_NEEDS_RSP; + scTask.msgQueue = &queue; + scTask.msg = (OSMesg)0; + scTask.framebuffer = NULL; + scTask.list = sCIC6105Task; + // Send it to the scheduler for execution + osSendMesg(&gScheduler.cmdQueue, &scTask, OS_MESG_BLOCK); Sched_Notify(&gScheduler); - osRecvMesg(&queue, NULL, 1); - B_80008EF8 = IO_READ(SP_DMEM_START + 0xFF4); - B_80008EFC = IO_READ(SP_DMEM_START + 0xFFC); - func_80001714(); + // Blocking wait until completion + osRecvMesg(&queue, NULL, OS_MESG_BLOCK); + // Retrieve results from RSP DMEM, it is assumed no other RSP task is running + sCICTaskResult0 = IO_READ(SP_DMEM_START + 0xFF4); + sCICTaskResult1 = IO_READ(SP_DMEM_START + 0xFFC); + CIC6105_Stub(); } -s32 func_80001714(void) { +s32 CIC6105_Stub(void) { return 0; } -void func_80001720(void) { - B_80008EE0 = IO_READ(0x002FB1F4); - B_80008EE4 = IO_READ(0x002FE1C0); +void CIC6105_SaveBootMagicValues(void) { + // IPL3 writes two magic values into RDRAM during the boot process into fixed locations. + // These must be retrieved early before memory is claimed by game memory management systems. + gCICBootMagic0 = IO_READ(0x002FB1F4); + gCICBootMagic1 = IO_READ(0x002FE1C0); } diff --git a/src/code/audio_thread_manager.c b/src/code/audio_thread_manager.c index 01b0072d9b..6a1ae175c6 100644 --- a/src/code/audio_thread_manager.c +++ b/src/code/audio_thread_manager.c @@ -29,7 +29,7 @@ void AudioMgr_NotifyTaskDone(AudioMgr* audioMgr) { void AudioMgr_HandleRetrace(AudioMgr* audioMgr) { AudioTask* rspTask; - if (R_AUDIOMGR_DEBUG_LEVEL > AUDIOMGR_DEBUG_LEVEL_NONE) { + if (R_AUDIOMGR_ACTIVITY_LEVEL > AUDIOMGR_ACTIVITY_LEVEL_ALL) { // Inhibit audio rsp task processing audioMgr->rspTask = NULL; } @@ -53,7 +53,7 @@ void AudioMgr_HandleRetrace(AudioMgr* audioMgr) { gAudioThreadUpdateTimeStart = osGetTime(); - if (R_AUDIOMGR_DEBUG_LEVEL >= AUDIOMGR_DEBUG_LEVEL_NO_UPDATE) { + if (R_AUDIOMGR_ACTIVITY_LEVEL >= AUDIOMGR_ACTIVITY_LEVEL_NO_UPDATE) { // Skip update, no rsp task produced rspTask = NULL; } else { @@ -160,7 +160,7 @@ void AudioMgr_Init(AudioMgr* audioMgr, void* stack, OSPri pri, OSId id, Schedule audioMgr->rspTask = NULL; #if PLATFORM_N64 - R_AUDIOMGR_DEBUG_LEVEL = AUDIOMGR_DEBUG_LEVEL_NO_RSP; + R_AUDIOMGR_ACTIVITY_LEVEL = AUDIOMGR_ACTIVITY_LEVEL_NO_RSP; #endif osCreateMesgQueue(&audioMgr->taskDoneQueue, &audioMgr->taskDoneMsg, 1); diff --git a/src/code/main.c b/src/code/main.c index e137cff750..f693a5c499 100644 --- a/src/code/main.c +++ b/src/code/main.c @@ -159,7 +159,7 @@ void Main(void* arg) { #if PLATFORM_N64 CIC6105_AddFaultClient(); - func_80001640(); + CIC6105_RunBootTask(); #endif IrqMgr_AddClient(&gIrqMgr, &irqClient, &irqMgrMsgQueue); diff --git a/src/overlays/actors/ovl_Fishing/z_fishing.c b/src/overlays/actors/ovl_Fishing/z_fishing.c index 52e636c77d..f1e41b80e7 100644 --- a/src/overlays/actors/ovl_Fishing/z_fishing.c +++ b/src/overlays/actors/ovl_Fishing/z_fishing.c @@ -869,7 +869,7 @@ void Fishing_Init(Actor* thisx, PlayState* play2) { #if PLATFORM_N64 // Anti-piracy check, if the check fails the line can't be reeled in if // a fish is caught and the fish will always let go after 50 frames. - sReelLock = !(B_80008EE0 == 0xAD090010); + sReelLock = !CIC_BOOT_MAGIC0_IS_CORRECT(); #else sReelLock = 0; #endif diff --git a/src/overlays/gamestates/ovl_title/z_title.c b/src/overlays/gamestates/ovl_title/z_title.c index a17bd0f4d8..c8b90787b7 100644 --- a/src/overlays/gamestates/ovl_title/z_title.c +++ b/src/overlays/gamestates/ovl_title/z_title.c @@ -212,7 +212,7 @@ void ConsoleLogo_Destroy(GameState* thisx) { Sram_InitSram(&this->state, &this->sramCtx); #if PLATFORM_N64 - func_800014E8(); + CIC6105_EnableAudio(); #endif }