Last active
September 22, 2025 02:49
-
-
Save ntpopgetdope/9afcca51d242a34bcafe978539ef0a74 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # Ghidra Add Vita KBL Param DIP Switch Enums | |
| # @category Vita | |
| # @author ntpopgetdope | |
| # @keybinding | |
| # @menupath Vita.Add DIP Switch Enums | |
| # @toolbar | |
| from ghidra.program.model.data import EnumDataType, UnionDataType, DataTypeConflictHandler, CategoryPath | |
| from ghidra.program.model.data import UnsignedIntegerDataType, IntegerDataType | |
| from ghidra.program.model.listing.Function import FunctionUpdateType | |
| from ghidra.program.model.listing import ParameterImpl | |
| from ghidra.program.model.symbol import SourceType | |
| # Retrieved from HENkaku wiki revision 21617 on 21/09/25: | |
| # https://wiki.henkaku.xyz/index.php?title=KBL_Param&oldid=21617#DIP_Switches | |
| # Enum data format: (name, members_dict, category_path) | |
| # Member data format: {name : (dip_value, comment)} | |
| enum_definitions = \ | |
| [ | |
| ( | |
| "sdk_sce_flags_t", { | |
| # https://wiki.henkaku.xyz/vita/KBL_Param#SDK_(SCE)_flags | |
| "SDK_FLAG_MEMORY_SIZE_SWITCH" : (128, "Memory Size. Console Size: 1 - Development Tool Size: 0. (Extended game memory)"), | |
| "SDK_FLAG_RELEASE_CHECK_MODE_CONSOLE" : (129, "Release Mode Console. On: 1 - Off: 0"), | |
| "SDK_FLAG_PLATFORM_EMULATION_DOLCE" : (152, "PS TV Emulation. On: 1 - Off: 0"), | |
| "SDK_FLAG_DEVELOPMENT_MODE" : (159, "Release Check Mode. Development Mode: 1 - Release Mode: 0") | |
| }, | |
| "/Vita/KBL" | |
| ), | |
| ( | |
| "shell_flags_t", { | |
| # https://wiki.henkaku.xyz/vita/KBL_Param#Shell_flags | |
| "SHELL_FLAG_MEMORY_SIZE" : (168, "Memory Size."), | |
| "SHELL_FLAG_ENABLE_EXTRA_TTY" : (184, "Enable extra TTY: On: 1 - Off: 0. (tty7:)"), | |
| "SHELL_FLAG_ENABLE_BOOT_NOTIFICATIONS" : (185, "Enable System Boot Time Notifications: On: 1 - Off: 0"), | |
| "SHELL_FLAG_SHELL_CRON_CALLBACK" : (186, "Related to SceShellCronCallback."), | |
| "SHELL_FLAG_ALLOW_ALL_CORES" : (187, "Allow processes to run on all cores (CPU affinity): On: 1 - Off: 0"), | |
| "SHELL_FLAG_DISABLE_PHY_MEM_DUMMY" : (190, "Disable to ScePhyMemPartShellDummy with PA 0x78000000 0x8000000-bytes."), | |
| "SHELL_FLAG_GROW_PHY_MEM" : (191, "Grow PhyMemPart with PA 0x78000000 0x8000000-bytes. Shell += 48MiB, Shared += 80MiB.") | |
| }, | |
| "/Vita/KBL" | |
| ), | |
| ( | |
| "debug_control_flags_t", { | |
| # https://wiki.henkaku.xyz/vita/KBL_Param#Debug_control_flags | |
| "DEBUG_FLAG_ENABLE_DMAC6" : (192, "Enable Dmac6."), | |
| "DEBUG_FLAG_ENABLE_SDBG_SDIO" : (193, "Enable SDbgSdio, deci4p_sdfmgr, deci4p_sttyp"), | |
| "DEBUG_FLAG_ENABLE_USER_DECI" : (194, "Enable User DECI. Enables Cpup, SceDbgSdio, SceDbgUsb and dtrace module start. Force UnloadProcessModules on Process Delete."), | |
| "DEBUG_FLAG_DISABLE_USB_DEBUG" : (195, "Disable USB Debug. nouse_dbgusb (if enabled, SceDbgUsb does not init)."), | |
| "DEBUG_FLAG_ENABLE_UART0" : (196, "Enable kernel/NSKBL UART0 console logging (if enabled, UART0 is initialized and SceDebug handlers are set to UART0 functions). Or disable remote power control. In 0.920, enable UART3 output."), | |
| "DEBUG_FLAG_ENABLE_UART1" : (197, "Enable kernel/NSKBL UART1 console logging: On: 1 - Off: 0"), | |
| "DEBUG_FLAG_ENABLE_SYSTEM_TTY" : (198, "Enable System TTY: On: 1 - Off: 0. See SceDeci4pSTtyp."), | |
| "DEBUG_FLAG_ENABLE_TTY_STDIO" : (199, "Enable TTY stdio (tty0:): On: 1 - Off: 0"), | |
| "DEBUG_FLAG_STOP_ON_ASSERT" : (200, "Stop when an assertion fails: On: 1 - Off: 0"), | |
| "DEBUG_FLAG_ASSERT_LEVEL_1" : (201, "Set default assertion level to 1: On: 1 - Off: 0. Used in SceSysmem."), | |
| "DEBUG_FLAG_ASSERT_LEVEL_2" : (202, "Set default assertion level to 2: On: 1 - Off: 0. Used in SceSysmem."), | |
| "DEBUG_FLAG_DISABLE_DIPSW_RECONFIG" : (203, "Disable Dipsw Re-Configuration: Disable: 1 - Enable: 0. Used in SKBL. Enabling this will force some Dipsw to be cleared or set by SKBL."), | |
| "DEBUG_FLAG_DEBUG_LEVEL_1" : (204, "Set default debug level to 1: On: 1 - Off: 0. Used in NSKBL and SceSysmem."), | |
| "DEBUG_FLAG_DEBUG_LEVEL_2" : (205, "Set default debug level to 2: On: 1 - Off: 0. Used in NSKBL and SceSysmem."), | |
| "DEBUG_FLAG_ALLOW_SYSCALL_DEBUG" : (206, "Allow syscall debug. Used in SceKernelThreadMgr."), | |
| "DEBUG_FLAG_ENABLE_TOOL_PHYMEMPART" : (210, "SCE_DIPSW_ENABLE_TOOL_PHYMEMPART. Allow Kernel Budget (Enable Devkit 512MiB DRAM): On: 1 - Off: 0"), | |
| "DEBUG_FLAG_ENABLE_USERMODE_UART" : (211, "Enable usermode UART console logging: On: 1 - Off: 0. Enables SceTty2uart. Used in SceCoredump."), | |
| "DEBUG_FLAG_ENABLE_PA_MAPPING" : (212, "Enable PA memory mapping for usermode. Used in NSKBL and SceSysmem, ScePamgr. Works with dipsw 213."), | |
| "DEBUG_FLAG_PA_MAPPING_ADDRESS" : (213, "PA memory mapping address. 1 to use PA 0x78000000 0x8000000-bytes. 0 to use PA 0x80000000 0x20000000-bytes. Used in NSKBL and SceSysmem, ScePamgr. Works with dipsw 212."), | |
| "DEBUG_FLAG_DISABLE_ASLR" : (214, "Disable ASLR: Disabled: 1 - Enabled: 0."), | |
| "DEBUG_FLAG_DISABLE_DECI4P_TRACE" : (215, "Disable DECI4P System Debug process Trace: Disabled: 1 - Enabled: 0."), | |
| "DEBUG_FLAG_WIPE_KERNEL_STACK" : (216, "Wipe kernel stack by 0xFF: On: 1 - Off: 0."), | |
| "DEBUG_FLAG_ENABLE_PATH_LOGGING" : (217, "Enable path logging: On: 1 - Off: 0. Used in SceIofilemgr. If set, SceKernelThreadMgr sets kernel thread stack size to 0x4000-bytes instead of 0x1000-bytes."), | |
| "DEBUG_FLAG_IGNORE_KEYSTONE_ERROR" : (218, "Ignore app keystone error in SceAppMgr: On: 1 - Off: 0."), | |
| "DEBUG_FLAG_ENABLE_MEM_TEST_SP" : (222, "Enable KBL Simple Memory Test over ScePowerScratchPad32KiB: On: 1 - Off: 0. See Physical Memory, SKBL."), | |
| "DEBUG_FLAG_ENABLE_MEM_TEST_SECURE" : (223, "Enable KBL Simple Memory Test over Secure DRAM: On: 1 - Off: 0. See Physical Memory, SKBL.") | |
| }, | |
| "/Vita/KBL" | |
| ), | |
| ( | |
| "system_control_flags_t", { | |
| # https://wiki.henkaku.xyz/vita/KBL_Param#System_control_flags | |
| "SYSTEM_FLAG_ENABLE_SD_CONFIG" : (224, "SCE_DIPSW_PSP2_CONFIG_SD Enable sdbgp's sysmemChecker. Allow plain psp2-config. Allows loading sd0:psp2-config.txt."), | |
| "SYSTEM_FLAG_L2_CACHE_DISABLED" : (225, "L2 Cache Disabled? (0 = L2 Cache ON, 1 = L2 Cache OFF). Used in SceSysmem, NSKBL when doing something with exception stacks"), | |
| "SYSTEM_FLAG_ENABLE_HW_BREAKPOINTS" : (228, "Enables hardware break/watch point. Used in SKBL, SceProcessmgr and SceKernelThreadMgr. SKBL seems to enable/disable unknown devices. SceKernelThreadMgr copies some process info to SceKernelThreadObject related to breakpoints."), | |
| "SYSTEM_FLAG_HDCP_ENABLE" : (229, "HDCP enable/disable. Used in SceAudio."), | |
| "SYSTEM_FLAG_INIT_SD0_UR0" : (230, "Enable initialization of sd0 and ur0. Used in SceExfatfs, ?SceIofilemgr?."), | |
| "SYSTEM_FLAG_INIT_OS0" : (231, "Enable initialization of os0. Used in SceExfatfs, SceIofilemgr."), | |
| "SYSTEM_FLAG_UNKNOWN_232" : (232, "Used in second_loader. DIP Switches 232, 240 and 241 are related."), | |
| "SYSTEM_FLAG_SDSSTOR_FEATURE" : (233, "Used in SceSdstor on System Software version 1.50."), | |
| "SYSTEM_FLAG_GPU_OVERCLOCK" : (236, "GPU overclock. When enabled, GPU and GPU Xbar are overclocked from 111MHz to 166MHz."), | |
| "SYSTEM_FLAG_UNDERCLOCK_OVERCLOCK" : (237, "Probably underclock/overclock related. On FW 0.990 (but not on FW 0.931 nor 3.60), this is the only DIP switch out of range 0-63 that can be set from usermode."), | |
| "SYSTEM_FLAG_UNDERCLOCK" : (238, "Underclock. When enabled, something is underclocked from 222MHz to 111MHz."), | |
| "SYSTEM_FLAG_UNDERCLOCK_OVERCLOCK2" : (239, "Underclock/overclock related."), | |
| "SYSTEM_FLAG_DISABLE_QA_FLAGS" : (240, "Disable QA flags. Used in second_loader. DIP Switches 232, 240 and 241 are related."), | |
| "SYSTEM_FLAG_DISABLE_QA_FLAGS_MASK" : (241, "Disable QA flags 0xD mask 1 and 0xE mask 1. Used in second_loader. DIP Switches 232, 240 and 241 are related."), | |
| "SYSTEM_FLAG_ENABLE_TTY0" : (250, "Enable 'tty0:'"), | |
| "SYSTEM_FLAG_ENABLE_DUMMYTTY0" : (251, "Enable 'dummytty0:'. Also allow sysmodule load from host0: (SceSysmodule debug)."), | |
| "SYSTEM_FLAG_ALLOW_HOST0_ACCESS" : (252, "Allow host0: access. Used in SceSysStateMgr, SceSblFwLoader."), | |
| "SYSTEM_FLAG_ENABLE_CONSOLE_LOGGING" : (253, "Enable some console logging: On: 1 - Off: 0. Used in NSKBL. Related to remote power control in (old?) deci4p_dfctl.skprx") | |
| }, | |
| "/Vita/KBL" | |
| ), | |
| ] | |
| class SceKblDipSwiHelper: | |
| def __init__(self, add_cmts=True): # -> None | |
| # Initial Ghidra state. | |
| self.cmts = add_cmts | |
| self.prog = getCurrentProgram() | |
| self.dtm = self.prog.getDataTypeManager() | |
| # Constants for programmatic type creation. | |
| self.enum_name = "SceKblParamDipSwitches" | |
| self.cat_path = "/Vita/KBL" | |
| # Optional as Ghidra sucks at handling unions. | |
| self.create_opt_union = askYesNo( | |
| "DIP Switch Type Helper", | |
| "Create optional union for DIP switch enums?" | |
| ) | |
| def set_func_proto(self, func_name="ksceKernelCheckDipsw"): # -> None | |
| ''' | |
| SceDipsw: | |
| kernel: false | |
| nid: 0xB36D5922 | |
| functions: | |
| sceKernelCheckDipsw: 0x1C783FB2 <--- | |
| sceKernelClearDipsw: 0x800EDCC1 | |
| sceKernelSetDipsw: 0x817053D4 | |
| SceDipswForDriver: | |
| kernel: true | |
| nid: 0xC9E26388 | |
| functions: | |
| ksceKernelCheckDipsw: 0xA98FC2FD <--- | |
| ksceKernelClearDipsw: 0xF1F3E9FE | |
| ksceKernelGetDipswInfo: 0xB2AD48BE | |
| ksceKernelSetDipsw: 0x82E45FBF | |
| ''' | |
| # Wrap function signature operations in a database transaction for safety. | |
| trx = self.prog.startTransaction("Updating Sce Function Signature") | |
| try: | |
| # Resolve 'ksceKernelCheckDipsw' function by name assuming NID resolution | |
| # has already been performed by the Ghidra loader during auto-analysis... | |
| func = getFunction(func_name) | |
| if func is None: | |
| # Fallback to address. | |
| addr = askAddress( | |
| "Manual Function Address Entry", | |
| "Could not locate '{}' by name. Enter address:".format(func_name), | |
| "81009188" | |
| ) | |
| # Find func containing user supplied addr. | |
| fmgr = self.prog.getFunctionManager() | |
| func = fmgr.getFunctionContaining(addr) | |
| # Check if main enum type already exists. | |
| enum_type = self.dtm.getDataType( | |
| CategoryPath(self.cat_path), # "/Vita/KBL" | |
| self.enum_name # "SceKblParamDipSwitches" | |
| ) | |
| # Ok, try again... | |
| if not enum_type: | |
| self.add_dipsw_types() | |
| # Check if main enum type now exists. | |
| enum_type = self.dtm.getDataType( | |
| CategoryPath(self.cat_path), # "/Vita/KBL" | |
| self.enum_name # "SceKblParamDipSwitches" | |
| ) | |
| # Bail if still missing. | |
| if not enum_type: | |
| print("Could not find enum type: '{}'".format(self.enum_name)) | |
| return | |
| # Given how simple this function is, I doubt the signature has changed | |
| # across different PUPs? Don't care to verify + works on my machine ;) | |
| # https://docs.vitasdk.org/group__SceDipswKernel.html#gae8e772adac10246ef4e57b525218ffaf | |
| # int ksceKernelCheckDipsw(unsigned int bit) | |
| armv7_r0 = self.prog.getLanguage().getRegister("r0") | |
| if armv7_r0 is None: | |
| print("R0 register not found in arch: '{}'".format(self.prog.getLanguageID())) | |
| return | |
| # void <init>(String name, DataType dataType, Register register, Program program, SourceType sourceType) | |
| # https://ghidra.re/ghidra_docs/api/ghidra/program/model/listing/ParameterImpl.html | |
| p0 = ParameterImpl( | |
| "dip_switch", | |
| enum_type, | |
| armv7_r0, | |
| self.prog, | |
| SourceType.ANALYSIS | |
| ) | |
| # Set CC & ensure changes marked as ANALYSIS. | |
| func.setSignatureSource(SourceType.ANALYSIS) | |
| func.setCallingConvention("default") | |
| # Retval 0/1 for DIP switch state. | |
| func.setReturnType( | |
| IntegerDataType.dataType, | |
| SourceType.ANALYSIS | |
| ) | |
| # Replace existing parameter definitions for the target function, reference: | |
| # https://ghidra.re/ghidra_docs/api/ghidra/program/model/listing/Function.html#updateFunction | |
| # https://ghidra.re/ghidra_docs/api/ghidra/program/model/listing/Function.html#replaceParameters | |
| func.replaceParameters( | |
| FunctionUpdateType.DYNAMIC_STORAGE_ALL_PARAMS, | |
| True, | |
| SourceType.ANALYSIS, | |
| p0 | |
| ) | |
| finally: | |
| # Cleanup & end current DB transaction. | |
| self.prog.endTransaction(trx, True) | |
| print("Function signature updated.") | |
| def init_enum_type(self, name, members, category): # -> EnumDataType | |
| # Create CategoryPath to lookup in DTM. | |
| category_path = CategoryPath(category) | |
| # Check if type already exists in DTM & skip creation. | |
| existing_type = self.dtm.getDataType(category_path, name) | |
| if existing_type: | |
| print("Type {} already exists, skipping...".format(name)) | |
| return existing_type | |
| # Create new enum of size 1-byte. | |
| enum_dt = EnumDataType(name, 1) | |
| for member_name, value in members.items(): | |
| cmt = "" # Optional comment. | |
| if isinstance(value, tuple): | |
| value, cmt = value | |
| if not self.cmts: | |
| cmt = "" | |
| elif not isinstance(value, int): | |
| # Only tuple of (val,cmt) or singular int of val expected. skip otherwise. | |
| print("Unexpected enum value type: {}, skipping...".format(type(value))) | |
| continue | |
| # Add member to EnumDataType object. | |
| enum_dt.add(member_name, value, cmt) | |
| # Create type category of specified path if req. | |
| category_obj = self.dtm.getCategory(category_path) | |
| if not category_obj: | |
| category_obj = self.dtm.createCategory(category_path) | |
| # Add newly created enum type into relevant category in current DTM. | |
| category_obj.addDataType(enum_dt, DataTypeConflictHandler.DEFAULT_HANDLER) | |
| print("Created enum: {}".format(name)) | |
| return enum_dt | |
| def add_dipsw_types(self): # -> None | |
| # Wrap type operations in a database transaction for safety. | |
| trx = self.prog.startTransaction("Add Vita KBL DIP Switch Types") | |
| try: | |
| enum_types = {} | |
| all_members = {} | |
| # Add basic types & collate all sub-enum values. | |
| for name, members, category in enum_definitions: | |
| all_members.update(members) | |
| enum_types[name] = \ | |
| self.init_enum_type( | |
| name, | |
| members, | |
| category | |
| ) | |
| # N.B. in the ideal world we would be able to use the union but Ghidra | |
| # refuses to automatically equate & propagate the values, so whatever... | |
| self.init_enum_type( | |
| self.enum_name, | |
| all_members, | |
| self.cat_path | |
| ) | |
| # Early exit to finally block? | |
| if not self.create_opt_union: | |
| return | |
| # Create union if it doesn't exist. | |
| union_name = "{}_u".format(self.enum_name) | |
| union_category_path = CategoryPath(self.cat_path) | |
| existing_union = self.dtm.getDataType(union_category_path, union_name) | |
| if existing_union: | |
| print("Union {} already exists, skipping...".format(union_name)) | |
| else: | |
| # Create new union for DIP switch enums. | |
| union_dt = UnionDataType(union_name) | |
| # Add each sub-enum type as a member of the union. | |
| union_dt.add(enum_types["sdk_sce_flags_t"], "sdk_flags", "") | |
| union_dt.add(enum_types["shell_flags_t"], "shell_flags", "") | |
| union_dt.add(enum_types["debug_control_flags_t"], "debug_flags", "") | |
| union_dt.add(enum_types["system_control_flags_t"], "sysctl_flags", "") | |
| # Create type category of specified path if required. | |
| category_obj = self.dtm.getCategory(union_category_path) | |
| if not category_obj: | |
| category_obj = self.dtm.createCategory(union_category_path) | |
| # Add newly constructed union type into relevant category in current DTM. | |
| category_obj.addDataType(union_dt, DataTypeConflictHandler.DEFAULT_HANDLER) | |
| print("Created union: {}".format(union_name)) | |
| finally: | |
| # Cleanup & end current DB transaction. | |
| self.prog.endTransaction(trx, True) | |
| print("Type creation completed.") | |
| if __name__ == "__main__": | |
| dip = SceKblDipSwiHelper() | |
| dip.add_dipsw_types() | |
| dip.set_func_proto() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment