Skip to content

Instantly share code, notes, and snippets.

@ntpopgetdope
Last active September 22, 2025 02:49
Show Gist options
  • Select an option

  • Save ntpopgetdope/9afcca51d242a34bcafe978539ef0a74 to your computer and use it in GitHub Desktop.

Select an option

Save ntpopgetdope/9afcca51d242a34bcafe978539ef0a74 to your computer and use it in GitHub Desktop.
# 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