Skip to content

Commit

Permalink
mpu, vectorbase: Protect the alternate vector base region
Browse files Browse the repository at this point in the history
This PR disables writes to the alternate vector base region (0x00000000)
in all cases. It also disables reads to the region if the alternate
vector base is not opted into. This helps detect null pointer
access/write attempts in homebrew, as these will now raise an exception.
  • Loading branch information
asiekierka authored and AntonioND committed Nov 6, 2024
1 parent bb1e9e1 commit e9bfb29
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 23 deletions.
35 changes: 26 additions & 9 deletions include/nds/system.h
Original file line number Diff line number Diff line change
Expand Up @@ -221,21 +221,34 @@ static inline void systemShutDown(void)
/// Battery level and external power source status.
u32 getBatteryLevel(void);

/// Set the arm9 vector base
/// Set the ARM9 interrupt vector base to one of two locations:
///
/// @param highVector
/// High vector.
/// - 0xFFFF0000 (default; controlled by the BIOS)
/// - 0x00000000 (alternate; configurable by the homebrew program, initialized
/// by default with function pointers to BIOS handlers)
///
/// @note
/// ARM9 only
void setVectorBase(int highVector);

/// A struct with all the CPU exeption vectors.
/// To initialize function pointers for the alternate vector base, set the
/// relevant values in the SystemVectors structure before calling this
/// function.
///
/// Note that it is recommended to call this function with interrupts disabled
/// (REG_IME = 0).
///
/// @param base
/// Vector base. Setting it to any non-zero value will use the default
/// vector base (0xFFFF0000); setting it to zero will use the alternate
/// vector base (0x00000000).
///
/// @see SystemVectors
void setVectorBase(int base);

/// Structure of function pointers corresponding to ARM CPU interrupts.
/// Each member contains an ARM instuction that will be executed when an
/// exeption occurs.
///
/// See gbatek for more information.
/// See GBATEK for more information on each interrupt.
///
/// @see SystemVectors
typedef struct sysVectors
{
VoidFn reset; ///< CPU reset.
Expand All @@ -248,6 +261,10 @@ typedef struct sysVectors
VoidFn fiq; ///< Fast interrupt.
} sysVectors_t;

/// Function pointers to user-provided interrupt handlers, used in the
/// alternate interrupt vector mode in place of BIOS interrupt handlers.
///
/// @see setVectorBase
extern sysVectors_t SystemVectors;

void setSDcallback(void (*callback)(int));
Expand Down
17 changes: 17 additions & 0 deletions source/arm9/system/mpu_internal.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: Zlib
//
// Copyright (c) 2023 Antonio Niño Díaz

#ifndef ARM9_SYSTEM_MPU_INTERNAL_H__
#define ARM9_SYSTEM_MPU_INTERNAL_H__

#define REGION_IO_REGISTERS 0
#define REGION_SYSTEM_ROM 1
#define REGION_ALT_VECTORS 2
#define REGION_SLOT_2_DSI_IWRAM 3 // DS: Slot-2 | DSi: Switchable IWRAM.
#define REGION_ITCM 4
#define REGION_RAM_UNCACHED 5
#define REGION_RAM_CACHED 6
#define REGION_DTCM 7

#endif // ARM9_SYSTEM_MPU_INTERNAL_H__
27 changes: 16 additions & 11 deletions source/arm9/system/mpu_setup.s
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
// ----+------------+--------+-----------+--------+-------+----+-----------------------
// 1 | 0xFFFF0000 | 64 KB | All | RO | Y | N | System ROM
// ----+------------+--------+-----------+--------+-------+----+-----------------------
// 2 | 0x00000000 | 4 KB | All | R/W | N | N | Alternate vector base
// 2 | 0x00000000 | 4 KB | All | RO[3] | N | N | Alternate vector base
// ----+------------+--------+-----------+--------+-------+----+-----------------------
// 3 | 0x08000000 | 128 MB | DS, DSd | R/W | N | N | DS Accessory (GBA Cart)
// | 0x03000000 | 8 MB | DSI, DSId | | | | DSi switchable IWRAM
Expand Down Expand Up @@ -58,6 +58,16 @@
// the main RAM can only be accessed from their non-cachable mirror. This isn't
// a problem in a regular retail DSi because it has exactly 16 MB of RAM.
//
// [3]: Normally, access to this region is disabled. If the alternate vector
// base is enabled via setVectorBase(), code and data read access is enabled.
// This allows us to detect null pointer access attempts. However, writes are
// always disabled, as the alternate vector base is written to via the ITCM
// mirror at 0x01000000.
//
// In theory, data fetches could be disabled by using a B opcode to jump to
// (readable) ITCM first, but this would have the side effect of marginally
// slowing down IRQ performance.
//
// Legend:
//
// Access: Data and instruction access permissions (same for privileged and user)
Expand All @@ -72,14 +82,7 @@
#include <nds/arm9/cp15_asm.h>
#include <nds/asminc.h>

