Raw File
patch.h
/* Memory patching */

/* Features:
 * - Keep track of all memory patches
 * - Patch either a single address or an array/matrix of related memory addresses
 * - Undo the patches (built-in undo storage for simple patches, user-supplied storage for array/matrix patches)
 * - Menu display
 */

/* Design goals:
 * - Traceability: be able to see and review all patched addresses from menu
 * - Safety checking: do not patch if the memory contents is not what you expected
 * - Minimally invasive: only lock down cache when there's some ROM address to patch, and unlock it when it's no longer needed
 * - Troubleshooting: automatically check whether the patch is still active or it was overwritten
 * - Versatility: cover a wide range of situations (bit patching, scaling, offset) with the same API
 * 
 * Please do not patch memory directly; use these functions instead (especially for patches that can be undone at runtime).
 * RAM patches applied at boot time can be hardcoded for now (this may change).
 * ROM patches must be always applied via this library.
 * 
 * Long-term goal: any patch that changes Canon functionality should be applied via this library.
 * (including boot patches, state object hooks, any other hooks done by direct memory patching).
 */

#ifndef _patch_h_
#define _patch_h_

#define E_PATCH_OK 0
#define E_PATCH_UNKNOWN_ERROR       0x1
#define E_PATCH_ALREADY_PATCHED     0x2
#define E_PATCH_TOO_MANY_PATCHES    0x4
#define E_PATCH_OLD_VALUE_MISMATCH  0x8
#define E_PATCH_CACHE_COLLISION     0x10
#define E_PATCH_CACHE_ERROR         0x20
#define E_PATCH_REG_NOT_FOUND       0x40

#define E_UNPATCH_OK                0
#define E_UNPATCH_NOT_PATCHED       0x10000
#define E_UNPATCH_OVERWRITTEN       0x20000
#define E_UNPATCH_REG_NOT_FOUND     0x80000

/****************
 * Data patches *
 ****************/

/* simple data patch */
int patch_memory(
    uintptr_t addr,             /* patched address (32 bits) */
    uint32_t old_value,         /* old value before patching (if it differs, the patch will fail) */
    uint32_t new_value,         /* new value */
    const char* description     /* what does this patch do? example: "raw_rec: slowdown dialog timers" */
                                /* note: you must provide storage for the description string */
                                /* a string literal will do; a local variable where you sprintf will not work */
);

/* a more complex patch (e.g. for 16-bit values, or for flipping only some bits) */
int patch_memory_ex(
    uintptr_t addr,             /* patched address (up to 32 bits) */
    uint32_t check_mask,        /* before patching: what bits to check to make sure we are patching the right thing */
    uint32_t check_value,       /* before patching: expected value of the checked bits */
    uint32_t patch_mask,        /* what bits to patch (up to 32) */
    uint32_t patch_scaling,     /* scaling factor for the old value (0x10000 = 1.0; 0 discards the old value, obviously) */
    uint32_t patch_offset,      /* offset added after scaling (this becomes new_value when scaling factor is 0, also obviously) */
    const char* description     /* what does this patch do? example: "mini_iso: patch CMOS[4] to reduce shadow noise" */
);

/* A patch described by mask, scaling and offset is applied as follows (imagine a masked saxpy):
 *      uint32_t old = backup & mask;
 *      uint32_t new = (uint64_t) old * scaling / 0x10000 + offset;
 *      return new & p->patch_mask;
 *
 * Therefore, a simple 32-bit patch has mask=0xFFFFFFFF, scaling=0 and offset=new_value.
 * This trick covers a lot of cases with a minimal description (without too much increase in complexity).
 */