#define REGION_IO_REGISTERS 0
#define REGION_SYSTEM_ROM 1
#define REGION_ALT_VECTORS 2
#define REGION_SLOT_2_DSI_IWRAM 3 // DS: Slot-2 | DSi: Switchable IWRAM.
#define REGION_ITCM 4
#define REGION_RAM_UNCACHED 5
#define REGION_RAM_CACHED 6
#define REGION_DTCM 7
#include "mpu_internal.h"

.syntax unified
.arch armv5te
Expand Down Expand Up @@ -269,10 +272,11 @@ setregions:

// Instruction access permission. All regions are RW except for:
// - System ROM. It's read-only.
// - Alternate vector base. See note [3].
// - DTCM. The CPU can´t execute code from here.
ldr r0, =(CP15_AREA_ACCESS_PERMISSIONS_PRW_URW(REGION_IO_REGISTERS) | \
CP15_AREA_ACCESS_PERMISSIONS_PRO_URO(REGION_SYSTEM_ROM) | \
CP15_AREA_ACCESS_PERMISSIONS_PRW_URW(REGION_ALT_VECTORS) | \
CP15_AREA_ACCESS_PERMISSIONS_PNO_UNO(REGION_ALT_VECTORS) | \
CP15_AREA_ACCESS_PERMISSIONS_PRW_URW(REGION_SLOT_2_DSI_IWRAM) | \
CP15_AREA_ACCESS_PERMISSIONS_PRW_URW(REGION_ITCM) | \
CP15_AREA_ACCESS_PERMISSIONS_PRW_URW(REGION_RAM_UNCACHED) | \
Expand All @@ -282,9 +286,10 @@ setregions:

// Data access permission. All regions are RW except for:
// - System ROM. It's read-only.
// - Alternate vector base. See note [3].
ldr r0, =(CP15_AREA_ACCESS_PERMISSIONS_PRW_URW(REGION_IO_REGISTERS) | \
CP15_AREA_ACCESS_PERMISSIONS_PRO_URO(REGION_SYSTEM_ROM) | \
CP15_AREA_ACCESS_PERMISSIONS_PRW_URW(REGION_ALT_VECTORS) | \
CP15_AREA_ACCESS_PERMISSIONS_PNO_UNO(REGION_ALT_VECTORS) | \
CP15_AREA_ACCESS_PERMISSIONS_PRW_URW(REGION_SLOT_2_DSI_IWRAM) | \
CP15_AREA_ACCESS_PERMISSIONS_PRW_URW(REGION_ITCM) | \
CP15_AREA_ACCESS_PERMISSIONS_PRW_URW(REGION_RAM_UNCACHED) | \
Expand Down
24 changes: 21 additions & 3 deletions source/arm9/system/vectorbase.s
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
#include <nds/arm9/cp15_asm.h>
#include <nds/asminc.h>

#include "mpu_internal.h"

.syntax unified
.arch armv5te
.cpu arm946e-s
Expand Down Expand Up @@ -45,19 +47,35 @@ vec_fiq:

BEGIN_ASM_FUNC setVectorBase

// Load the CP15 Control Register
// Set eq/ne flags to alternate vector base false/true.
cmp r0, #0

// Initialize instruction/data access permissions.
mrc CP15_REG5_INSTRUCTION_ACCESS_PERMISSION(r1)

bic r1, r1, #CP15_ACCESS_PERMISSIONS_AREA_MASK(REGION_ALT_VECTORS)
orreq r1, r1, #CP15_AREA_ACCESS_PERMISSIONS_PRO_URO(REGION_ALT_VECTORS)

mcr CP15_REG5_INSTRUCTION_ACCESS_PERMISSION(r1)

mrc CP15_REG5_DATA_ACCESS_PERMISSION(r1)

bic r1, r1, #CP15_ACCESS_PERMISSIONS_AREA_MASK(REGION_ALT_VECTORS)
orreq r1, r1, #CP15_AREA_ACCESS_PERMISSIONS_PRO_URO(REGION_ALT_VECTORS)

mcr CP15_REG5_DATA_ACCESS_PERMISSION(r1)

// Then, set the alternate vector flag.
mrc CP15_REG1_CONTROL_REGISTER(r1)

// if (highVector)
// r1 |= CP15_CONTROL_ALTERNATE_VECTOR_SELECT
// else
// r1 &= ~CP15_CONTROL_ALTERNATE_VECTOR_SELECT

cmp r0, #0
biceq r1, r1, #CP15_CONTROL_ALTERNATE_VECTOR_SELECT
orrne r1, r1, #CP15_CONTROL_ALTERNATE_VECTOR_SELECT

// Store the control register
mcr CP15_REG1_CONTROL_REGISTER(r1)

bx lr

0 comments on commit e9bfb29

Please sign in to comment.