/* patch a linear array of values (all altered in the same way, as described by mask, scaling and offset) */
int patch_memory_array(
    uintptr_t addr,             /* first patched address (up to 32 bits) */
    int num_items,              /* how many items do we have in the array? */
    int item_size,              /* how many bytes until the second patched address? */
    uint32_t check_mask,        /* before patching: what bits to check to make sure we are patching the right thing */
    uint32_t check_value,       /* before patching: expected value of the checked bits */
    uint32_t patch_mask,        /* what bits to patch (up to 32) */
    uint32_t patch_scaling,     /* scaling factor for the old value (0x10000 = 1.0; 0 discards the old value, obviously) */
    uint32_t patch_offset,      /* offset added after scaling (this becomes new_value when scaling factor is 0, also obviously) */
    uint32_t* backup_storage,   /* must be an array with "num_items" items */
    const char* description     /* what does this patch do? example: "dual_iso: patch CMOS[0] gains" */
);

/* patch a matrix of values (all altered in the same way, as described by mask, scaling and offset) */
int patch_memory_matrix(
    uintptr_t addr,             /* first patched address (up to 32 bits) */
    int num_columns,            /* how many columns do we have in the matrix? */
    int col_size,               /* how many bytes until the second column? */
    int num_rows,               /* how many rows do we have in the matrix? */
    int row_size,               /* how many bytes until the second column? */
    uint32_t check_mask,        /* before patching: what bits to check to make sure we are patching the right thing */
    uint32_t check_value,       /* before patching: expected value of the checked bits */
    uint32_t patch_mask,        /* what bits to patch (up to 32) */
    uint32_t patch_scaling,     /* scaling factor for the old value (0x10000 = 1.0; 0 discards the old value, obviously) */
    uint32_t patch_offset,      /* offset added after scaling (this becomes new_value when scaling factor is 0, also obviously) */
    uint32_t* backup_storage,   /* old values; must be a uint32_t[num_items] array; it can be 0 for a 1x1 matrix => internal storage */
    const char* description     /* what does this patch do? example: "mini_iso: scale ADTG gains" */
);

/* undo the patching done by one of the above calls */
int unpatch_memory(uintptr_t addr);

/* patch a ENGIO register in a FFFFFFFF-terminated list */
/* this will also prevent Canon code from changing that register to some other value (*) */
/* (*) this will only work for Canon code that looks up the register in a list, sets the value if found, and does no error checking */
int patch_engio_list(uint32_t * engio_list, uint32_t patched_register, uint32_t patched_value, const char * description);
int unpatch_engio_list(uint32_t * engio_list, uint32_t patched_register);

/******************************
 * Instruction (code) patches *
 ******************************/

/* patch an executable instruction (will clear the instruction cache) */
/* same arguments as patch_memory */
int patch_instruction(
    uintptr_t addr,
    uint32_t old_value,
    uint32_t new_value,
    const char* description
);

/* to undo, use unpatch_memory(addr) */

/* re-apply the ROM (cache) patches */
/* call this after you have flushed the caches, for example */
int reapply_cache_patches();

/*****************
 * Logging hooks *
 *****************/

/* 
 * Hook a custom logging function in the middle of some ASM code
 * similar to GDB hooks, but lighter:
 * - patches only a single address (slightly lower chances of collision)
 * - does not patch anything when the hook is triggered (self-modifying code runs only once, when set up => faster and less stuff that can break)
 * - uses less black magic (easy to understand by ASM noobs like me)
 * - hooking on instructions that do relative addressing is not fully supported; LDR Rn, [PC, #off] is fine (relocated)
 * - regs contain R0-R12 and LR (be careful)
 * - first 4 args of the inspected function are in regs[0] ... regs[3]
 * - next args are in stack[0], stack[1] and so on
 * - pc is the address where we installed the hook
 * - orig_instr is just for sanity checking
 * 
 * credits: Maqs
 */
typedef void (*patch_hook_function_cbr)(uint32_t* regs, uint32_t* stack, uint32_t pc);

/* to be called only from a patch_hook_function_cbr */
#define PATCH_HOOK_CALLER() (regs[13]-4)    /* regs[13] contains LR, not SP */

int patch_hook_function(uintptr_t addr, uint32_t orig_instr, patch_hook_function_cbr logging_function, const char * description);

/* to undo, use unpatch_memory(addr) */

#endif
back to top