diff --git a/build.py b/build.py new file mode 100644 index 0000000..f38bbf9 --- /dev/null +++ b/build.py @@ -0,0 +1,16 @@ + +import os +import subprocess + +PPC_GCC = r"C:\devkitPro\devkitPPC\bin\powerpc-eabi-g++" + +files = [] +for dirname, subdirs, subfiles in os.walk("src"): + for filename in subfiles: + if filename.endswith(".cpp") or filename.endswith(".c") or \ + filename.endswith(".S") or filename.endswith(".s"): + filepath = os.path.join(dirname, filename) + files.append(filepath) + +args = "-O2 -Isrc -fno-exceptions -nostartfiles -T src/link.ld -Wl,-n -Wl,--gc-sections" +subprocess.call("%s %s %s" %(PPC_GCC, args, " ".join(files))) diff --git a/disassemble.py b/disassemble.py new file mode 100644 index 0000000..00ec5a2 --- /dev/null +++ b/disassemble.py @@ -0,0 +1,940 @@ + +trap_condition_table = { + 1: "lgt", + 2: "llt", + 4: "eq", + 5: "lge", + 6: "lle", + 8: "gt", + 12: "ge", + 16: "lt", + 20: "le", + 24: "ne", + 31: "u" +} + +spr_table_suffix = { + 1: "xer", + 8: "lr", + 9: "ctr" +} + +spr_table = {} + +def decodeI(value): + return (value >> 2) & 0xFFFFFF, (value >> 1) & 1, value & 1 + +def decodeB(value): + return (value >> 21) & 0x1F, (value >> 16) & 0x1F, (value >> 2) & 0x3FFF, (value >> 1) & 1, value & 1 + +def decodeD(value): + return (value >> 21) & 0x1F, (value >> 16) & 0x1F, value & 0xFFFF + +def decodeX(value): + return (value >> 21) & 0x1F, (value >> 16) & 0x1F, (value >> 11) & 0x1F, value & 1 + +def decodeABC(value): + return (value >> 21) & 0x1F, (value >> 16) & 0x1F, (value >> 11) & 0x1F, (value >> 6) & 0x1F, value & 1 + +def decodeR(value): + return (value >> 21) & 0x1F, (value >> 16) & 0x1F, (value >> 11) & 0x1F, (value >> 6) & 0x1F, (value >> 1) & 0x1F, value & 1 + +def extend_sign(value, bits=16): + if value & 1 << (bits - 1): + value -= 1 << bits + return value + +def ihex(value): + sign = "-" if value < 0 else "" + return "%s0x%X" %(sign, value) + + +condition_table_true = ["lt", "gt", "eq"] +condition_table_false = ["ge", "le", "ne"] + +def decode_cond_true(BI): + if BI % 4 < 3: + return condition_table_true[BI % 4] + +def decode_cond_false(BI): + if BI % 4 < 3: + return condition_table_false[BI % 4] + +def decode_cond(BO, BI): + ctr = "" + if not BO & 4: + if BO & 2: + ctr = "dz" + else: + ctr = "dnz" + + cond = "" + if not BO & 16: + if BO & 8: + cond = decode_cond_true(BI) + else: + cond = decode_cond_false(BI) + + if cond is not None: + return ctr + cond + + +NO_SWAP = False +SWAP = True + +UNSIGNED = False +SIGNED = True + +def decode_simple(instr): + def decode(value, addr): + D, A, B, Rc = decodeX(value) + if D or A or B or Rc: + return "???" + return instr + return decode + +def decode_triple_reg(instr, swap): + def decode(value, addr): + D, A, B, Rc = decodeX(value) + if swap: + D, A = A, D + return "%s%s" %(instr, "." * Rc), "r%i, r%i, r%i" %(D, A, B) + return decode + +def decode_double_reg(instr, swap): + def decode(value, addr): + D, A, B, Rc = decodeX(value) + if B != 0: + return "???" + if swap: + D, A = A, D + return "%s%s" %(instr, "." * Rc), "r%i, r%i" %(D, A) + return decode + +def decode_single_reg(instr): + def decode(value, addr): + D, A, B, Rc = decodeX(value) + if A or B or Rc: + return "???" + return instr, "r%i" %D + return decode + +def decode_double_reg_imm(instr, sign, swap): + def decode(value, addr): + D, A, IMM = decodeD(value) + if sign: + IMM = extend_sign(IMM) + if swap: + D, A = A, D + return instr, "r%i, r%i, %s" %(D, A, ihex(IMM)) + return decode + +def decode_triple_cr(instr, simple1, simple2): + def decode(value, addr): + D, A, B, pad = decodeX(value) + if pad: + return "???" + if D == A == B and simple1: + return simple1, "cr%i" %D + if A == B and simple2: + return simple2, "cr%i, cr%i" %(D, A) + return instr, "cr%i, cr%i, cr%i" %(D, A, B) + return decode + +def decode_single_cr(instr, rc_ok): + def decode(value, addr): + D, A, B, Rc = decodeX(value) + if A or B or (Rc and not rc_ok): + return "???" + return instr + "." * Rc, "cr%i" %D + return decode + +def decode_compare_reg(instr): + def decode(value, addr): + cr, A, B, pad = decodeX(value) + if cr & 3 or pad: + return "???" + + if cr == 0: + return instr, "r%i, r%i" %(A, B) + return instr, "cr%i, r%i, r%i" %(cr >> 2, A, B) + return decode + +def decode_compare_imm(instr, sign): + def decode(value, addr): + cr, A, IMM = decodeD(value) + if cr & 3: + return "???" + + if sign: + IMM = extend_sign(IMM) + + if cr == 0: + return instr, "r%i, %s" %(A, ihex(IMM)) + return instr, "cr%i, r%i, %s" %(cr >> 2, A, ihex(IMM)) + return decode + +def decode_branch_to_reg(reg): + def decode(value, addr): + BO, BI, pad, LK = decodeX(value) + if pad: + return "???" + + cond = decode_cond(BO, BI) + suffix = "l" * LK + + if cond is None: + return "bc%s%s" %(reg, suffix), "%i, %i" %(BO, BI) + + instr = "b%s%s%s" %(cond, reg, suffix) + if BI >= 4: + return instr, "cr%i" %(BI // 4) + return instr + return decode + +def decode_double_float(instr): + def decode(value, addr): + D, A, B, Rc = decodeX(value) + if A != 0: + return "???" + return "%s%s" %(instr, "." * Rc), "f%i, f%i" %(D, B) + return decode + +def decode_triple_float(instr): + def decode(value, addr): + D, A, B, Rc = decodeX(value) + return "%s%s" %(instr, "." * Rc), "f%i, f%i, f%i" %(D, A, B) + return decode + +def decode_triple_float_alt(instr): + def decode(value, addr): + D, A, B, C, Rc = decodeABC(value) + if B != 0: + return "???" + return "%s%s" %(instr, "." * Rc), "f%i, f%i, f%i" %(D, A, C) + return decode + +def decode_quad_float(instr): + def decode(value, addr): + D, A, B, C, Rc = decodeABC(value) + return "%s%s" %(instr, "." * Rc), "f%i, f%i, f%i, f%i" %(D, A, C, B) + return decode + +def decode_compare_float(instr): + def decode(value, addr): + cr, A, B, pad = decodeX(value) + if cr & 3 or pad: + return "???" + return instr, "cr%i, f%i, f%i" %(cr >> 2, A, B) + return decode + +def decode_memory(instr): + def decode(value, addr): + D, A, d = decodeD(value) + A = extend_sign(A) + return instr, "r%i, %s(r%i)" %(D, ihex(d), A) + return decode + +def decode_memory_indexed(instr): + def decode(value, addr): + D, A, B, Rc = decodeX(value) + if Rc: + return "???" + if A == 0: + return instr, "r%i, 0, r%i" %(D, B) + return instr, "r%i, r%i, r%i" %(D, A, B) + return decode + +def decode_memory_float(instr): + def decode(value, addr): + D, A, d = decodeD(value) + A = extend_sign(A) + return instr, "f%i, %s(r%i)" %(D, ihex(d), A) + return decode + +def decode_memory_float_indexed(instr): + def decode(value, addr): + D, A, B, Rc = decodeX(value) + if Rc: + return "???" + if A == 0: + return instr, "f%i, 0, r%i" %(D, B) + return instr, "f%i, r%i, r%i" %(D, A, B) + return decode + +def decode_cache(instr): + def decode(value, addr): + D, A, B, Rc = decodeX(value) + if D != 0 or Rc != 0: + return "???" + return instr, "r%i, r%i" %(A, B) + return decode + + +add = decode_triple_reg("add", NO_SWAP) +addc = decode_triple_reg("addc", NO_SWAP) +adde = decode_triple_reg("adde", NO_SWAP) + +def addi(value, addr): + D, A, SIMM = decodeD(value) + SIMM = extend_sign(SIMM) + if A == 0: + return "li", "r%i, %s" %(D, ihex(SIMM)) + return "addi", "r%i, r%i, %s" %(D, A, ihex(SIMM)) + +addic = decode_double_reg_imm("addic", SIGNED, NO_SWAP) +addic_ = decode_double_reg_imm("addic.", SIGNED, NO_SWAP) + +def addis(value, addr): + D, A, SIMM = decodeD(value) + SIMM = extend_sign(SIMM) + if A == 0: + return "lis", "r%i, %s" %(D, ihex(SIMM)) + return "addis", "r%i, r%i, %s" %(D, A, ihex(SIMM)) + +addme = decode_double_reg("addme", NO_SWAP) +addze = decode_double_reg("addze", NO_SWAP) + +and_ = decode_triple_reg("and", SWAP) +andc = decode_triple_reg("andc", SWAP) +andi = decode_double_reg_imm("andi.", UNSIGNED, SWAP) +andis = decode_double_reg_imm("andis.", UNSIGNED, SWAP) + +def b(value, addr): + LI, AA, LK = decodeI(value) + LI = extend_sign(LI, 24) * 4 + if AA: dst = LI + else: + dst = addr + LI + return "b%s%s" %("l" * LK, "a" * AA), ihex(dst) + +def bc(value, addr): + BO, BI, BD, AA, LK = decodeB(value) + BD = extend_sign(BD, 14) * 4 + + suffix = "%s%s" %("l" * LK, "a" * AA) + + if AA: dst = BD + else: + dst = addr + BD + + cond = decode_cond(BO, BI) + if cond is None: + return "bc" + suffix, "%i, %i, %s" %(BO, BI, ihex(dst)) + if BI >= 4: + return "b" + cond + suffix, "cr%i, %s" %(BI // 4, ihex(dst)) + return "b" + cond + suffix, ihex(dst) + +bcctr = decode_branch_to_reg("ctr") +bclr = decode_branch_to_reg("lr") + +cmp = decode_compare_reg("cmpw") +cmpi = decode_compare_imm("cmpwi", SIGNED) +cmpl = decode_compare_reg("cmplw") +cmpli = decode_compare_imm("cmplwi", UNSIGNED) + +cntlzw = decode_double_reg("cntlzw", SWAP) + +crand = decode_triple_cr("crand", None, None) +crandc = decode_triple_cr("crandc", None, None) +creqv = decode_triple_cr("creqv", "crset", None) +crnand = decode_triple_cr("crnand", None, None) +crnor = decode_triple_cr("crnor", None, "crnot") +cror = decode_triple_cr("cror", None, "crmove") +crorc = decode_triple_cr("crorc", None, None) +crxor = decode_triple_cr("crxor", "crclr", None) + +dcba = decode_cache("dcba") +dcbf = decode_cache("dcbf") +dcbi = decode_cache("dcbi") +dcbst = decode_cache("dcbst") +dcbt = decode_cache("dcbt") +dcbtst = decode_cache("dcbtst") +dcbz = decode_cache("dcbz") + +divw = decode_triple_reg("divw", NO_SWAP) +divwu = decode_triple_reg("divwu", NO_SWAP) + +eieio = decode_simple("eieio") + +eqv = decode_triple_reg("eqv", SWAP) + +extsb = decode_double_reg("extsb", SWAP) +extsh = decode_double_reg("extsh", SWAP) + +fabs = decode_double_float("fabs") +fadd = decode_triple_float("fadd") +fadds = decode_triple_float("fadds") +fcmpo = decode_compare_float("fcmpo") +fcmpu = decode_compare_float("fcmpu") +fctiw = decode_double_float("fctiw") +fctiwz = decode_double_float("fctiwz") +fdiv = decode_triple_float("fdiv") +fdivs = decode_triple_float("fdivs") +fmadd = decode_quad_float("fmadd") +fmadds = decode_quad_float("fmadds") +fmr = decode_double_float("fmr") +fmsub = decode_quad_float("fmsub") +fmsubs = decode_quad_float("fmsubs") +fmul = decode_triple_float_alt("fmul") +fmuls = decode_triple_float_alt("fmuls") +fnabs = decode_double_float("fnabs") +fneg = decode_double_float("fneg") +fnmadd = decode_quad_float("fnmadd") +fnmadds = decode_quad_float("fnmadds") +fnmsub = decode_quad_float("fnmsub") +fnmsubs = decode_quad_float("fnmsubs") +fres = decode_double_float("fres") +frsp = decode_double_float("frsp") +fsel = decode_quad_float("fsel") +fsqrte = decode_double_float("fsqrte") +fsqrt = decode_double_float("fsqrt") +fsqrts = decode_double_float("fsqrts") +fsub = decode_triple_float("fsub") +fsubs = decode_triple_float("fsubs") + +icbi = decode_cache("icbi") +isync = decode_simple("isync") + +lbz = decode_memory("lbz") +lbzu = decode_memory("lbzu") +lbzux = decode_memory_indexed("lbzux") +lbzx = decode_memory_indexed("lbzx") +lfd = decode_memory_float("lfd") +lfdu = decode_memory_float("lfdu") +lfdux = decode_memory_float_indexed("lfdux") +lfdx = decode_memory_float_indexed("lfdx") +lfs = decode_memory_float("lfs") +lfsu = decode_memory_float("lfsu") +lfsux = decode_memory_float_indexed("lfsux") +lfsx = decode_memory_float_indexed("lfsx") +lha = decode_memory("lha") +lhau = decode_memory("lhau") +lhaux = decode_memory_indexed("lhaux") +lhax = decode_memory_indexed("lhax") +lhbrx = decode_memory_indexed("lhbrx") +lhz = decode_memory("lhz") +lhzu = decode_memory("lhzu") +lhzux = decode_memory_indexed("lhzux") +lhzx = decode_memory_indexed("lhzx") +lmw = decode_memory("lmw") + +def lswi(value, addr): + D, A, NB, Rc = decodeX(value) + if Rc: + return "???" + return "lswi", "r%i, r%i, %i" %(D, A, NB) + +lswx = decode_memory_indexed("lswx") +lwarx = decode_memory_indexed("lwarx") +lwbrx = decode_memory_indexed("lwbrx") +lwz = decode_memory("lwz") +lwzu = decode_memory("lwzu") +lwzux = decode_memory_indexed("lwzux") +lwzx = decode_memory_indexed("lwzx") + +def mcrf(value, addr): + crD, crS, pad1, pad2 = decodeX(value) + if crD & 3 or crS & 3 or pad1 or pad2: + return "???" + return "mcrf", "cr%i, cr%i" %(crD, crS) + +def mcrfs(value, addr): + crD, crS, pad1, pad2 = decodeX(value) + if crD & 3 or crS & 3 or pad1 or pad2: + return "???" + return "mcrfs", "cr%i, cr%i" %(crD, crS) + +mcrxr = decode_single_cr("mcrxr", False) + +mfcr = decode_single_reg("mfcr") + +def mffs(value, addr): + D, A, B, Rc = decodeX(value) + if A or B: + return "???" + return "mffs%s" %("." * Rc), "f%i" %D + +mfmsr = decode_single_reg("mfmsr") + +def mfspr(value, addr): + D, lo, hi, pad = decodeX(value) + spr = (hi << 5) | lo + + if pad: + return "???" + + if spr in spr_table_suffix: + return "mf%s" %spr_table_suffix[spr], "r%i" %D + if spr in spr_table: + return "mfspr", "r%i, %s" %(D, spr_table[spr]) + return "mfspr", "r%i, %i" %(D, spr) + +def mfsr(value, addr): + D, SR, pad1, pad2 = decodeX(value) + if pad1 or pad2 or SR > 15: + return "???" + return "mfsr", "r%i, %i" %(D, SR) + +def mfsrin(value, addr): + D, A, B, Rc = decodeX(value) + if A or Rc: + return "???" + return "mfsrin", "r%i, r%i" %(D, B) + +def mftb(value, addr): + D, lo, hi, pad = decodeX(value) + tbr = (hi << 5) | lo + + if pad: + return "???" + + if tbr == 268: return "mftb", "r%i" %D + if tbr == 269: return "mftbu", "r%i" %D + return "???" + +def mtcrf(value, addr): + S, hi, lo, pad = decodeX(value) + if lo & 1 or hi & 16 or pad: + return "???" + mask = (hi << 4) | (lo >> 1) + if mask == 0xFF: + return "mtcr", "r%i" %S + return "mtcrf", "%s, r%i" %(ihex(mask), S) + +mtfsb0 = decode_single_cr("mtfsb0", True) +mtfsb1 = decode_single_cr("mtfsb1", True) + +def mtfsf(value, addr): + hi, lo, B, Rc = decodeX(value) + if lo & 1 or hi & 16: + return "???" + mask = (hi << 4) | (lo >> 1) + return "mtfsf" + "." * Rc, "%s, f%i" %(ihex(mask), B) + +def mtfsfi(value, addr): + cr, pad, IMM, Rc = decodeX(value) + if cr & 3 or pad or IMM & 1: + return "???" + return "mtfsfi" + "." * Rc, "cr%i, %i" %(cr >> 2, IMM >> 1) + +mtmsr = decode_single_reg("mtmsr") + +def mtspr(value, addr): + S, lo, hi, pad = decodeX(value) + spr = (hi << 5) | lo + + if pad: + return "???" + + if spr in spr_table_suffix: + return "mt%s" %spr_table_suffix[spr], "r%i" %S + if spr in spr_table: + return "mtspr", "%s, r%i" %(spr_table[spr], S) + return "mtspr", "%i, r%i" %(spr, S) + +def mtsr(value, addr): + S, SR, pad1, pad2 = decodeX(value) + if pad1 or pad2 or SR > 15: + return "???" + return "mtsr", "%i, %ri" %(SR, S) + +def mtsrin(value, addr): + S, A, B, Rc = decodeX(value) + if A or Rc: + return "???" + return "mtsrin", "r%i, r%i" %(S, B) + +mulhw = decode_triple_reg("mulhw", NO_SWAP) +mulhwu = decode_triple_reg("mulhwu", NO_SWAP) +mulli = decode_double_reg_imm("mulli", SIGNED, NO_SWAP) +mullwu = decode_triple_reg("mullwu", NO_SWAP) +nand = decode_triple_reg("nand", SWAP) +neg = decode_double_reg("neg", NO_SWAP) + +def nor(value, addr): + S, A, B, Rc = decodeX(value) + if S == B: + return "not" + "." * Rc, "r%i, r%i" %(A, S) + return "nor" + "." * Rc, "r%i, r%i, r%i" %(A, S, B) + +def or_(value, addr): + S, A, B, Rc = decodeX(value) + if S == B: + return "mr" + "." * Rc, "r%i, r%i" %(A, S) + return "or" + "." * Rc, "r%i, r%i, r%i" %(A, S, B) + +orc = decode_triple_reg("orc", SWAP) + +def ori(value, addr): + S, A, UIMM = decodeD(value) + if UIMM == 0: + return "nop" + return "ori", "r%i, r%i, %s" %(A, S, ihex(UIMM)) + +oris = decode_double_reg_imm("oris", UNSIGNED, SWAP) + +rfi = decode_simple("rfi") + +def rlwimi(value, addr): + S, A, SH, MB, ME, Rc = decodeR(value) + suffix = "." * Rc + if SH == 32 - MB and ME >= MB: + return "inslwi" + suffix, "r%i, r%i, %i, %i" %(A, S, ME - MB + 1, MB) + if MB < 31 and SH + ME == 32: + return "insrwi" + suffix, "r%i, r%i, %i, %i" %(A, S, 32 - MB - SH, MB) + return "rlwimi" + suffix, "r%i, r%i, %i, %i, %i" %(A, S, SH, MB, ME) + +def rlwinm(value, addr): + S, A, SH, MB, ME, Rc = decodeR(value) + suffix = "." * Rc + if MB == 0 and ME == 31 - SH: + return "slwi" + suffix, "r%i, r%i, %i" %(A, S, SH) + if ME == 31 and SH == 32 - MB: + return "srwi" + suffix, "r%i, r%i, %i" %(A, S, MB) + if MB == 0 and ME < 31: + return "extlwi" + suffix, "r%i, r%i, %i, %i" %(A, S, ME + 1, SH) + if ME == 31 and SH >= 32 - MB: + return "extrwi" + suffix, "r%i, r%i, %i, %i" %(A, S, 32 - MB, SH + MB - 32) + if MB == 0 and ME == 31: + if SH >= 16: + return "rotlwi" + suffix, "r%i, r%i, %i" %(A, S, SH) + return "rotrwi" + suffix, "r%i, r%i, %i" %(A, S, 32 - SH) + if SH == 0 and ME == 31: + return "clrlwi" + suffix, "r%i, r%i, %i" %(A, S, MB) + if SH == 0 and MB == 0: + return "clrrwi" + suffix, "r%i, r%i, %i" %(A, S, 31 - ME) + if ME == 31 - SH and MB + SH < 32: + return "clrlslwi" + suffix, "r%i, r%i, %i, %i" (MB + SH, SH) + return "rlwinm" + suffix, "r%i, r%i, %i, %i, %i" %(A, S, SH, MB, ME) + +def rlwnm(value, addr): + S, A, B, MB, ME, Rc = decodeR(value) + suffix = "." * Rc + if MB == 0 and ME == 31: + return "rotlw" + suffix, "r%i, r%i, r%i" %(A, S, B) + return "rlwnm" + suffix, "r%i, r%i, r%i, %i, %i" %(A, S, B, MB, ME) + +sc = decode_simple("sc") + +slw = decode_triple_reg("slw", SWAP) +sraw = decode_triple_reg("sraw", SWAP) + +def srawi(value, addr): + S, A, SH, Rc = decodeX(value) + return "srawi" + "." * Rc, "r%i, r%i, %i" %(A, S, SH) + +srw = decode_triple_reg("srw", SWAP) + +stb = decode_memory("stb") +stbu = decode_memory("stbu") +stbux = decode_memory_indexed("stbux") +stbx = decode_memory_indexed("stbx") +stfd = decode_memory_float("stfd") +stfdu = decode_memory_float("stfdu") +stfdux = decode_memory_float_indexed("stfdux") +stfdx = decode_memory_float_indexed("stfdx") +stfiwx = decode_memory_float_indexed("stfiwx") +stfs = decode_memory_float("stfs") +stfsu = decode_memory_float("stfsu") +stfsux = decode_memory_float_indexed("stfsux") +stfsx = decode_memory_float_indexed("stfsx") +sth = decode_memory("sth") +sthbrx = decode_memory_indexed("sthbrx") +sthu = decode_memory("sthu") +sthux = decode_memory_indexed("sthux") +sthx = decode_memory_indexed("sthx") +stmw = decode_memory("stmw") + +def stswi(value, addr): + S, A, NB, Rc = decodeX(value) + if Rc: + return "???" + return "stswi", "r%i, r%i, %i" %(S, A, NB) + +stswx = decode_memory_indexed("stswx") +stw = decode_memory("stw") +stwbrx = decode_memory_indexed("stwbrx") + +def stwcx(value, addr): + S, A, B, Rc = decodeX(value) + if not Rc: + return "???" + return "stwcx.", "r%i, r%i, r%i" %(S, A, B) + +stwu = decode_memory("stwu") +stwux = decode_memory_indexed("stwux") +stwx = decode_memory_indexed("stwx") + +subf = decode_triple_reg("subf", NO_SWAP) +subfc = decode_triple_reg("subfc", NO_SWAP) +subfe = decode_triple_reg("subfe", NO_SWAP) +subfic = decode_double_reg_imm("subfic", SIGNED, NO_SWAP) +subfme = decode_double_reg("subfme", NO_SWAP) +subfze = decode_double_reg("subfze", NO_SWAP) + +sync = decode_simple("sync") +tlbia = decode_simple("tlbia") + +def tlbie(value, addr): + D, A, B, Rc = decodeX(value) + if D or A or Rc: + return "???" + return "tlbie", "r%i" %B + +tlbsync = decode_simple("tlbsync") + +def tw(value, addr): + TO, A, B, Rc = decodeX(value) + if Rc: + return "???" + + if TO == 31 and A == 0 and B == 0: + return "trap" + + if TO in trap_condition_table: + return "tw%s" %trap_condition_table[TO], "r%i, r%i" %(A, B) + return "tw", "%i, r%i, r%i" %(TO, A, B) + +def twi(value, addr): + TO, A, SIMM = decodeD(value) + SIMM = extend_sign(SIMM) + + if TO in trap_condition_table: + return "tw%si" %trap_condition_table[TO], "r%i, %s" %(A, ihex(SIMM)) + return "twi", "%i, r%i, %s" %(TO, A, ihex(SIMM)) + +xor = decode_triple_reg("xor", SWAP) +xori = decode_double_reg_imm("xori", UNSIGNED, SWAP) +xoris = decode_double_reg_imm("xoris", UNSIGNED, SWAP) + + +opcode_table_19 = { + 0: mcrf, + 16: bclr, + 33: crnor, + 50: rfi, + 129: crandc, + 150: isync, + 193: crxor, + 225: crnand, + 257: crand, + 289: creqv, + 417: crorc, + 449: cror, + 528: bcctr +} + +opcode_table_31 = { + 0: cmp, + 4: tw, + 8: subfc, + 10: addc, + 11: mulhwu, + 19: mfcr, + 20: lwarx, + 23: lwzx, + 24: slw, + 26: cntlzw, + 28: and_, + 32: cmpl, + 40: subf, + 54: dcbst, + 55: lwzux, + 60: andc, + 75: mulhw, + 83: mfmsr, + 86: dcbf, + 87: lbzx, + 119: lbzux, + 124: nor, + 138: adde, + 146: mtmsr, + 150: stwcx, + 151: stwx, + 183: stwux, + 202: addze, + 210: mtsr, + 215: stbx, + 234: addme, + 242: mtsrin, + 246: dcbtst, + 247: stbux, + 266: add, + 278: dcbt, + 279: lhzx, + 284: eqv, + 306: tlbie, + 311: lhzux, + 316: xor, + 339: mfspr, + 343: lhax, + 370: tlbia, + 375: lhaux, + 407: sthx, + 412: orc, + 439: sthux, + 444: or_, + 467: mtspr, + 470: dcbi, + 476: nand, + 512: mcrxr, + 533: lswx, + 534: lwbrx, + 535: lfsx, + 536: srw, + 566: tlbsync, + 567: lfsux, + 595: mfsr, + 597: lswi, + 598: sync, + 599: lfdx, + 631: lfdux, + 659: mfsrin, + 661: stswx, + 662: stwbrx, + 663: stfsx, + 695: stfsux, + 725: stswi, + 727: stfdx, + 758: dcba, + 759: stfdux, + 790: lhbrx, + 792: sraw, + 824: srawi, + 854: eieio, + 918: sthbrx, + 922: extsh, + 954: extsb, + 982: icbi, + 983: stfiwx, + 1014: dcbz +} + +opcode_table_59 = { + 18: fdivs, + 20: fsubs, + 21: fadds, + 22: fsqrts, + 24: fres, + 25: fmuls, + 28: fmsubs, + 29: fmadds, + 30: fnmsubs, + 31: fnmadds +} + +opcode_table_63 = { + 0: fcmpu, + 12: frsp, + 14: fctiw, + 15: fctiwz, + 18: fdiv, + 20: fsub, + 21: fadd, + 22: fsqrt, + 23: fsel, + 25: fmul, + 26: fsqrte, + 28: fmsub, + 29: fmadd, + 30: fnmsub, + 31: fnmadd, + 32: fcmpo, + 38: mtfsb1, + 40: fneg, + 64: mcrfs, + 70: mtfsb0, + 72: fmr, + 134: mtfsfi, + 136: fnabs, + 264: fabs, + 583: mffs, + 711: mtfsf +} + +def opcode19(value, addr): + XO = (value >> 1) & 0x3FF + if XO not in opcode_table_19: + return "???" + return opcode_table_19[XO](value, addr) + +def opcode31(value, addr): + XO = (value >> 1) & 0x3FF + if XO not in opcode_table_31: + return "???" + return opcode_table_31[XO](value, addr) + +def opcode59(value, addr): + XO = (value >> 1) & 0x1F + if XO not in opcode_table_59: + return "???" + return opcode_table_59[XO](value, addr) + +def opcode63(value, addr): + XO = (value >> 1) & 0x3FF + if XO not in opcode_table_63: + return "???" + return opcode_table_63[XO](value, addr) + + +opcode_table = { + 3: twi, + 7: mulli, + 8: subfic, + 10: cmpli, + 11: cmpi, + 12: addic, + 13: addic_, + 14: addi, + 15: addis, + 16: bc, + 17: sc, + 18: b, + 19: opcode19, + 20: rlwimi, + 21: rlwinm, + 23: rlwnm, + 24: ori, + 25: oris, + 26: xori, + 27: xoris, + 28: andi, + 29: andis, + 31: opcode31, + 32: lwz, + 33: lwzu, + 34: lbz, + 35: lbzu, + 36: stw, + 37: stwu, + 38: stb, + 39: stbu, + 40: lhz, + 41: lhzu, + 42: lha, + 43: lhau, + 44: sth, + 45: sthu, + 46: lmw, + 47: stmw, + 48: lfs, + 49: lfsu, + 50: lfd, + 51: lfdu, + 52: stfs, + 53: stfsu, + 54: stfd, + 55: stfdu, + 59: opcode59, + 63: opcode63 +} + +def disassemble(value, address): + opcode = value >> 26 + if opcode not in opcode_table: + return "???" + instr = opcode_table[opcode](value, address) + if type(instr) == str: + return instr + return "%-10s%s" %(instr[0], instr[1]) diff --git a/main.py b/main.py new file mode 100644 index 0000000..5aeacd1 --- /dev/null +++ b/main.py @@ -0,0 +1,1225 @@ + +from PyQt5.QtWidgets import * +from PyQt5.QtGui import * +from PyQt5.QtCore import * +import socket +import struct +import string +import sys +import os + +import disassemble + + +""""""""""""""""""""""""""" + Signals +""""""""""""""""""""""""""" + +class EventHolder(QObject): + Connected = pyqtSignal() + Closed = pyqtSignal() + BreakPointChanged = pyqtSignal() + Exception = pyqtSignal(object) + Continue = pyqtSignal(object) + Step = pyqtSignal(object) +events = EventHolder() + + +""""""""""""""""""""""""""" + Debugger client +""""""""""""""""""""""""""" + +class ExceptionState: + + exceptionNames = ["DSI", "ISI", "Program"] + + def __init__(self, message): + #Convert tuple to list to make it mutable + data = message.data + self.gpr = list(struct.unpack_from(">32I", data, 8)) + self.cr, self.lr, self.ctr, self.xer = struct.unpack_from(">4I", data, 0x88) + self.srr0, self.srr1, self.ex0, self.ex1 = struct.unpack_from(">4I", data, 0x98) + self.fpr = list(struct.unpack_from(">32d", data, 0xB8)) + self.gqr = list(struct.unpack_from(">8I", data, 0x1BC)) + self.psf = list(struct.unpack_from(">32d", data, 0x1E0)) + + self.thread = message.arg + + self.exceptionName = self.exceptionNames[message.type] + + self.stepping = False + + def isBreakPoint(self): + return self.exceptionName == "Program" and self.srr1 & 0x20000 + + def isFatal(self): + return not self.isBreakPoint() + + +class Message: + #Server messages + DSI = 0 + ISI = 1 + Program = 2 + + #Client messages + Continue = 0 + Step = 1 + StepOver = 2 + + def __init__(self, type, data, arg): + self.type = type + self.data = data + self.arg = arg + + +class Thread: + + cores = { + 1: "Core 0", + 2: "Core 1", + 4: "Core 2" + } + + def __init__(self, data, offs=0): + self.handle = struct.unpack_from(">I", data, offs)[0] + self.core = self.cores[struct.unpack_from(">I", data, offs + 4)[0]] + self.priority = struct.unpack_from(">I", data, offs + 8)[0] + self.stackBase = struct.unpack_from(">I", data, offs + 12)[0] + self.stackEnd = struct.unpack_from(">I", data, offs + 16)[0] + self.entryPoint = struct.unpack_from(">I", data, offs + 20)[0] + self.namelen = struct.unpack_from(">I", data, offs + 24)[0] + self.name = data[offs + 28 : offs + 28 + self.namelen].decode("shift-jis") + + +class Module: + def __init__(self, data, offs=0): + self.textAddr = struct.unpack_from(">I", data, offs)[0] + self.textSize = struct.unpack_from(">I", data, offs + 4)[0] + self.dataAddr = struct.unpack_from(">I", data, offs + 8)[0] + self.dataSize = struct.unpack_from(">I", data, offs + 12)[0] + self.entryPoint = struct.unpack_from(">I", data, offs + 16)[0] + + self.namelen = struct.unpack_from(">I", data, offs + 20)[0] + self.name = data[offs + 24 : offs + 24 + self.namelen].decode("ascii") + + +COMMAND_CLOSE = 0 +COMMAND_READ = 1 +COMMAND_WRITE = 2 +COMMAND_WRITE_CODE = 3 +COMMAND_GET_MODULE_NAME = 4 +COMMAND_GET_MODULE_LIST = 5 +COMMAND_GET_THREAD_LIST = 6 +COMMAND_GET_STACK_TRACE = 7 +COMMAND_TOGGLE_BREAKPOINT = 8 +COMMAND_POKE_REGISTERS = 9 +COMMAND_RECEIVE_MESSAGES = 10 +COMMAND_SEND_MESSAGE = 11 + +class Debugger: + def __init__(self): + super().__init__() + + self.reset() + + self.messageHandlers = { + Message.DSI: self.handleException, + Message.ISI: self.handleException, + Message.Program: self.handleException + } + + def reset(self): + self.connected = False + self.breakpoints = [] + self.trapEvents = [] + self.crashEvents = [] + self.threads = [] + + def connect(self, host): + self.s = socket.socket() + self.s.settimeout(4) + self.s.connect((host, 1560)) + self.connected = True + self.closeRequest = False + events.Connected.emit() + + def handleClose(self): + self.reset() + events.Closed.emit() + + def close(self): + self.sendbyte(COMMAND_CLOSE) + self.s.close() + self.handleClose() + + def read(self, addr, num): + self.sendbyte(COMMAND_READ) + self.sendall(struct.pack(">II", addr, num)) + data = self.recvall(num) + return data + + def write(self, addr, data): + self.sendbyte(COMMAND_WRITE) + self.sendall(struct.pack(">II", addr, len(data))) + self.sendall(data) + + def writeCode(self, addr, data): + self.sendbyte(COMMAND_WRITE_CODE) + self.sendall(struct.pack(">II", addr, len(data))) + self.sendall(data) + + def writeInstr(self, addr, value): + self.writeCode(addr, struct.pack(">I", value)) + + def getModuleName(self): + self.sendbyte(COMMAND_GET_MODULE_NAME) + length = struct.unpack(">I", self.recvall(4))[0] + return self.recvall(length).decode("ascii") + ".rpx" + + def getModuleList(self): + self.sendbyte(COMMAND_GET_MODULE_LIST) + length = struct.unpack(">I", self.recvall(4))[0] + data = self.recvall(length) + + offset = 0 + modules = [] + while offset < length: + module = Module(data, offset) + modules.append(module) + offset += 24 + len(module.name) + return sorted(modules, key=lambda m: m.textAddr) + + def getThreadList(self): + self.sendbyte(COMMAND_GET_THREAD_LIST) + length = struct.unpack(">I", self.recvall(4))[0] + data = self.recvall(length) + + offset = 0 + self.threads = [] + while offset < length: + thread = Thread(data, offset) + self.threads.append(thread) + offset += 28 + thread.namelen + return self.threads + + def getStackTrace(self, state): + self.sendbyte(COMMAND_GET_STACK_TRACE) + self.sendall(struct.pack(">I", state.thread)) + count = struct.unpack(">I", self.recvall(4))[0] + trace = struct.unpack(">%iI" %count, self.recvall(4 * count)) + return trace + + def toggleBreakPoint(self, addr): + if addr in self.breakpoints: + self.breakpoints.remove(addr) + else: + self.breakpoints.append(addr) + + self.sendbyte(COMMAND_TOGGLE_BREAKPOINT) + self.sendall(struct.pack(">I", addr)) + events.BreakPointChanged.emit() + + def pokeRegisters(self, state): + self.sendbyte(COMMAND_POKE_REGISTERS) + self.sendall(struct.pack(">I", state.thread)) + self.sendall(struct.pack(">32I32d", *state.gpr, *state.fpr)) + + def updateMessages(self): + self.sendbyte(COMMAND_RECEIVE_MESSAGES) + count = struct.unpack(">I", self.recvall(4))[0] + for i in range(count): + type, ptr, length, arg = struct.unpack(">IIII", self.recvall(16)) + data = None + if length: + data = self.recvall(length) + self.messageHandlers[type](Message(type, data, arg)) + + def sendMessage(self, message, data0=0, data1=0, data2=0): + self.sendbyte(COMMAND_SEND_MESSAGE) + self.sendall(struct.pack(">IIII", message, data0, data1, data2)) + + def handleException(self, msg): + state = ExceptionState(msg) + events.Exception.emit(state) + + def continueThread(self, state): self.sendCrashMessage(Message.Continue, state) + def stepInto(self, state): self.sendCrashMessage(Message.Step, state) + def stepOver(self, state): self.sendCrashMessage(Message.StepOver, state) + + def sendCrashMessage(self, message, state): + self.sendMessage(message, state.thread) + if message == Message.Continue: + state.stepping = False + events.Continue.emit(state) + else: + state.stepping = True + events.Step.emit(state) + + def _findThread(self, handle): + for thread in self.threads: + if thread.handle == handle: + return thread + + def findThread(self, handle): + thread = self._findThread(handle) + if not thread: + self.getThreadList() + thread = self._findThread(handle) + return thread + + def sendbyte(self, byte): + self.sendall(bytes([byte])) + + def sendall(self, data): + try: + self.s.sendall(data) + except socket.error: + self.handleClose() + + def recvall(self, num): + try: + data = b"" + while len(data) < num: + chunk = self.s.recv(num - len(data)) + if not chunk: + self.handleClose() + return bytes(num) + data += chunk + except socket.error: + self.handleClose() + return bytes(num) + + return data +debugger = Debugger() + + +""""""""""""""""""""""""""" + Custom widgets +""""""""""""""""""""""""""" + +class HexSpinBox(QAbstractSpinBox): + def __init__(self, stepSize = 1): + super().__init__() + self._value = 0 + self.stepSize = stepSize + + def validate(self, text, pos): + if all([char in "0123456789abcdefABCDEF" for char in text]): + if not text: + return QValidator.Intermediate, text.upper(), pos + + value = int(text, 16) + if value <= 0xFFFFFFFF: + self._value = value + if value % self.stepSize: + self._value -= value % self.stepSize + return QValidator.Acceptable, text.upper(), pos + return QValidator.Acceptable, text.upper(), pos + + return QValidator.Invalid, text.upper(), pos + + def stepBy(self, steps): + self._value = min(max(self._value + steps * self.stepSize, 0), 0x100000000 - self.stepSize) + self.lineEdit().setText("%X" %self._value) + + def stepEnabled(self): + return QAbstractSpinBox.StepUpEnabled | QAbstractSpinBox.StepDownEnabled + + def setValue(self, value): + self._value = value + self.lineEdit().setText("%X" %self._value) + + def value(self): + return self._value + + +""""""""""""""""""""""""""" + Memory widgets +""""""""""""""""""""""""""" + +def format_hex(blob, offs): + return "%02X" %blob[offs] + +def format_ascii(blob, offs): + char = chr(blob[offs]) + if char in string.ascii_letters + string.digits + string.punctuation + " ": + return char + return "?" + +def format_float(blob, offs): + value = struct.unpack_from(">f", blob, offs)[0] + if abs(value) >= 1000000 or 0 < abs(value) < 0.000001: + return "%e" %value + return ("%.8f" %value).rstrip("0") + + +class MemoryViewer(QWidget): + + class Format: + Hex = 0 + Ascii = 1 + Float = 2 + + Width = 1, 1, 4 + Funcs = format_hex, format_ascii, format_float + + def __init__(self): + super().__init__() + + self.layout = QGridLayout() + + for i in range(16): + self.layout.addWidget(QLabel("%X" %i, self), 0, i + 1) + self.addrLabels = [] + for i in range(16): + label = QLabel("%X" %(i * 0x10), self) + self.layout.addWidget(label, i + 1, 0) + self.addrLabels.append(label) + self.dataCells = [] + + self.base = 0 + self.format = self.Format.Hex + self.updateData() + + self.setLayout(self.layout) + + events.Connected.connect(self.connected) + + def connected(self): + self.setBase(0x10000000) + + def setFormat(self, format): + self.format = format + self.updateData() + + def setBase(self, base): + window.mainWidget.tabWidget.memoryTab.memoryInfo.baseBox.setValue(base) + self.base = base + for i in range(16): + self.addrLabels[i].setText("%X" %(self.base + i * 0x10)) + self.updateData() + + def updateData(self): + for cell in self.dataCells: + self.layout.removeWidget(cell) + cell.setParent(None) + + if debugger.connected: + blob = debugger.read(self.base, 0x100) + else: + blob = b"\x00" * 0x100 + + width = self.Width[self.format] + func = self.Funcs[self.format] + for i in range(16 // width): + for j in range(16): + label = QLabel(func(blob, j * 0x10 + i * width), self) + self.layout.addWidget(label, j + 1, i * width + 1, 1, width) + self.dataCells.append(label) + + +class MemoryInfo(QWidget): + def __init__(self): + super().__init__() + self.dataTypeLabel = QLabel("Data type:") + self.dataTypeBox = QComboBox() + self.dataTypeBox.addItems(["Hex", "Ascii", "Float"]) + self.dataTypeBox.currentIndexChanged.connect(self.updateDataType) + + self.baseLabel = QLabel("Address:") + self.baseBox = HexSpinBox(0x10) + self.baseButton = QPushButton("Update", self) + self.baseButton.clicked.connect(self.updateMemoryBase) + + self.pokeAddr = HexSpinBox(4) + self.pokeValue = HexSpinBox() + self.pokeButton = QPushButton("Poke", self) + self.pokeButton.clicked.connect(self.pokeMemory) + + self.layout = QGridLayout() + self.layout.addWidget(self.baseLabel, 0, 0) + self.layout.addWidget(self.baseBox, 0, 1) + self.layout.addWidget(self.baseButton, 0, 2) + self.layout.addWidget(self.pokeAddr, 1, 0) + self.layout.addWidget(self.pokeValue, 1, 1) + self.layout.addWidget(self.pokeButton, 1, 2) + self.layout.addWidget(self.dataTypeLabel, 2, 0) + self.layout.addWidget(self.dataTypeBox, 2, 1, 1, 2) + self.setLayout(self.layout) + + def updateDataType(self, index): + window.mainWidget.tabWidget.memoryTab.memoryViewer.setFormat(index) + + def updateMemoryBase(self): + window.mainWidget.tabWidget.memoryTab.memoryViewer.setBase(self.baseBox.value()) + + def pokeMemory(self): + debugger.write(self.pokeAddr.value(), struct.pack(">I", self.pokeValue.value())) + window.mainWidget.tabWidget.memoryTab.memoryViewer.updateData() + + +class MemoryTab(QWidget): + def __init__(self): + super().__init__() + self.memoryInfo = MemoryInfo() + self.memoryViewer = MemoryViewer() + self.layout = QHBoxLayout() + self.layout.addWidget(self.memoryInfo) + self.layout.addWidget(self.memoryViewer) + self.setLayout(self.layout) + + +""""""""""""""""""""""""""" + Disassembly widgets +""""""""""""""""""""""""""" + +class DisassemblyWidget(QTextEdit): + def __init__(self): + super().__init__() + self.setTextInteractionFlags(Qt.NoTextInteraction) + + self.setMinimumWidth(500) + + self.currentInstruction = None + self.selectedAddress = 0 + self.setBase(0) + + events.Closed.connect(self.updateHighlight) + events.BreakPointChanged.connect(self.updateHighlight) + + def setCurrentInstruction(self, instr): + self.currentInstruction = instr + if instr is not None: + self.setBase(instr - 0x20) + else: + self.updateHighlight() + + def setBase(self, base): + self.base = base + self.updateText() + self.updateHighlight() + + def updateText(self): + if debugger.connected: + blob = debugger.read(self.base, 0x60) + else: + blob = b"\x00" * 0x60 + + text = "" + for i in range(24): + address = self.base + i * 4 + value = struct.unpack_from(">I", blob, i * 4)[0] + instr = disassemble.disassemble(value, address) + text += "%08X: %08X %s\n" %(address, value, instr) + self.setPlainText(text) + + def updateHighlight(self): + selections = [] + for i in range(24): + address = self.base + i * 4 + + color = self.getColor(address) + if color: + cursor = self.textCursor() + cursor.movePosition(QTextCursor.Down, n=i) + cursor.select(QTextCursor.LineUnderCursor) + format = QTextCharFormat() + format.setBackground(QBrush(QColor(color))) + selection = QTextEdit.ExtraSelection() + selection.cursor = cursor + selection.format = format + selections.append(selection) + self.setExtraSelections(selections) + + def getColor(self, addr): + colors = [] + if addr in debugger.breakpoints: + colors.append((255, 0, 0)) + if addr == self.currentInstruction: + colors.append((0, 255, 0)) + if addr == self.selectedAddress: + colors.append((128, 128, 255)) + + if not colors: + return None + + color = [sum(l)//len(colors) for l in zip(*colors)] + return "#%02X%02X%02X" %tuple(color) + + def mousePressEvent(self, e): + super().mousePressEvent(e) + line = self.cursorForPosition(e.pos()).blockNumber() + self.selectedAddress = self.base + line * 4 + if e.button() == Qt.MidButton: + debugger.toggleBreakPoint(self.selectedAddress) + self.updateHighlight() + + +class DisassemblyInfo(QWidget): + def __init__(self): + super().__init__() + self.baseLabel = QLabel("Address:") + self.baseBox = HexSpinBox(4) + self.baseButton = QPushButton("Update", self) + self.baseButton.clicked.connect(self.updateDisassemblyBase) + + self.pokeBox = HexSpinBox() + self.pokeButton = QPushButton("Poke", self) + self.pokeButton.clicked.connect(self.poke) + + self.layout = QGridLayout() + self.layout.addWidget(self.baseLabel, 0, 0) + self.layout.addWidget(self.baseBox, 0, 1) + self.layout.addWidget(self.baseButton, 0, 2) + self.layout.addWidget(self.pokeBox, 1, 0) + self.layout.addWidget(self.pokeButton, 1, 1, 1, 2) + self.setLayout(self.layout) + self.setMinimumWidth(300) + + def updateDisassemblyBase(self): + window.mainWidget.tabWidget.disassemblyTab.disassemblyWidget.setBase(self.baseBox.value()) + + def poke(self): + disassembly = window.mainWidget.tabWidget.disassemblyTab.disassemblyWidget + if disassembly.selectedAddress: + debugger.writeInstr(disassembly.selectedAddress, self.pokeBox.value()) + disassembly.updateText() + + +class DisassemblyTab(QWidget): + def __init__(self): + super().__init__() + self.disassemblyInfo = DisassemblyInfo() + self.disassemblyWidget = DisassemblyWidget() + self.layout = QHBoxLayout() + self.layout.addWidget(self.disassemblyInfo) + self.layout.addWidget(self.disassemblyWidget) + self.setLayout(self.layout) + + events.Connected.connect(self.connected) + + def connected(self): + self.disassemblyWidget.setBase(0x10000000) + + +""""""""""""""""""""""""""" + Thread widgets +""""""""""""""""""""""""""" + +class ThreadList(QTableWidget): + def __init__(self): + super().__init__(0, 5) + self.setHorizontalHeaderLabels(["Name", "Priority", "Core", "Stack", "Entry Point"]) + self.setEditTriggers(self.NoEditTriggers) + self.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) + + events.Connected.connect(self.updateThreads) + + def updateThreads(self): + threads = debugger.getThreadList() + self.setRowCount(len(threads)) + for i in range(len(threads)): + thread = threads[i] + self.setItem(i, 0, QTableWidgetItem(thread.name)) + self.setItem(i, 1, QTableWidgetItem(str(thread.priority))) + self.setItem(i, 2, QTableWidgetItem(thread.core)) + self.setItem(i, 3, QTableWidgetItem("0x%X - 0x%X" %(thread.stackEnd, thread.stackBase))) + self.setItem(i, 4, QTableWidgetItem("0x%X" %thread.entryPoint)) + + +class ThreadingTab(QWidget): + def __init__(self): + super().__init__() + self.threadList = ThreadList() + self.updateButton = QPushButton("Update", self) + self.updateButton.clicked.connect(self.threadList.updateThreads) + self.layout = QVBoxLayout() + self.layout.addWidget(self.threadList) + self.layout.addWidget(self.updateButton) + self.setLayout(self.layout) + + +""""""""""""""""""""""""""" + Module widgets +""""""""""""""""""""""""""" + +class ModuleList(QTableWidget): + def __init__(self): + super().__init__(0, 4) + self.setHorizontalHeaderLabels(["Name", "Code", "Data", "Entry Point"]) + self.setEditTriggers(self.NoEditTriggers) + self.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) + + events.Connected.connect(self.updateModules) + + def updateModules(self): + modules = debugger.getModuleList() + self.setRowCount(len(modules)) + for i in range(len(modules)): + module = modules[i] + textEnd = module.textAddr + module.textSize + dataEnd = module.dataAddr + module.dataSize + self.setItem(i, 0, QTableWidgetItem(module.name)) + self.setItem(i, 1, QTableWidgetItem("%X - %X" %(module.textAddr, textEnd))) + self.setItem(i, 2, QTableWidgetItem("%X - %X" %(module.dataAddr, dataEnd))) + self.setItem(i, 3, QTableWidgetItem("%X" %module.entryPoint)) + + +class ModuleTab(QWidget): + def __init__(self): + super().__init__() + self.moduleList = ModuleList() + self.updateButton = QPushButton("Update", self) + self.updateButton.clicked.connect(self.moduleList.updateModules) + self.layout = QVBoxLayout() + self.layout.addWidget(self.moduleList) + self.layout.addWidget(self.updateButton) + self.setLayout(self.layout) + + +""""""""""""""""""""""""""" + Breakpoint widgets +""""""""""""""""""""""""""" + +class BreakPointList(QListWidget): + def __init__(self): + super().__init__() + self.itemDoubleClicked.connect(self.goToDisassembly) + events.BreakPointChanged.connect(self.updateList) + + def updateList(self): + self.clear() + for bp in debugger.breakpoints: + self.addItem("0x%08X" %bp) + + def goToDisassembly(self, item): + address = debugger.breakpoints[self.row(item)] + window.mainWidget.tabWidget.disassemblyTab.disassemblyWidget.setBase(address) + window.mainWidget.tabWidget.setCurrentIndex(1) + + +class BreakPointTab(QWidget): + def __init__(self): + super().__init__() + self.list = BreakPointList() + self.button = QPushButton("Remove") + self.button.clicked.connect(self.removeBreakPoint) + self.layout = QVBoxLayout() + self.layout.addWidget(self.list) + self.layout.addWidget(self.button) + self.setLayout(self.layout) + + def removeBreakPoint(self): + if self.list.currentRow() != -1: + debugger.toggleBreakPoint(debugger.breakpoints[self.list.currentRow()]) + + +""""""""""""""""""""""""""" + Exception widgets +""""""""""""""""""""""""""" + +class ExceptionThreadList(QTableWidget): + + itemSelected = pyqtSignal(object) + + def __init__(self, title): + super().__init__(0, 1) + + self.setHorizontalHeaderLabels([title]) + + self.setEditTriggers(self.NoEditTriggers) + self.setSelectionMode(QAbstractItemView.NoSelection) + self.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch) + self.verticalHeader().hide() + + self.currentItem = None + self.itemDoubleClicked.connect(self.selectItem) + + self.threads = [] + + events.Closed.connect(self.handleClose) + + def getItem(self, state): + if state.thread in self.threads: + return self.item(self.threads.index(state.thread), 0) + + def addItem(self, state): + if state.thread not in self.threads: + thread = debugger.findThread(state.thread) + if thread and thread.name: + name = thread.name + else: + name = "<%08X>" + item = QTableWidgetItem(name) + item.state = state + self.setRowCount(self.rowCount() + 1) + self.setItem(self.rowCount() - 1, 0, item) + self.threads.append(state.thread) + return item + else: + item = self.getItem(state) + item.state = state + return item + + def removeItem(self, state): + if state.thread in self.threads: + index = self.threads.index(state.thread) + item = self.item(index, 0) + if item == self.currentItem: + self.currentItem = None + self.itemSelected.emit(None) + self.removeRow(self.threads.index(state.thread)) + self.threads.remove(state.thread) + + def selectItem(self, item): + if item != self.currentItem: + self.resetSelection() + item.setBackground(QBrush(QColor(128, 128, 255))) + self.currentItem = item + self.itemSelected.emit(item) + + def resetSelection(self): + if self.currentItem: + self.currentItem.setBackground(QBrush()) + self.currentItem = None + + def handleClose(self): + self.threads = [] + self.currentItem = None + self.clearContents() + self.setRowCount(0) + + +class ExceptionThreads(QWidget): + + itemSelected = pyqtSignal(object) + + def __init__(self): + super().__init__() + + self.pausedThreads = ExceptionThreadList("Paused threads") + self.pausedThreads.itemSelected.connect(self.handlePauseSelect) + self.crashedThreads = ExceptionThreadList("Crashed threads") + self.crashedThreads.itemSelected.connect(self.handleCrashSelect) + + layout = QVBoxLayout(self) + layout.addWidget(self.pausedThreads) + layout.addWidget(self.crashedThreads) + + events.Exception.connect(self.handleException) + events.Continue.connect(self.handleContinue) + + def handlePauseSelect(self, item): + if item: + self.crashedThreads.resetSelection() + self.itemSelected.emit(item) + + def handleCrashSelect(self, item): + if item: + self.pausedThreads.resetSelection() + self.itemSelected.emit(item) + + def handleException(self, state): + if state.isFatal(): + self.pausedThreads.removeItem(state) + item = self.crashedThreads.addItem(state) + self.crashedThreads.selectItem(item) + else: + self.crashedThreads.removeItem(state) + item = self.pausedThreads.addItem(state) + self.pausedThreads.selectItem(item) + + def handleContinue(self, state): + self.pausedThreads.removeItem(state) + self.crashedThreads.removeItem(state) + + +class ExceptionInfo(QGroupBox): + def __init__(self): + super().__init__("Info") + self.typeLabel = QLabel() + + self.layout = QVBoxLayout(self) + self.layout.addWidget(self.typeLabel) + + def setState(self, state): + self.typeLabel.setText("Type: %s" %state.exceptionName) + + +class SpecialRegisters(QGroupBox): + def __init__(self): + super().__init__("Special registers") + self.cr = QLabel() + self.lr = QLabel() + self.ctr = QLabel() + self.xer = QLabel() + self.srr0 = QLabel() + self.srr1 = QLabel() + self.ex0 = QLabel() + self.ex1 = QLabel() + + self.userLayout = QFormLayout() + self.kernelLayout = QFormLayout() + + self.userLayout.addRow("CR:", self.cr) + self.userLayout.addRow("LR:", self.lr) + self.userLayout.addRow("CTR:", self.ctr) + self.userLayout.addRow("XER:", self.xer) + + self.kernelLayout = QFormLayout() + self.kernelLayout.addRow("SRR0:", self.srr0) + self.kernelLayout.addRow("SRR1:", self.srr1) + self.kernelLayout.addRow("EX0:", self.ex0) + self.kernelLayout.addRow("EX1:", self.ex1) + + self.layout = QHBoxLayout(self) + self.layout.addLayout(self.userLayout) + self.layout.addLayout(self.kernelLayout) + + def setState(self, state): + self.cr.setText("%08X" %state.cr) + self.lr.setText("%08X" %state.lr) + self.ctr.setText("%08X" %state.ctr) + self.xer.setText("%08X" %state.xer) + self.srr0.setText("%08X" %state.srr0) + self.srr1.setText("%08X" %state.srr1) + self.ex0.setText("%08X" %state.ex0) + self.ex1.setText("%08X" %state.ex1) + + +class ExceptionInfoTab(QWidget): + def __init__(self): + super().__init__() + self.exceptionInfo = ExceptionInfo() + self.specialRegisters = SpecialRegisters() + + self.layout = QHBoxLayout(self) + self.layout.addWidget(self.exceptionInfo) + self.layout.addWidget(self.specialRegisters) + + def setState(self, state): + self.exceptionInfo.setState(state) + self.specialRegisters.setState(state) + + +class RegisterTab(QWidget): + def __init__(self): + super().__init__() + self.gprLabels = [] + self.gprBoxes = [] + self.fprLabels = [] + self.fprBoxes = [] + for i in range(32): + self.gprLabels.append(QLabel("r%i" %i)) + self.fprLabels.append(QLabel("f%i" % i)) + gprBox = HexSpinBox() + fprBox = QDoubleSpinBox() + fprBox.setRange(float("-inf"), float("inf")) + self.gprBoxes.append(gprBox) + self.fprBoxes.append(fprBox) + + self.layout = QGridLayout(self) + for i in range(32): + self.layout.addWidget(self.gprLabels[i], i % 16, i // 16 * 2) + self.layout.addWidget(self.gprBoxes[i], i % 16, i // 16 * 2 + 1) + self.layout.addWidget(self.fprLabels[i], i % 16, i // 16 * 2 + 4) + self.layout.addWidget(self.fprBoxes[i], i % 16, i // 16 * 2 + 5) + + self.pokeButton = QPushButton("Poke") + self.resetButton = QPushButton("Reset") + self.pokeButton.clicked.connect(self.pokeRegisters) + self.resetButton.clicked.connect(self.updateRegisters) + self.layout.addWidget(self.pokeButton, 16, 0, 1, 4) + self.layout.addWidget(self.resetButton, 16, 4, 1, 4) + + self.setEditEnabled(False) + + events.Step.connect(self.handleStep) + + def setState(self, state): + self.state = state + self.updateRegisters() + self.setEditEnabled(state.isBreakPoint() and not state.stepping) + + def handleStep(self, state): + if state == self.state: + self.setEditEnabled(False) + + def setEditEnabled(self, enabled): + for i in range(32): + self.gprBoxes[i].setEnabled(enabled) + self.fprBoxes[i].setEnabled(enabled) + self.pokeButton.setEnabled(enabled) + self.resetButton.setEnabled(enabled) + + def updateRegisters(self): + for i in range(32): + self.gprBoxes[i].setValue(self.state.gpr[i]) + self.fprBoxes[i].setValue(self.state.fpr[i]) + + def pokeRegisters(self): + for i in range(32): + self.state.gpr[i] = self.gprBoxes[i].value() + self.state.fpr[i] = self.fprBoxes[i].value() + debugger.pokeRegisters(self.state) + + +class StackTrace(QListWidget): + def setState(self, state): + self.clear() + stackTrace = debugger.getStackTrace(state) + for address in (state.srr0, state.lr) + stackTrace: + self.addItem("%X" %address) + +class StackTraceTab(QWidget): + def __init__(self): + super().__init__() + self.stackTrace = StackTrace() + self.disassembly = DisassemblyWidget() + + layout = QHBoxLayout(self) + layout.addWidget(self.stackTrace) + layout.addWidget(self.disassembly) + + self.stackTrace.itemDoubleClicked.connect(self.jumpDisassembly) + + events.Step.connect(self.handleStep) + + def handleStep(self, state): + self.disassembly.setCurrentInstruction(None) + + def setState(self, state): + self.stackTrace.setState(state) + if not state.stepping: + self.disassembly.setCurrentInstruction(state.srr0) + else: + self.disassembly.setCurrentInstruction(None) + + def jumpDisassembly(self, item): + self.disassembly.setCurrentInstruction(int(item.text(), 16)) + + +class ExceptionStateTabs(QTabWidget): + def __init__(self): + super().__init__() + self.infoTab = ExceptionInfoTab() + self.registerTab = RegisterTab() + self.stackTab = StackTraceTab() + self.addTab(self.infoTab, "General") + self.addTab(self.registerTab, "Registers") + self.addTab(self.stackTab, "Stack trace") + + def setState(self, state): + self.infoTab.setState(state) + self.registerTab.setState(state) + self.stackTab.setState(state) + + +class BreakpointActions(QWidget): + def __init__(self): + super().__init__() + + self.continueButton = QPushButton("Continue") + self.continueButton.clicked.connect(self.continueThread) + self.stepButton = QPushButton("Step") + self.stepButton.clicked.connect(self.stepInto) + self.stepOverButton = QPushButton("Step over") + self.stepOverButton.clicked.connect(self.stepOver) + + layout = QHBoxLayout(self) + layout.addWidget(self.continueButton) + layout.addWidget(self.stepButton) + layout.addWidget(self.stepOverButton) + + self.state = None + + def setState(self, state): + self.state = state + if state.isFatal(): + self.continueButton.hide() + self.stepButton.hide() + self.stepOverButton.hide() + else: + self.continueButton.show() + self.stepButton.show() + self.stepOverButton.show() + + def continueThread(self): debugger.continueThread(self.state) + def stepInto(self): debugger.stepInto(self.state) + def stepOver(self): debugger.stepOver(self.state) + + +class ExceptionStateWidget(QTabWidget): + def __init__(self): + super().__init__() + + self.exceptionTabs = ExceptionStateTabs() + self.breakpointActions = BreakpointActions() + + layout = QVBoxLayout(self) + layout.addWidget(self.exceptionTabs) + layout.addWidget(self.breakpointActions) + + events.Step.connect(self.handleStep) + + def setState(self, state): + self.exceptionTabs.setState(state) + self.breakpointActions.setState(state) + self.breakpointActions.setEnabled(not state.stepping) + + def handleStep(self, state): + self.breakpointActions.setEnabled(False) + + +class ExceptionTab(QSplitter): + def __init__(self): + super().__init__() + self.threadList = ExceptionThreads() + self.threadList.itemSelected.connect(self.handleItemSelected) + self.stateWidget = ExceptionStateWidget() + self.stateWidget.setEnabled(False) + self.addWidget(self.threadList) + self.addWidget(self.stateWidget) + + self.setSizes([100, 400]) + + events.Closed.connect(self.handleClose) + + def handleItemSelected(self, item): + if item: + self.stateWidget.setState(item.state) + self.stateWidget.setEnabled(True) + else: + self.stateWidget.setEnabled(False) + + def handleClose(self): + self.stateWidget.setEnabled(False) + + +""""""""""""""""""""""""""" + Main widgets +""""""""""""""""""""""""""" + +class DebuggerTabs(QTabWidget): + def __init__(self): + super().__init__() + self.memoryTab = MemoryTab() + self.disassemblyTab = DisassemblyTab() + self.threadingTab = ThreadingTab() + self.moduleTab = ModuleTab() + self.breakPointTab = BreakPointTab() + self.exceptionTab = ExceptionTab() + self.addTab(self.memoryTab, "Memory") + self.addTab(self.disassemblyTab, "Disassembly") + self.addTab(self.threadingTab, "Threads") + self.addTab(self.moduleTab, "Modules") + self.addTab(self.breakPointTab, "Breakpoints") + self.addTab(self.exceptionTab, "Exceptions") + + events.Exception.connect(self.exceptionOccurred) + events.Connected.connect(self.connected) + events.Closed.connect(self.disconnected) + + def exceptionOccurred(self, state): + self.setCurrentIndex(5) #Exceptions + + def connected(self): + self.setEnabled(True) + + def disconnected(self): + self.setEnabled(False) + + +class StatusWidget(QWidget): + def __init__(self): + super().__init__() + self.serverLabel = QLabel("Wii U IP:") + self.serverBox = QLineEdit(self) + self.serverBox.returnPressed.connect(self.connect) + self.connectButton = QPushButton("Connect", self) + self.connectButton.clicked.connect(self.connect) + self.disconnectButton = QPushButton("Disconnect", self) + self.disconnectButton.clicked.connect(debugger.close) + self.disconnectButton.setEnabled(False) + + self.statusInfo = QLabel("Disconnected", self) + + self.layout = QGridLayout() + self.layout.addWidget(self.serverLabel, 0, 0) + self.layout.addWidget(self.serverBox, 1, 0) + self.layout.addWidget(self.connectButton, 0, 1) + self.layout.addWidget(self.disconnectButton, 1, 1) + self.layout.addWidget(self.statusInfo, 3, 0, 1, 2) + self.setLayout(self.layout) + + events.Connected.connect(self.connected) + events.Closed.connect(self.disconnected) + + def connect(self): + try: + address = self.serverBox.text() + debugger.connect(address) + except: + pass + + def connected(self): + self.statusInfo.setText("Connected") + self.connectButton.setEnabled(False) + self.serverBox.setEnabled(False) + self.disconnectButton.setEnabled(True) + + def disconnected(self): + self.statusInfo.setText("Disconnected") + self.connectButton.setEnabled(True) + self.serverBox.setEnabled(True) + self.disconnectButton.setEnabled(False) + + +class MainWidget(QWidget): + def __init__(self): + super().__init__() + self.tabWidget = DebuggerTabs() + self.statusWidget = StatusWidget() + self.layout = QVBoxLayout() + self.layout.addWidget(self.tabWidget) + self.layout.addWidget(self.statusWidget) + self.tabWidget.setEnabled(False) + self.setLayout(self.layout) + + +class MainWindow(QMainWindow): + def __init__(self): + super().__init__() + self.setWindowTitle("DiiBugger") + self.resize(1080, 720) + + self.mainWidget = MainWidget() + self.setCentralWidget(self.mainWidget) + + self.timer = QTimer(self) + self.timer.setInterval(100) + self.timer.timeout.connect(self.updateMessages) + self.timer.start() + + events.Connected.connect(self.updateTitle) + events.Closed.connect(self.updateTitle) + + def updateTitle(self): + if debugger.connected: + name = debugger.getModuleName() + self.setWindowTitle("DiiBugger - %s" %name) + else: + self.setWindowTitle("DiiBugger") + + def updateMessages(self): + if debugger.connected: + debugger.updateMessages() + + +if __name__ == "__main__": + app = QApplication(sys.argv) + app.setFont(QFontDatabase.systemFont(QFontDatabase.FixedFont)) + window = MainWindow() + window.show() + app.exec() diff --git a/src/cafe/coreinit.cpp b/src/cafe/coreinit.cpp new file mode 100644 index 0000000..69bdcae --- /dev/null +++ b/src/cafe/coreinit.cpp @@ -0,0 +1,248 @@ + +#include "cafe/coreinit.h" +#include "hbl.h" +#include +#include + +int (*OSDynLoad_Acquire)(const char *name, uint32_t *handle); +int (*OSDynLoad_FindExport)(uint32_t handle, bool isData, const char *name, void *ptr); +int (*OSDynLoad_GetModuleName)(uint32_t handle, char *name, int *size); +OSMutex *OSDynLoad_gLoaderLock; + +bool (*OSIsDebuggerInitialized)(); + +void (*exit)(int result); +void (*_Exit)(int result); + +void (*OSFatal)(const char *msg); + +uint32_t (*OSGetSymbolName)(uint32_t addr, char *buffer, size_t bufsize); +void (*DisassemblePPCRange)(uint32_t start, uint32_t end, DisassemblyPrintFn printFunc, DisassemblyFindSymbolFn symFunc, DisassemblyFlags flags); +void (*DisassemblePPCOpcode)(uint32_t addr, void *buffer, size_t bufsize, DisassemblyFindSymbolFn symFunc, DisassemblyFlags flags); + +int (*OSSetExceptionCallback)(OSExceptionType type, OSExceptionCallback callback); +int (*OSSetExceptionCallbackEx)(OSExceptionMode mode, OSExceptionType type, OSExceptionCallback callback); +void (*OSLoadContext)(OSContext *context); + +int (*OSDisableInterrupts)(); +void (*OSRestoreInterrupts)(int state); + +void (*__OSLockScheduler)(void *); +void (*__OSUnlockScheduler)(void *); + +void (*OSInitMutex)(OSMutex *mutex); +void (*OSLockMutex)(OSMutex *mutex); +void (*OSUnlockMutex)(OSMutex *mutex); + +void (*OSInitMessageQueue)(OSMessageQueue *queue, OSMessage *messages, int count); +bool (*OSSendMessage)(OSMessageQueue *queue, OSMessage *message, OSMessageFlags flags); +bool (*OSReceiveMessage)(OSMessageQueue *queue, OSMessage *message, OSMessageFlags flags); + +void (*OSCreateAlarm)(OSAlarm *alarm); +bool (*OSSetAlarm)(OSAlarm *alarm, uint64_t timeout, OSAlarmCallback callback); +void (*OSCancelAlarm)(OSAlarm *alarm); +bool (*OSWaitAlarm)(OSAlarm *alarm); + +OSThread *(*OSGetCurrentThread)(); +OSThread *(*OSGetDefaultThread)(int core); +bool (*OSCreateThread)(OSThread *thread, OSThreadFunc func, int argc, void *argv, void *stack, uint32_t stackSize, int priority, int attr); +int (*OSResumeThread)(OSThread *thread); +bool (*OSJoinThread)(OSThread *thread, int *result); +void (*OSExitThread)(int result); +const char *(*OSGetThreadName)(OSThread *thread); +void (*OSSetThreadName)(OSThread *thread, const char *name); +uint32_t (*OSGetThreadAffinity)(OSThread *thread); +int (*OSGetThreadPriority)(OSThread *thread); + +OSSystemInfo *(*OSGetSystemInfo)(); +void (*OSSleepTicks)(uint64_t ticks); + +uint64_t (*OSGetTitleID)(); + +int (*OSGetMemBound)(OSMemoryType type, uint32_t *startPtr, uint32_t *sizePtr); +uint32_t (*OSEffectiveToPhysical)(uint32_t addr); + +void (*OSScreenInit)(); +uint32_t (*OSScreenGetBufferSizeEx)(int screen); +void (*OSScreenSetBufferEx)(int screen, void *buffer); +void (*OSScreenEnableEx)(int screen, bool enabled); +void (*OSScreenClearBufferEx)(int screen, uint32_t color); +void (*OSScreenFlipBuffersEx)(int screen); +void (*OSScreenPutPixelEx)(int screen, int x, int y, uint32_t color); +void (*OSScreenPutFontEx)(int screen, int x, int y, const char *text); + +void (*DCFlushRange)(const void *buffer, uint32_t length); +void (*DCInvalidateRange)(const void *buffer, uint32_t length); +void (*ICInvalidateRange)(const void *buffer, uint32_t length); + +int (*IOS_Open)(const char *path, int mode); +int (*IOS_Ioctl)(int fd, uint32_t request, const void *inptr, uint32_t inlen, void *outptr, uint32_t outlen); +int (*IOS_Close)(int fd); + +int (*FSInit)(); +int (*FSAddClient)(FSClient *client, uint32_t flags); +int (*FSInitCmdBlock)(FSCmdBlock *block); +int (*FSGetMountSource)(FSClient *client, FSCmdBlock *block, FSMountSourceType type, FSMountSource *source, uint32_t flags); +int (*FSMount)(FSClient *client, FSCmdBlock *block, FSMountSource *source, char *path, uint32_t bytes, uint32_t flags); +int (*FSMakeDir)(FSClient *client, FSCmdBlock *block, const char *path, uint32_t flags); +int (*FSChangeDir)(FSClient *client, FSCmdBlock *block, const char *path, uint32_t flags); +int (*FSGetCwd)(FSClient *client, FSCmdBlock *block, char *path, int length, uint32_t flags); +int (*FSOpenDir)(FSClient *client, FSCmdBlock *block, const char *path, int *handle, uint32_t flags); +int (*FSReadDir)(FSClient *client, FSCmdBlock *block, int handle, FSDirectoryEntry *entry, uint32_t flags); +int (*FSCloseDir)(FSClient *client, FSCmdBlock *block, int handle, uint32_t flags); +int (*FSOpenFile)(FSClient *client, FSCmdBlock *block, const char *path, const char *mode, int *handle, uint32_t flags); +int (*FSGetStatFile)(FSClient *client, FSCmdBlock *block, int handle, FSStat *stat, uint32_t flags); +int (*FSReadFile)(FSClient *client, FSCmdBlock *block, void *buffer, int size, int count, int handle, int flag, uint32_t flags); +int (*FSWriteFile)(FSClient *client, FSCmdBlock *block, const void *buffer, int size, int count, int handle, int flag, uint32_t flags); +int (*FSCloseFile)(FSClient *client, FSCmdBlock *block, int handle, uint32_t flags); +int (*FSGetStat)(FSClient *client, FSCmdBlock *block, const char *path, FSStat *stat, uint32_t flags); +int (*FSRename)(FSClient *client, FSCmdBlock *block, const char *oldPath, const char *newPath, uint32_t flags); +int (*FSRemove)(FSClient *client, FSCmdBlock *block, const char *path, uint32_t flags); +int (*FSDelClient)(FSClient *client, uint32_t flags); +void (*FSShutdown)(); + +int (*MCP_Open)(); +int (*MCP_GetDeviceId)(int handle, uint32_t *deviceId); +int (*MCP_TitleList)(int handle, uint32_t *count, MCPTitleListType *list, uint32_t size); +int (*MCP_TitleListByAppType)(int handle, uint32_t appType, uint32_t *count, MCPTitleListType *list, uint32_t size); +int (*MCP_TitleListByDevice)(int handle, const char *device, uint32_t *count, MCPTitleListType *list, uint32_t size); +void (*MCP_Close)(int handle); + +void (*__KernelGetInfo)(KernelInfoType type, void *buffer, uint32_t size, uint32_t unk); + +int (*snprintf)(char *str, size_t size, const char *format, ...); + +void *(*MEMGetBaseHeapHandle)(int type); +uint32_t (*MEMGetAllocatableSizeForExpHeapEx)(void *handle, int alignment); + +void *(**pMEMAllocFromDefaultHeap)(uint32_t size); +void *(**pMEMAllocFromDefaultHeapEx)(uint32_t size, int alignment); +void (**pMEMFreeToDefaultHeap)(void *ptr); + +OSDynLoad_RPLInfo **pMainRPL; +OSDynLoad_RPLInfo **pFirstRPL; +OSThread **pThreadList; + +void coreinitInitialize() { + *(uint32_t *)&OSDynLoad_Acquire = OS_SPECIFICS->OSDynLoad_Acquire; + *(uint32_t *)&OSDynLoad_FindExport = OS_SPECIFICS->OSDynLoad_FindExport; + + uint32_t handle; + OSDynLoad_Acquire("coreinit.rpl", &handle); + + OSDynLoad_FindExport(handle, false, "OSDynLoad_GetModuleName", &OSDynLoad_GetModuleName); + OSDynLoad_FindExport(handle, true, "OSDynLoad_gLoaderLock", &OSDynLoad_gLoaderLock); + + OSDynLoad_FindExport(handle, false, "OSIsDebuggerInitialized", &OSIsDebuggerInitialized); + + OSDynLoad_FindExport(handle, false, "exit", &exit); + OSDynLoad_FindExport(handle, false, "_Exit", &_Exit); + + OSDynLoad_FindExport(handle, false, "OSFatal", &OSFatal); + + OSDynLoad_FindExport(handle, false, "OSGetSymbolName", &OSGetSymbolName); + OSDynLoad_FindExport(handle, false, "DisassemblePPCRange", &DisassemblePPCRange); + OSDynLoad_FindExport(handle, false, "DisassemblePPCOpcode", &DisassemblePPCOpcode); + + OSDynLoad_FindExport(handle, false, "OSSetExceptionCallback", &OSSetExceptionCallback); + OSDynLoad_FindExport(handle, false, "OSSetExceptionCallbackEx", &OSSetExceptionCallbackEx); + OSDynLoad_FindExport(handle, false, "OSLoadContext", &OSLoadContext); + + OSDynLoad_FindExport(handle, false, "OSDisableInterrupts", &OSDisableInterrupts); + OSDynLoad_FindExport(handle, false, "OSRestoreInterrupts", &OSRestoreInterrupts); + + OSDynLoad_FindExport(handle, false, "__OSLockScheduler", &__OSLockScheduler); + OSDynLoad_FindExport(handle, false, "__OSUnlockScheduler", &__OSUnlockScheduler); + + OSDynLoad_FindExport(handle, false, "OSInitMutex", &OSInitMutex); + OSDynLoad_FindExport(handle, false, "OSLockMutex", &OSLockMutex); + OSDynLoad_FindExport(handle, false, "OSUnlockMutex", &OSUnlockMutex); + + OSDynLoad_FindExport(handle, false, "OSInitMessageQueue", &OSInitMessageQueue); + OSDynLoad_FindExport(handle, false, "OSSendMessage", &OSSendMessage); + OSDynLoad_FindExport(handle, false, "OSReceiveMessage", &OSReceiveMessage); + + OSDynLoad_FindExport(handle, false, "OSCreateAlarm", &OSCreateAlarm); + OSDynLoad_FindExport(handle, false, "OSSetAlarm", &OSSetAlarm); + OSDynLoad_FindExport(handle, false, "OSCancelAlarm", &OSCancelAlarm); + OSDynLoad_FindExport(handle, false, "OSWaitAlarm", &OSWaitAlarm); + + OSDynLoad_FindExport(handle, false, "OSGetCurrentThread", &OSGetCurrentThread); + OSDynLoad_FindExport(handle, false, "OSGetDefaultThread", &OSGetDefaultThread); + OSDynLoad_FindExport(handle, false, "OSCreateThread", &OSCreateThread); + OSDynLoad_FindExport(handle, false, "OSResumeThread", &OSResumeThread); + OSDynLoad_FindExport(handle, false, "OSJoinThread", &OSJoinThread); + OSDynLoad_FindExport(handle, false, "OSExitThread", &OSExitThread); + OSDynLoad_FindExport(handle, false, "OSGetThreadName", &OSGetThreadName); + OSDynLoad_FindExport(handle, false, "OSSetThreadName", &OSSetThreadName); + OSDynLoad_FindExport(handle, false, "OSGetThreadAffinity", &OSGetThreadAffinity); + OSDynLoad_FindExport(handle, false, "OSGetThreadPriority", &OSGetThreadPriority); + + OSDynLoad_FindExport(handle, false, "OSGetSystemInfo", &OSGetSystemInfo); + OSDynLoad_FindExport(handle, false, "OSSleepTicks", &OSSleepTicks); + + OSDynLoad_FindExport(handle, false, "OSGetTitleID", &OSGetTitleID); + + OSDynLoad_FindExport(handle, false, "OSGetMemBound", &OSGetMemBound); + OSDynLoad_FindExport(handle, false, "OSEffectiveToPhysical", &OSEffectiveToPhysical); + + OSDynLoad_FindExport(handle, false, "OSScreenInit", &OSScreenInit); + OSDynLoad_FindExport(handle, false, "OSScreenGetBufferSizeEx", &OSScreenGetBufferSizeEx); + OSDynLoad_FindExport(handle, false, "OSScreenSetBufferEx", &OSScreenSetBufferEx); + OSDynLoad_FindExport(handle, false, "OSScreenEnableEx", &OSScreenEnableEx); + OSDynLoad_FindExport(handle, false, "OSScreenClearBufferEx", &OSScreenClearBufferEx); + OSDynLoad_FindExport(handle, false, "OSScreenFlipBuffersEx", &OSScreenFlipBuffersEx); + OSDynLoad_FindExport(handle, false, "OSScreenPutPixelEx", &OSScreenPutPixelEx); + OSDynLoad_FindExport(handle, false, "OSScreenPutFontEx", &OSScreenPutFontEx); + + OSDynLoad_FindExport(handle, false, "DCFlushRange", &DCFlushRange); + OSDynLoad_FindExport(handle, false, "DCInvalidateRange", &DCInvalidateRange); + OSDynLoad_FindExport(handle, false, "ICInvalidateRange", &ICInvalidateRange); + OSDynLoad_FindExport(handle, false, "IOS_Open", &IOS_Open); + OSDynLoad_FindExport(handle, false, "IOS_Ioctl", &IOS_Ioctl); + OSDynLoad_FindExport(handle, false, "IOS_Close", &IOS_Close); + + OSDynLoad_FindExport(handle, false, "FSInit", &FSInit); + OSDynLoad_FindExport(handle, false, "FSAddClient", &FSAddClient); + OSDynLoad_FindExport(handle, false, "FSInitCmdBlock", &FSInitCmdBlock); + OSDynLoad_FindExport(handle, false, "FSGetMountSource", &FSGetMountSource); + OSDynLoad_FindExport(handle, false, "FSMount", &FSMount); + OSDynLoad_FindExport(handle, false, "FSMakeDir", &FSMakeDir); + OSDynLoad_FindExport(handle, false, "FSChangeDir", &FSChangeDir); + OSDynLoad_FindExport(handle, false, "FSGetCwd", &FSGetCwd); + OSDynLoad_FindExport(handle, false, "FSOpenDir", &FSOpenDir); + OSDynLoad_FindExport(handle, false, "FSReadDir", &FSReadDir); + OSDynLoad_FindExport(handle, false, "FSCloseDir", &FSCloseDir); + OSDynLoad_FindExport(handle, false, "FSOpenFile", &FSOpenFile); + OSDynLoad_FindExport(handle, false, "FSGetStatFile", &FSGetStatFile); + OSDynLoad_FindExport(handle, false, "FSReadFile", &FSReadFile); + OSDynLoad_FindExport(handle, false, "FSWriteFile", &FSWriteFile); + OSDynLoad_FindExport(handle, false, "FSCloseFile", &FSCloseFile); + OSDynLoad_FindExport(handle, false, "FSGetStat", &FSGetStat); + OSDynLoad_FindExport(handle, false, "FSRename", &FSRename); + OSDynLoad_FindExport(handle, false, "FSRemove", &FSRemove); + OSDynLoad_FindExport(handle, false, "FSDelClient", &FSDelClient); + OSDynLoad_FindExport(handle, false, "FSShutdown", &FSShutdown); + + OSDynLoad_FindExport(handle, false, "MCP_Open", &MCP_Open); + OSDynLoad_FindExport(handle, false, "MCP_GetDeviceId", &MCP_GetDeviceId); + OSDynLoad_FindExport(handle, false, "MCP_TitleList", &MCP_TitleList); + OSDynLoad_FindExport(handle, false, "MCP_TitleListByAppType", &MCP_TitleListByAppType); + OSDynLoad_FindExport(handle, false, "MCP_TitleListByDevice", &MCP_TitleListByDevice); + OSDynLoad_FindExport(handle, false, "MCP_Close", &MCP_Close); + + OSDynLoad_FindExport(handle, false, "__KernelGetInfo", &__KernelGetInfo); + + OSDynLoad_FindExport(handle, false, "__os_snprintf", &snprintf); + + OSDynLoad_FindExport(handle, false, "MEMGetBaseHeapHandle", &MEMGetBaseHeapHandle); + OSDynLoad_FindExport(handle, false, "MEMGetAllocatableSizeForExpHeapEx", &MEMGetAllocatableSizeForExpHeapEx); + + OSDynLoad_FindExport(handle, true, "MEMAllocFromDefaultHeap", &pMEMAllocFromDefaultHeap); + OSDynLoad_FindExport(handle, true, "MEMAllocFromDefaultHeapEx", &pMEMAllocFromDefaultHeapEx); + OSDynLoad_FindExport(handle, true, "MEMFreeToDefaultHeap", &pMEMFreeToDefaultHeap); + + pMainRPL = (OSDynLoad_RPLInfo **)0x10081014; + pFirstRPL = (OSDynLoad_RPLInfo **)0x10081018; + pThreadList = (OSThread **)0x100567F8; +} diff --git a/src/cafe/coreinit.h b/src/cafe/coreinit.h new file mode 100644 index 0000000..c63376b --- /dev/null +++ b/src/cafe/coreinit.h @@ -0,0 +1,513 @@ + +#pragma once + +#include +#include + +// Timers +#define OSTimerClockSpeed ((OSGetSystemInfo()->busClockSpeed) / 4) + +#define OSSecondsToTicks(val) ((uint64_t)(val) * (uint64_t)OSTimerClockSpeed) +#define OSMillisecondsToTicks(val) (((uint64_t)(val) * (uint64_t)OSTimerClockSpeed) / 1000ull) + +#define OSTicksToSeconds(val) ((uint64_t)(val) / (uint64_t)OSTimerClockSpeed) +#define OSTicksToMilliseconds(val) (((uint64_t)(val) * 1000ull) / (uint64_t)OSTimerClockSpeed) + +// Memory +enum OSMemoryType { + MEM1 = 1, + MEM2 = 2 +}; + +// System +enum KernelInfoType { + TITLE_INFO = 0, + SYSTEM_INFO = 1, + PLATFORM_INFO = 2, + + KERNEL_STATISTICS = 4, + PERFORMANCE_NUMBERS = 5, + + PROCESS_INFO = 8, + + CRASH_INFO = 11, + APP_CRASH_CONTROL = 12, + COS_REPORT_MASKS = 13, + CRASH_RECOVERY = 14, + CRASH_DETAIL_LEVEL = 15, + CRASH_DUMP_TYPE = 16, + SHUTDOWN_REASON = 17, + WRITE_GATHER_REGS = 18, + PROC_DATA_BOUNDS = 19 +}; + +struct OSSystemInfo { + uint32_t busClockSpeed; + uint32_t coreClockSpeed; + int64_t baseTime; + char _10[0x10]; +}; + +struct OSTitleInfo { + char _0[0xC]; + uint32_t ramStart; + uint32_t ramEnd; + char _14[0x20]; + uint32_t systemHeapSize; + char _38[0x40]; + uint32_t textStart; + uint32_t _7C; + uint32_t textSize; + uint32_t dataStart; + uint32_t _88; + uint32_t dataSize; + uint32_t loadStart; + uint32_t _94; + uint32_t loadSize; + char _9C[0xC]; +}; + +struct OSTitleInfoEx { + OSTitleInfo info; + uint64_t osVersionId; +}; + +// OSDynLoad +struct OSDynLoad_NotifyData { + const char *path; + uint32_t textAddr; + uint32_t textOffset; + uint32_t textSize; + uint32_t dataAddr; + uint32_t dataOffset; + uint32_t dataSize; + uint32_t readAddr; + uint32_t readOffset; + uint32_t readSize; +}; + +struct OSDynLoad_RPLInfo { + uint32_t handle; + uint32_t _4; + const char *name; + char _C[0x1C]; + OSDynLoad_NotifyData *notifyData; + void *entryPoint; + char _30[0x24]; + OSDynLoad_RPLInfo *next; + char _58[0x3C]; +}; + +// Thread / mutex / context +struct OSThread; +struct OSMutex; +struct OSAlarm; + +struct OSContext { + uint64_t tag; + + uint32_t gpr[32]; + + uint32_t cr; + uint32_t lr; + uint32_t ctr; + uint32_t xer; + + uint32_t srr0; + uint32_t srr1; + + uint32_t dsisr; + uint32_t dar; + + char _A8[0xC]; + + uint32_t fpscr; + double fpr[32]; + + uint16_t spinLockCount; + uint16_t state; + + uint32_t gqr[8]; + + uint32_t _1DC; + + double psf[32]; + + uint64_t coretime[3]; + uint64_t starttime; + + uint32_t error; + + uint32_t _304; + + uint32_t pmc1; + uint32_t pmc2; + uint32_t pmc3; + uint32_t pmc4; + uint32_t mmcr0; + uint32_t mmcr1; +}; + +struct OSThreadQueue { + OSThread *head; + OSThread *tail; + void *parent; + uint32_t _C; +}; + +struct OSThreadLink { + OSThread *next; + OSThread *prev; +}; + +struct OSMutexLink { + OSMutex *next; + OSMutex *prev; +}; + +struct OSMutexQueue { + OSMutex *head; + OSMutex *tail; + void *parent; + uint32_t _C; +}; + +struct OSMutex { + uint32_t tag; + const char *name; + uint32_t _8; + + OSThreadQueue queue; + OSThread *thread; + int count; + OSMutexLink link; +}; + +typedef int (*OSThreadFunc)(int argc, void *argv); + +struct OSThread { + OSContext context; + + uint32_t tag; + + uint8_t state; + uint8_t attr; + uint16_t id; + uint32_t suspendCounter; + + int priority; + int basePriority; + + int exitValue; + + char _338[0x24]; + + OSThreadQueue *queue; + OSThreadLink link; + + OSThreadQueue joinQueue; + + OSMutex *mutex; + OSMutexQueue mutexQueue; + + OSThreadLink activeLink; + + void *stackBase; + void *stackEnd; + + OSThreadFunc entryPoint; + + char _3A0[0x57C - 0x3A0]; + + void *specific[0x10]; + + int type; + + const char *name; + + char _5C4[0x6A0 - 0x5C4]; + +}; + +// Messages +enum OSMessageFlags { + OS_MESSAGE_FLAGS_NONE = 0, + OS_MESSAGE_FLAGS_BLOCKING = 1 +}; + +struct OSMessage { + uint32_t message; + uint32_t args[3]; +}; + +struct OSMessageQueue { + uint32_t tag; + const char *name; + uint32_t _8; + OSThreadQueue sendQueue; + OSThreadQueue recvQueue; + OSMessage *messages; + uint32_t size; + uint32_t first; + uint32_t used; +}; + +// Alarms +struct OSAlarmQueue { + uint32_t tag; + const char *name; + uint32_t _8; + + OSThreadQueue threadQueue; + OSAlarm *head; + OSAlarm *tail; +}; + +struct OSAlarmLink { + OSAlarm *prev; + OSAlarm *next; +}; + +typedef void (*OSAlarmCallback)(OSAlarm *alarm, OSContext *context); + +struct OSAlarm { + uint32_t tag; + const char *name; + uint32_t _8; + OSAlarmCallback callback; + uint32_t group; + uint32_t _14; + uint64_t nextFire; + OSAlarmLink link; + uint64_t period; + uint64_t start; + void *userData; + uint32_t state; + OSThreadQueue threadQueue; + OSAlarmQueue *alarmQueue; + OSContext *context; +}; + +// PPC disassembly +typedef void (*DisassemblyPrintFn)(const char *fmt, ...); +typedef uint32_t (*DisassemblyFindSymbolFn)(uint32_t addr, char *buffer, size_t bufsize); + +enum DisassemblyFlags { + DISASSEMBLY_FLAGS_NONE = 0, + DISASSEMBLY_FLAGS_SIMPLIFY = 1, + DISASSEMBLY_FLAGS_SPACE = 0x20, + DISASSEMBLY_FLAGS_PLAIN = 0x40, + DISASSEMBLY_FLAGS_NO_OPCODE = 0x80, + DISASSEMBLY_FLAGS_PRINT_SYMBOLS = 0x100 +}; + +// Exceptions +enum OSExceptionMode { + OS_EXCEPTION_MODE_THREAD = 1, + OS_EXCEPTION_MODE_GLOBAL = 2, + OS_EXCEPTION_MODE_THREAD_ALL_CORES = 3, + OS_EXCEPTION_MODE_GLOBAL_ALL_CORES = 4 +}; + +enum OSExceptionType { + OS_EXCEPTION_TYPE_DSI = 2, + OS_EXCEPTION_TYPE_ISI = 3, + OS_EXCEPTION_TYPE_PROGRAM = 6 +}; + +typedef bool (*OSExceptionCallback)(OSContext *context); + +// File system +enum FSMode { + FS_MODE_READ_OWNER = 0x400, + FS_MODE_WRITE_OWNER = 0x200, + FS_MODE_EXEC_OWNER = 0x100, + + FS_MODE_READ_GROUP = 0x040, + FS_MODE_WRITE_GROUP = 0x020, + FS_MODE_EXEC_GROUP = 0x010, + + FS_MODE_READ_OTHER = 0x004, + FS_MODE_WRITE_OTHER = 0x002, + FS_MODE_EXEC_OTHER = 0x001 +}; + +enum FSStatFlags { + FS_STAT_DIRECTORY = 0x80000000 +}; + +enum FSMountSourceType { + FS_MOUNT_SOURCE_SD = 0 +}; + +struct FSClient { + char data[0x1700]; +}; + +struct FSCmdBlock { + char data[0xA80]; +}; + +struct FSMountSource { + char data[0x300]; +}; + +struct __attribute__((packed)) FSStat { + FSStatFlags flags; + FSMode mode; + uint32_t owner; + uint32_t group; + uint32_t size; + char _14[0xC]; + uint32_t entryId; + int64_t created; + int64_t modified; + char _34[0x30]; +}; + +struct FSDirectoryEntry { + FSStat info; + char name[256]; +}; + +// MCP +struct MCPTitleListType { + uint64_t titleId; + uint32_t _4; + char path[56]; + uint32_t appType; + char _48[0xC]; + uint8_t device; + char _55; + char indexedDevice[10]; + uint8_t _60; +}; + +// Function pointers +extern int (*OSDynLoad_Acquire)(const char *name, uint32_t *handle); +extern int (*OSDynLoad_FindExport)(uint32_t handle, bool isData, const char *name, void *ptr); +extern int (*OSDynLoad_GetModuleName)(uint32_t handle, char *name, int *size); +extern OSMutex *OSDynLoad_gLoaderLock; + +extern bool (*OSIsDebuggerInitialized)(); + +extern void (*exit)(int result); +extern void (*_Exit)(int result); + +extern void (*OSFatal)(const char *msg); + +extern uint32_t (*OSGetSymbolName)(uint32_t addr, char *buffer, size_t bufsize); +extern void (*DisassemblePPCRange)(uint32_t start, uint32_t end, DisassemblyPrintFn printFunc, DisassemblyFindSymbolFn symFunc, DisassemblyFlags flags); +extern void (*DisassemblePPCOpcode)(uint32_t addr, void *buffer, size_t bufsize, DisassemblyFindSymbolFn symFunc, DisassemblyFlags flags); + +extern int (*OSSetExceptionCallback)(OSExceptionType type, OSExceptionCallback callback); +extern int (*OSSetExceptionCallbackEx)(OSExceptionMode mode, OSExceptionType type, OSExceptionCallback callback); +extern void (*OSLoadContext)(OSContext *context); + +extern int (*OSDisableInterrupts)(); +extern void (*OSRestoreInterrupts)(int state); + +extern void (*__OSLockScheduler)(void *); +extern void (*__OSUnlockScheduler)(void *); + +extern void (*OSInitMutex)(OSMutex *mutex); +extern void (*OSLockMutex)(OSMutex *mutex); +extern void (*OSUnlockMutex)(OSMutex *mutex); + +extern void (*OSInitMessageQueue)(OSMessageQueue *queue, OSMessage *messages, int count); +extern bool (*OSSendMessage)(OSMessageQueue *queue, OSMessage *message, OSMessageFlags flags); +extern bool (*OSReceiveMessage)(OSMessageQueue *queue, OSMessage *message, OSMessageFlags flags); + +extern void (*OSCreateAlarm)(OSAlarm *alarm); +extern bool (*OSSetAlarm)(OSAlarm *alarm, uint64_t timeout, OSAlarmCallback callback); +extern void (*OSCancelAlarm)(OSAlarm *alarm); +extern bool (*OSWaitAlarm)(OSAlarm *alarm); + +extern OSThread *(*OSGetCurrentThread)(); +extern OSThread *(*OSGetDefaultThread)(int core); +extern bool (*OSCreateThread)(OSThread *thread, OSThreadFunc func, int argc, void *argv, void *stack, uint32_t stackSize, int priority, int attr); +extern int (*OSResumeThread)(OSThread *thread); +extern bool (*OSJoinThread)(OSThread *thread, int *result); +extern void (*OSExitThread)(int result); +extern const char *(*OSGetThreadName)(OSThread *thread); +extern void (*OSSetThreadName)(OSThread *thread, const char *name); +extern uint32_t (*OSGetThreadAffinity)(OSThread *thread); +extern int (*OSGetThreadPriority)(OSThread *thread); + +extern OSSystemInfo *(*OSGetSystemInfo)(); +extern void (*OSSleepTicks)(uint64_t ticks); + +extern uint64_t (*OSGetTitleID)(); + +extern int (*OSGetMemBound)(OSMemoryType type, uint32_t *startPtr, uint32_t *sizePtr); +extern uint32_t (*OSEffectiveToPhysical)(uint32_t addr); + +extern void (*OSScreenInit)(); +extern uint32_t (*OSScreenGetBufferSizeEx)(int screen); +extern void (*OSScreenSetBufferEx)(int screen, void *buffer); +extern void (*OSScreenEnableEx)(int screen, bool enabled); +extern void (*OSScreenClearBufferEx)(int screen, uint32_t color); +extern void (*OSScreenFlipBuffersEx)(int screen); +extern void (*OSScreenPutPixelEx)(int screen, int x, int y, uint32_t color); +extern void (*OSScreenPutFontEx)(int screen, int x, int y, const char *text); + +extern void (*DCFlushRange)(const void *buffer, uint32_t length); +extern void (*DCInvalidateRange)(const void *buffer, uint32_t length); +extern void (*ICInvalidateRange)(const void *buffer, uint32_t length); + +extern int (*IOS_Open)(const char *path, int mode); +extern int (*IOS_Ioctl)(int fd, uint32_t request, const void *inptr, uint32_t inlen, void *outptr, uint32_t outlen); +extern int (*IOS_Close)(int fd); + +extern int (*FSInit)(); +extern int (*FSAddClient)(FSClient *client, uint32_t flags); +extern int (*FSInitCmdBlock)(FSCmdBlock *block); +extern int (*FSGetMountSource)(FSClient *client, FSCmdBlock *block, FSMountSourceType type, FSMountSource *source, uint32_t flags); +extern int (*FSMount)(FSClient *client, FSCmdBlock *block, FSMountSource *source, char *path, uint32_t bytes, uint32_t flags); +extern int (*FSMakeDir)(FSClient *client, FSCmdBlock *block, const char *path, uint32_t flags); +extern int (*FSChangeDir)(FSClient *client, FSCmdBlock *block, const char *path, uint32_t flags); +extern int (*FSGetCwd)(FSClient *client, FSCmdBlock *block, char *path, int length, uint32_t flags); +extern int (*FSOpenDir)(FSClient *client, FSCmdBlock *block, const char *path, int *handle, uint32_t flags); +extern int (*FSReadDir)(FSClient *client, FSCmdBlock *block, int handle, FSDirectoryEntry *entry, uint32_t flags); +extern int (*FSCloseDir)(FSClient *client, FSCmdBlock *block, int handle, uint32_t flags); +extern int (*FSOpenFile)(FSClient *client, FSCmdBlock *block, const char *path, const char *mode, int *handle, uint32_t flags); +extern int (*FSGetStatFile)(FSClient *client, FSCmdBlock *block, int handle, FSStat *stat, uint32_t flags); +extern int (*FSReadFile)(FSClient *client, FSCmdBlock *block, void *buffer, int size, int count, int handle, int flag, uint32_t flags); +extern int (*FSWriteFile)(FSClient *client, FSCmdBlock *block, const void *buffer, int size, int count, int handle, int flag, uint32_t flags); +extern int (*FSCloseFile)(FSClient *client, FSCmdBlock *block, int handle, uint32_t flags); +extern int (*FSGetStat)(FSClient *client, FSCmdBlock *block, const char *path, FSStat *stat, uint32_t flags); +extern int (*FSRename)(FSClient *client, FSCmdBlock *block, const char *oldPath, const char *newPath, uint32_t flags); +extern int (*FSRemove)(FSClient *client, FSCmdBlock *block, const char *path, uint32_t flags); +extern int (*FSDelClient)(FSClient *client, uint32_t flags); +extern void (*FSShutdown)(); + +extern int (*MCP_Open)(); +extern int (*MCP_GetDeviceId)(int handle, uint32_t *deviceId); +extern int (*MCP_TitleList)(int handle, uint32_t *count, MCPTitleListType *list, uint32_t size); +extern int (*MCP_TitleListByAppType)(int handle, uint32_t appType, uint32_t *count, MCPTitleListType *list, uint32_t size); +extern int (*MCP_TitleListByDevice)(int handle, const char *device, uint32_t *count, MCPTitleListType *list, uint32_t size); +extern void (*MCP_Close)(int handle); + +extern void (*__KernelGetInfo)(KernelInfoType type, void *buffer, uint32_t size, uint32_t unk); + +extern int (*snprintf)(char *str, size_t size, const char *format, ...); + +extern void *(*MEMGetBaseHeapHandle)(int type); +extern uint32_t (*MEMGetAllocatableSizeForExpHeapEx)(void *handle, int alignment); + +extern void *(**pMEMAllocFromDefaultHeap)(uint32_t size); +extern void *(**pMEMAllocFromDefaultHeapEx)(uint32_t size, int alignment); +extern void (**pMEMFreeToDefaultHeap)(void *ptr); +#define MEMAllocFromDefaultHeap (*pMEMAllocFromDefaultHeap) +#define MEMAllocFromDefaultHeapEx (*pMEMAllocFromDefaultHeapEx) +#define MEMFreeToDefaultHeap (*pMEMFreeToDefaultHeap) + +// Internal +extern OSDynLoad_RPLInfo **pMainRPL; +extern OSDynLoad_RPLInfo **pFirstRPL; +extern OSThread **pThreadList; +#define MainRPL (*pMainRPL) +#define FirstRPL (*pFirstRPL) +#define ThreadList (*pThreadList) + +void coreinitInitialize(); diff --git a/src/cafe/nn_act.cpp b/src/cafe/nn_act.cpp new file mode 100644 index 0000000..e71b22d --- /dev/null +++ b/src/cafe/nn_act.cpp @@ -0,0 +1,20 @@ + +#include "cafe/coreinit.h" +#include + +namespace nn::act { + uint32_t (*Initialize)(); + uint8_t (*GetSlotNo)(); + uint32_t (*GetPersistentIdEx)(uint8_t slot); + uint32_t (*Finalize)(); +} + +void nnactInitialize() { + uint32_t handle; + OSDynLoad_Acquire("nn_act.rpl", &handle); + + OSDynLoad_FindExport(handle, false, "Initialize__Q2_2nn3actFv", &nn::act::Initialize); + OSDynLoad_FindExport(handle, false, "GetSlotNo__Q2_2nn3actFv", &nn::act::GetSlotNo); + OSDynLoad_FindExport(handle, false, "GetPersistentIdEx__Q2_2nn3actFUc", &nn::act::GetPersistentIdEx); + OSDynLoad_FindExport(handle, false, "Finalize__Q2_2nn3actFv", &nn::act::Finalize); +} diff --git a/src/cafe/nn_act.h b/src/cafe/nn_act.h new file mode 100644 index 0000000..cdc77fd --- /dev/null +++ b/src/cafe/nn_act.h @@ -0,0 +1,11 @@ + +#include + +namespace nn::act { + extern uint32_t (*Initialize)(); + extern uint8_t (*GetSlotNo)(); + extern uint32_t (*GetPersistentIdEx)(uint8_t slot); + extern uint32_t (*Finalize)(); +} + +void nnactInitialize(); diff --git a/src/cafe/nn_save.cpp b/src/cafe/nn_save.cpp new file mode 100644 index 0000000..257de8f --- /dev/null +++ b/src/cafe/nn_save.cpp @@ -0,0 +1,18 @@ + +#include "cafe/coreinit.h" +#include + +int (*SAVEInit)(); +int (*SAVEOpenFile)(FSClient *client, FSCmdBlock *block, uint8_t slot, const char *path, int *handle, uint32_t flags); +int (*SAVEGetSharedDataTitlePath)(uint64_t titleId, const char *path, char *outpath, uint32_t outlen); +void (*SAVEShutdown)(); + +void nnsaveInitialize() { + uint32_t handle; + OSDynLoad_Acquire("nn_save.rpl", &handle); + + OSDynLoad_FindExport(handle, false, "SAVEInit", &SAVEInit); + OSDynLoad_FindExport(handle, false, "SAVEOpenFile", &SAVEOpenFile); + OSDynLoad_FindExport(handle, false, "SAVEGetSharedDataTitlePath", &SAVEGetSharedDataTitlePath); + OSDynLoad_FindExport(handle, false, "SAVEShutdown", &SAVEShutdown); +} diff --git a/src/cafe/nn_save.h b/src/cafe/nn_save.h new file mode 100644 index 0000000..b1da9de --- /dev/null +++ b/src/cafe/nn_save.h @@ -0,0 +1,12 @@ + +#pragma once + +#include "cafe/coreinit.h" +#include + +extern int (*SAVEInit)(); +extern int (*SAVEOpenFile)(FSClient *client, FSCmdBlock *block, uint8_t slot, const char *path, int *handle, uint32_t flags); +extern int (*SAVEGetSharedDataTitlePath)(uint64_t titleId, const char *path, char *outpath, uint32_t outlen); +extern void (*SAVEShutdown)(); + +void nnsaveInitialize(); diff --git a/src/cafe/nsysnet.cpp b/src/cafe/nsysnet.cpp new file mode 100644 index 0000000..33697c9 --- /dev/null +++ b/src/cafe/nsysnet.cpp @@ -0,0 +1,35 @@ + +#include "cafe/nsysnet.h" +#include "cafe/coreinit.h" +#include + +int (*socket_lib_init)(); +int (*inet_aton)(const char *ip, uint32_t *addr); +int (*socket)(int domain, int type, int protocol); +int (*setsockopt)(int socket, int level, int optname, void *optval, int optlen); +int (*connect)(int socket, sockaddr *addr, int addrlen); +int (*bind)(int socket, sockaddr *addr, int addrlen); +int (*listen)(int socket, int backlog); +int (*accept)(int socket, sockaddr *addr, int *addrlen); +int (*send)(int socket, const void *buffer, int size, int flags); +int (*recv)(int socket, void *buffer, int size, int flags); +int (*socketclose)(int socket); +int (*socket_lib_finish)(); + +void nsysnetInitialize() { + uint32_t handle; + OSDynLoad_Acquire("nsysnet.rpl", &handle); + + OSDynLoad_FindExport(handle, false, "socket_lib_init", &socket_lib_init); + OSDynLoad_FindExport(handle, false, "inet_aton", &inet_aton); + OSDynLoad_FindExport(handle, false, "socket", &socket); + OSDynLoad_FindExport(handle, false, "setsockopt", &setsockopt); + OSDynLoad_FindExport(handle, false, "connect", &connect); + OSDynLoad_FindExport(handle, false, "bind", &bind); + OSDynLoad_FindExport(handle, false, "listen", &listen); + OSDynLoad_FindExport(handle, false, "accept", &accept); + OSDynLoad_FindExport(handle, false, "send", &send); + OSDynLoad_FindExport(handle, false, "recv", &recv); + OSDynLoad_FindExport(handle, false, "socketclose", &socketclose); + OSDynLoad_FindExport(handle, false, "socket_lib_finish", &socket_lib_finish); +} diff --git a/src/cafe/nsysnet.h b/src/cafe/nsysnet.h new file mode 100644 index 0000000..b9f95f3 --- /dev/null +++ b/src/cafe/nsysnet.h @@ -0,0 +1,37 @@ + +#pragma once + +#include + +#define AF_INET 2 + +#define SOCK_STREAM 1 +#define SOCK_DGRAM 2 + +#define IPPROTO_TCP 6 +#define IPPROTO_UDP 17 + +#define SOL_SOCKET -1 +#define SO_REUSEADDR 4 + +struct sockaddr { + uint16_t family; + uint16_t port; + uint32_t addr; + char zero[8]; +}; + +extern int (*socket_lib_init)(); +extern int (*inet_aton)(const char *ip, uint32_t *addr); +extern int (*socket)(int domain, int type, int protocol); +extern int (*setsockopt)(int socket, int level, int optname, void *optval, int optlen); +extern int (*connect)(int socket, sockaddr *addr, int addrlen); +extern int (*bind)(int socket, sockaddr *addr, int addrlen); +extern int (*listen)(int socket, int backlog); +extern int (*accept)(int socket, sockaddr *addr, int *addrlen); +extern int (*send)(int socket, const void *buffer, int size, int flags); +extern int (*recv)(int socket, void *buffer, int size, int flags); +extern int (*socketclose)(int socket); +extern int (*socket_lib_finish)(); + +void nsysnetInitialize(); diff --git a/src/cafe/sysapp.cpp b/src/cafe/sysapp.cpp new file mode 100644 index 0000000..fbe453e --- /dev/null +++ b/src/cafe/sysapp.cpp @@ -0,0 +1,20 @@ + +#include "cafe/sysapp.h" +#include "cafe/coreinit.h" + +bool (*SYSCheckTitleExists)(uint64_t titleId); +void (*SYSLaunchTitle)(uint64_t titleId); +void (*SYSLaunchMenu)(); + +void (*SYSLaunchTitleByPathFromLauncher)(const char *path, int len); + +void sysappInitialize() { + uint32_t handle; + OSDynLoad_Acquire("sysapp.rpl", &handle); + + OSDynLoad_FindExport(handle, false, "SYSCheckTitleExists", &SYSCheckTitleExists); + OSDynLoad_FindExport(handle, false, "SYSLaunchTitle", &SYSLaunchTitle); + OSDynLoad_FindExport(handle, false, "SYSLaunchMenu", &SYSLaunchMenu); + + OSDynLoad_FindExport(handle, false, "_SYSLaunchTitleByPathFromLauncher", &SYSLaunchTitleByPathFromLauncher); +} diff --git a/src/cafe/sysapp.h b/src/cafe/sysapp.h new file mode 100644 index 0000000..a8a68ff --- /dev/null +++ b/src/cafe/sysapp.h @@ -0,0 +1,12 @@ + +#pragma once + +#include + +extern bool (*SYSCheckTitleExists)(uint64_t titleId); +extern void (*SYSLaunchTitle)(uint64_t titleId); +extern void (*SYSLaunchMenu)(); + +extern void (*SYSLaunchTitleByPathFromLauncher)(const char *path, int len); + +void sysappInitialize(); diff --git a/src/cafe/vpad.cpp b/src/cafe/vpad.cpp new file mode 100644 index 0000000..0fc2b50 --- /dev/null +++ b/src/cafe/vpad.cpp @@ -0,0 +1,12 @@ + +#include "cafe/vpad.h" +#include "cafe/coreinit.h" + +void (*VPADRead)(int chan, VPADStatus *buffers, uint32_t count, int *error); + +void vpadInitialize() { + uint32_t handle; + OSDynLoad_Acquire("vpad.rpl", &handle); + + OSDynLoad_FindExport(handle, false, "VPADRead", &VPADRead); +} diff --git a/src/cafe/vpad.h b/src/cafe/vpad.h new file mode 100644 index 0000000..c937fba --- /dev/null +++ b/src/cafe/vpad.h @@ -0,0 +1,89 @@ + +#pragma once + +#include + +enum VPADButtons { + VPAD_BUTTON_A = 0x8000, + VPAD_BUTTON_B = 0x4000, + VPAD_BUTTON_X = 0x2000, + VPAD_BUTTON_Y = 0x1000, + VPAD_BUTTON_LEFT = 0x0800, + VPAD_BUTTON_RIGHT = 0x0400, + VPAD_BUTTON_UP = 0x0200, + VPAD_BUTTON_DOWN = 0x0100, + VPAD_BUTTON_ZL = 0x0080, + VPAD_BUTTON_ZR = 0x0040, + VPAD_BUTTON_L = 0x0020, + VPAD_BUTTON_R = 0x0010, + VPAD_BUTTON_PLUS = 0x0008, + VPAD_BUTTON_MINUS = 0x0004, + VPAD_BUTTON_HOME = 0x0002, + VPAD_BUTTON_SYNC = 0x0001, + + VPAD_BUTTON_STICK_R = 0x00020000, + VPAD_BUTTON_STICK_L = 0x00040000, + VPAD_BUTTON_TV = 0x00010000 +}; + +struct VPADVec2D { + float x; + float y; +}; + +struct VPADVec3D { + float x; + float y; + float z; +}; + +struct VPADDirection { + VPADVec3D x; + VPADVec3D y; + VPADVec3D z; +}; + +struct VPADTouchData { + uint16_t x; + uint16_t y; + uint16_t touched; + uint16_t validity; +}; + +struct VPADAccStatus { + VPADVec3D acc; + float magnitude; + float variation; + VPADVec2D vertical; +}; + +struct VPADStatus { + uint32_t hold; + uint32_t pressed; + uint32_t released; + VPADVec2D leftStick; + VPADVec2D rightStick; + VPADAccStatus accelorometer; + VPADVec3D gyro; + VPADVec3D angle; + uint8_t error; + uint8_t _51; + + VPADTouchData tpNormal; + VPADTouchData tpFiltered1; + VPADTouchData tpFiltered2; + uint16_t _6A; + + VPADDirection direction; + bool headphones; + VPADVec3D mag; + uint8_t volume; + uint8_t battery; + uint8_t micStatus; + uint8_t volumeEx; + char _A4[8]; +}; + +extern void (*VPADRead)(int chan, VPADStatus *buffers, uint32_t count, int *error); + +void vpadInitialize(); diff --git a/src/color.h b/src/color.h new file mode 100644 index 0000000..f0414c4 --- /dev/null +++ b/src/color.h @@ -0,0 +1,15 @@ + +#pragma once + +enum Colors { + COLOR_WHITE = 0xFFFFFFFF, + COLOR_BLACK = 0x000000FF, + + COLOR_RED = 0xFF0000FF, + COLOR_GREEN = 0x00FF00FF, + COLOR_BLUE = 0x0000FFFF, + + COLOR_LIGHT_RED = 0xFF8080FF, + COLOR_LIGHT_GREEN = 0x80FF80FF, + COLOR_LIGHT_BLUE = 0x8080FFFF, +}; diff --git a/src/debugger.cpp b/src/debugger.cpp new file mode 100644 index 0000000..4380e6b --- /dev/null +++ b/src/debugger.cpp @@ -0,0 +1,932 @@ + +#include "cafe/coreinit.h" +#include "cafe/nsysnet.h" +#include "cafe/vpad.h" +#include "kernel.h" + +#include "debugger.h" +#include "exceptions.h" +#include "screen.h" +#include "input.h" + +#include + + +bool BreakPoint::isRange(uint32_t addr, uint32_t length) { + return address >= addr && address <= addr + length - 1; +} + + +BreakPoint *BreakPointMgr::find(uint32_t addr, bool includeSpecial) { + BreakPoint *bp = breakpoints.find(addr); + if (!bp && includeSpecial) { + bp = special.find(addr); + } + return bp; +} + +BreakPoint *BreakPointMgr::findRange(uint32_t addr, uint32_t length, int *index, bool includeSpecial) { + BreakPoint *bp = breakpoints.findRange(addr, length, index); + if (bp) { + return bp; + } + + if (includeSpecial) { + int temp = *index - breakpoints.size(); + bp = special.findRange(addr, length, &temp); + *index = temp + breakpoints.size(); + return bp; + } + + return nullptr; +} + +SpecialBreakPoint *BreakPointMgr::findSpecial(uint32_t addr, OSThread *thread) { + int index = 0; + SpecialBreakPoint *bp = special.findRange(addr, 4, &index); + while (bp) { + if (bp->thread == thread) { + return bp; + } + bp = special.findRange(addr, 4, &index); + } + return nullptr; +} + +bool BreakPointMgr::isCustom(uint32_t addr) { + return find(addr, false); +} + +bool BreakPointMgr::isSpecial(uint32_t addr) { + return find(addr, true) && !find(addr, false); +} + +bool BreakPointMgr::isSoftware(uint32_t addr) { + uint32_t instr = getInstr(addr); + uint32_t opcode = instr >> 26; + if (opcode == 3) { //twi + return true; + } + else if (opcode == 31) { + return (instr & 0x7FF) == 8; //tw + } + return false; +} + +void BreakPointMgr::disable(BreakPoint *bp) { + uint32_t address = bp->address; + if (bp->address) { + bp->address = 0; + if (!find(address, true)) { + KernelWriteU32(address, bp->instruction); + } + bp->instruction = 0; + } +} + +void BreakPointMgr::enable(BreakPoint *bp, uint32_t addr) { + BreakPoint *other = find(addr, true); + if (other) { + bp->instruction = other->instruction; + } + else { + bp->instruction = *(uint32_t *)addr; + KernelWriteU32(addr, TRAP); + } + bp->address = addr; +} + +void BreakPointMgr::init() { + OSInitMutex(&mutex); +} + +void BreakPointMgr::lock() { + OSLockMutex(&mutex); +} + +void BreakPointMgr::unlock() { + OSUnlockMutex(&mutex); +} + +void BreakPointMgr::cleanup() { + lock(); + breakpoints.cleanup(); + special.cleanup(); + unlock(); +} + +void BreakPointMgr::read(void *buffer, uint32_t addr, uint32_t length) { + lock(); + + memcpy(buffer, (void *)addr, length); + + int index = 0; + BreakPoint *bp = findRange(addr, length, &index, true); + while (bp) { + uint32_t offset = bp->address - addr; + char *bufptr = (char *)buffer + offset; + if (bp->address > addr + length - 4) { + uint32_t value = bp->instruction; + for (int i = 0; i < length - offset; i++) { + bufptr[i] = value >> 24; + value <<= 8; + } + } + else { + *(uint32_t *)bufptr = bp->instruction; + } + bp = findRange(addr, length, &index, true); + } + unlock(); +} + +void BreakPointMgr::write(const void *buffer, uint32_t addr, uint32_t length) { + lock(); + + length = length & ~3; + + int index = 0; + if (!findRange(addr, length, &index, true)) { + KernelWrite(addr, buffer, length); + } + else { + for (uint32_t i = 0; i < length; i += 4) { + uint32_t value = *(uint32_t *)((char *)buffer + i); + + int index = 0; + BreakPoint *bp = findRange(addr + i, 4, &index, true); + if (!bp) { + KernelWriteU32(addr + i, value); + } + else { + while (bp) { + bp->instruction = value; + bp = findRange(addr + i, 4, &index, true); + } + } + } + } + + unlock(); +} + +void BreakPointMgr::toggle(uint32_t addr) { + lock(); + + BreakPoint *bp = find(addr, false); + if (bp) { + disable(bp); + } + else { + BreakPoint *bp = breakpoints.alloc(); + bp->isSpecial = false; + enable(bp, addr); + } + + unlock(); +} + +uint32_t BreakPointMgr::getInstr(uint32_t addr) { + lock(); + uint32_t instruction; + BreakPoint *bp = find(addr, true); + if (bp) { + instruction = bp->instruction; + } + else { + instruction = *(uint32_t *)addr; + } + unlock(); + return instruction; +} + +void BreakPointMgr::clearSpecial(OSThread *thread) { + lock(); + for (int i = 0; i < special.size(); i++) { + SpecialBreakPoint *bp = special[i]; + if (bp->thread == thread) { + disable(bp); + } + } + unlock(); +} + +void BreakPointMgr::predictStep(ExceptionState *state, bool stepOver) { + lock(); + + uint32_t address = state->context.srr0; + uint32_t instruction = getInstr(address); + + uint32_t target1 = address + 4; + uint32_t target2 = 0; + + uint8_t opcode = instruction >> 26; + if (opcode == 18) { //Branch + bool AA = instruction & 2; + bool LK = instruction & 1; + + uint32_t LI = instruction & 0x3FFFFFC; + if (LI & 0x2000000) LI -= 0x4000000; + + if (!LK || !stepOver) { + if (AA) target1 = LI; + else { + target1 = address + LI; + } + } + } + + else if (opcode == 16) { //Conditional branch + bool AA = instruction & 2; + bool LK = instruction & 1; + + uint32_t BD = instruction & 0xFFFC; + if (BD & 0x8000) BD -= 0x10000; + + if (!LK || !stepOver) { + if (AA) target2 = BD; + else { + target2 = address + BD; + } + } + } + + else if (opcode == 19) { //Conditional branch to lr/ctr + uint16_t XO = (instruction >> 1) & 0x3FF; + bool LK = instruction & 1; + + if (!LK || !stepOver) { + if (XO == 16) target2 = state->context.lr; + else if (XO == 528) target2 = state->context.ctr; + } + } + + SpecialBreakPoint *bp = special.alloc(); + bp->isSpecial = true; + bp->thread = state->thread; + enable(bp, target1); + + if (target2) { + SpecialBreakPoint *bp = special.alloc(); + bp->isSpecial = true; + bp->thread = state->thread; + enable(bp, target2); + } + + unlock(); +} + + +bool ExceptionState::isBreakpoint() { + return type == PROGRAM && context.srr1 & 0x20000; +} + +void ExceptionState::resume() { + OSLoadContext(&context); +} + + +void ExceptionMgr::init() { + OSInitMutex(&mutex); +} + +void ExceptionMgr::lock() { + OSLockMutex(&mutex); +} + +void ExceptionMgr::unlock() { + OSUnlockMutex(&mutex); +} + +void ExceptionMgr::cleanup() { + OSMessage message; + message.message = Debugger::STEP_CONTINUE; + + lock(); + + for (int i = 0; i < list.size(); i++) { + ExceptionState *state = list[i]; + OSSendMessage(&state->queue, &message, OS_MESSAGE_FLAGS_BLOCKING); + } + + unlock(); +} + +ExceptionState *ExceptionMgr::find(OSThread *thread) { + lock(); + for (int i = 0; i < list.size(); i++) { + ExceptionState *state = list[i]; + if (state->thread == thread) { + unlock(); + return state; + } + } + unlock(); + return nullptr; +} + +ExceptionState *ExceptionMgr::findOrCreate(OSThread *thread) { + lock(); + ExceptionState *state = find(thread); + if (!state) { + state = new ExceptionState(); + state->thread = thread; + OSInitMessageQueue(&state->queue, &state->message, 1); + list.push_back(state); + } + unlock(); + return state; +} + + +uint32_t StepMgr::buffer[96]; + +void StepMgr::init() { + OSInitMutex(&mutex); + usedMask = 0; +} + +void StepMgr::lock() { + OSLockMutex(&mutex); +} + +void StepMgr::unlock() { + OSUnlockMutex(&mutex); +} + +uint32_t *StepMgr::alloc() { + lock(); + int index = 0; + uint32_t mask = usedMask; + while (mask & 1) { + mask >>= 1; + index++; + } + usedMask |= 1 << index; + unlock(); + + if (index == 32) { + OSFatal("Displaced step allocation failed"); + } + return &buffer[index * 3]; +} + +void StepMgr::free(int index) { + lock(); + usedMask &= ~(1 << index); + unlock(); +} + +void StepMgr::branchConditional(ExceptionState *state, uint32_t instruction, uint32_t target, bool checkCtr) { + Bits<5> BO = (instruction >> 21) & 0x1F; + Bits<32> CR = state->context.cr; + + uint32_t BI = (instruction >> 16) & 0x1F; + bool LK = instruction & 1; + + if (LK) { + state->context.lr = state->context.srr0 + 4; + } + + bool ctr_ok = true; + if (checkCtr) { + if (!BO[2]) { + state->context.ctr--; + } + ctr_ok = BO[2] || ((state->context.ctr != 0) ^ BO[3]); + } + + bool cond_ok = BO[0] || (CR[BI] == BO[1]); + if (ctr_ok && cond_ok) { + state->context.srr0 = target; + } +} + +void StepMgr::singleStep(ExceptionState *state, uint32_t instruction) { + uint8_t opcode = instruction >> 26; + + if (opcode == 18) { //Branch + bool AA = instruction & 2; + bool LK = instruction & 1; + + uint32_t LI = instruction & 0x3FFFFFC; + if (LI & 0x2000000) LI -= 0x4000000; + + if (LK) { + state->context.lr = state->context.srr0 + 4; + } + + if (AA) state->context.srr0 = LI; + else { + state->context.srr0 += LI; + } + return; + } + + if (opcode == 16) { //Conditional branch + bool AA = instruction & 2; + + uint32_t BD = instruction & 0xFFFC; + if (BD & 0x8000) BD -= 0x10000; + + uint32_t target; + if (AA) target = BD; + else { + target = state->context.srr0 + BD; + } + + branchConditional(state, instruction, target, true); + return; + } + + if (opcode == 19) { //Conditional branch to lr/ctr + uint16_t XO = (instruction >> 1) & 0x3FF; + + uint32_t target; + if (XO == 16) { + branchConditional(state, instruction, state->context.lr, true); + return; + } + else if (XO == 528) { + branchConditional(state, instruction, state->context.ctr, false); + return; + } + } + + uint32_t *ptr = alloc(); + ptr[0] = instruction; + ptr[1] = TRAP; + ptr[2] = state->context.srr0; + DCFlushRange(ptr, 12); + ICInvalidateRange(ptr, 12); + state->context.srr0 = (uint32_t)ptr; +} + +void StepMgr::handleBreakPoint(ExceptionState *state) { + uint32_t start = (uint32_t)buffer; + uint32_t end = (uint32_t)buffer + sizeof(buffer); + + uint32_t addr = state->context.srr0; + if (addr >= start && addr < end) { + int offset = addr - start; + if (offset % 12 != 4) { + char buffer[100]; + snprintf(buffer, 100, "Unexpected srr0 after displaced step: %08X", addr); + OSFatal(buffer); + } + + int index = offset / 12; + state->context.srr0 = buffer[index * 3 + 2] + 4; + free(index); + + state->resume(); + } +} + +void StepMgr::adjustAddress(ExceptionState *state) { + uint32_t start = (uint32_t)buffer; + uint32_t end = (uint32_t)buffer + sizeof(buffer); + + uint32_t addr = state->context.srr0; + if (addr >= start && addr < end) { + int index = (addr - start) / 12; + state->context.srr0 = buffer[index * 3 + 2]; + } +} + + +bool Debugger::checkDataRead(uint32_t addr, uint32_t length) { + uint32_t memStart, memSize; + OSGetMemBound(MEM2, &memStart, &memSize); + + uint32_t memEnd = memStart + memSize; + + return addr >= 0x10000000 && addr + length <= memEnd; +} + +Debugger::StepCommand Debugger::notifyBreak(ExceptionState *state) { + OSMessage message; + message.message = ExceptionState::PROGRAM; + message.args[0] = (uint32_t)&state->context; + message.args[1] = sizeof(OSContext); + message.args[2] = (uint32_t)state->thread; + OSSendMessage(&eventQueue, &message, OS_MESSAGE_FLAGS_BLOCKING); + + OSReceiveMessage(&state->queue, &message, OS_MESSAGE_FLAGS_BLOCKING); + return (StepCommand)message.message; +} + +void Debugger::resumeBreakPoint(ExceptionState *state) { + uint32_t address = state->context.srr0; + if (breakpoints.isSoftware(address)) { + state->context.srr0 += 4; + state->resume(); + } + + uint32_t instruction = breakpoints.getInstr(state->context.srr0); + stepper.singleStep(state, instruction); + state->resume(); +} + +void Debugger::processBreakPoint(ExceptionState *state) { + StepCommand command = notifyBreak(state); + if (command == STEP_INTO || command == STEP_OVER) { + breakpoints.predictStep(state, command == STEP_OVER); + } +} + +void Debugger::handleBreakPoint(ExceptionState *state) { + if (firstTrap) { + firstTrap = false; + + Screen screen; + screen.init(); + screen.drawText( + 0, 0, "Waiting for debugger connection.\n" + "Press the home button to continue without debugger.\n" + "You can still connect while the game is running." + ); + screen.flip(); + + while (!connected) { + uint32_t buttons = GetInput(VPAD_BUTTON_HOME); + if (buttons) { + state->context.srr0 += 4; + state->resume(); + } + } + } + + stepper.handleBreakPoint(state); + + if (!connected) { + handleFatalCrash(state); + } + + uint32_t addr = state->context.srr0; + bool trap; + + breakpoints.lock(); + trap = breakpoints.isCustom(addr) || breakpoints.isSoftware(addr) || + breakpoints.findSpecial(addr, state->thread); + breakpoints.unlock(); + + breakpoints.clearSpecial(state->thread); + if (trap) { + processBreakPoint(state); + } + resumeBreakPoint(state); +} + +void Debugger::handleFatalCrash(ExceptionState *state) { + stepper.adjustAddress(state); + if (connected) { + OSMessage message; + message.message = state->type; + message.args[0] = (uint32_t)&state->context; + message.args[1] = sizeof(OSContext); + message.args[2] = (uint32_t)state->thread; + OSSendMessage(&eventQueue, &message, OS_MESSAGE_FLAGS_BLOCKING); + + while (true) { + OSReceiveMessage(&state->queue, &message, OS_MESSAGE_FLAGS_BLOCKING); + } + } + else { + const char *type; + if (state->type == ExceptionState::DSI) type = "A DSI"; + else if (state->type == ExceptionState::ISI) type = "An ISI"; + else { + type = "A program"; + } + DumpContext(&state->context, type); + } +} + +void Debugger::handleException(OSContext *context, ExceptionState::Type type) { + OSThread *thread = OSGetCurrentThread(); + + ExceptionState *state = exceptions.findOrCreate(thread); + memcpy(&state->context, context, sizeof(OSContext)); + state->type = type; + + delete context; + + if (state->isBreakpoint()) { + handleBreakPoint(state); + } + else { + handleFatalCrash(state); + } +} + +void Debugger::exceptionHandler(OSContext *context, ExceptionState::Type type) { + debugger->handleException(context, type); +} + +bool Debugger::dsiHandler(OSContext *context) { + OSContext *info = new OSContext(); + memcpy(info, context, sizeof(OSContext)); + context->srr0 = (uint32_t)exceptionHandler; + context->gpr[3] = (uint32_t)info; + context->gpr[4] = ExceptionState::DSI; + return true; +} + +bool Debugger::isiHandler(OSContext *context) { + OSContext *info = new OSContext(); + memcpy(info, context, sizeof(OSContext)); + context->srr0 = (uint32_t)exceptionHandler; + context->gpr[3] = (uint32_t)info; + context->gpr[4] = ExceptionState::ISI; + return true; +} + +bool Debugger::programHandler(OSContext *context) { + OSContext *info = new OSContext(); + memcpy(info, context, sizeof(OSContext)); + context->srr0 = (uint32_t)exceptionHandler; + context->gpr[3] = (uint32_t)info; + context->gpr[4] = ExceptionState::PROGRAM; + return true; +} + +void Debugger::cleanup() { + breakpoints.cleanup(); + exceptions.cleanup(); + + OSMessage message; + while (OSReceiveMessage(&eventQueue, &message, OS_MESSAGE_FLAGS_NONE)); +} + +void Debugger::mainLoop(Client *client) { + while (true) { + uint8_t cmd; + if (!client->recvall(&cmd, 1)) return; + + if (cmd == COMMAND_CLOSE) return; + else if (cmd == COMMAND_READ) { + uint32_t addr, length; + if (!client->recvall(&addr, 4)) return; + if (!client->recvall(&length, 4)) return; + + char *buffer = new char[length]; + breakpoints.read(buffer, addr, length); + if (!client->sendall(buffer, length)) { + delete buffer; + return; + } + delete buffer; + } + else if (cmd == COMMAND_WRITE) { + uint32_t addr, length; + if (!client->recvall(&addr, 4)) return; + if (!client->recvall(&length, 4)) return; + if (!client->recvall((void *)addr, length)) return; + } + else if (cmd == COMMAND_WRITE_CODE) { + uint32_t addr, length; + if (!client->recvall(&addr, 4)) return; + if (!client->recvall(&length, 4)) return; + + char *buffer = new char[length]; + if (!client->recvall(buffer, length)) { + delete buffer; + return; + } + breakpoints.write(buffer, addr, length); + delete buffer; + } + else if (cmd == COMMAND_GET_MODULE_NAME) { + char name[0x40]; + int length = 0x40; + OSDynLoad_GetModuleName(-1, name, &length); + + length = strlen(name); + if (!client->sendall(&length, 4)) return; + if (!client->sendall(name, length)) return; + } + else if (cmd == COMMAND_GET_MODULE_LIST) { + OSLockMutex(OSDynLoad_gLoaderLock); + + char buffer[0x1000]; //This should be enough + uint32_t offset = 0; + OSDynLoad_RPLInfo *current = FirstRPL; + while (current) { + OSDynLoad_NotifyData *info = current->notifyData; + + uint32_t namelen = strlen(current->name); + if (offset + 0x18 + namelen > 0x1000) { + break; + } + + uint32_t *infobuf = (uint32_t *)(buffer + offset); + infobuf[0] = info->textAddr; + infobuf[1] = info->textSize; + infobuf[2] = info->dataAddr; + infobuf[3] = info->dataSize; + infobuf[4] = (uint32_t)current->entryPoint; + infobuf[5] = namelen; + memcpy(&infobuf[6], current->name, namelen); + offset += 0x18 + namelen; + + current = current->next; + } + + OSUnlockMutex(OSDynLoad_gLoaderLock); + + if (!client->sendall(&offset, 4)) return; + if (!client->sendall(buffer, offset)) return; + } + else if (cmd == COMMAND_GET_THREAD_LIST) { + int state = OSDisableInterrupts(); + __OSLockScheduler(this); + + char buffer[0x1000]; //This should be enough + uint32_t offset = 0; + OSThread *current = ThreadList; + while (current) { + const char *name = OSGetThreadName(current); + + uint32_t namelen = 0; + if (name) { + namelen = strlen(name); + } + + if (offset + 0x1C + namelen > 0x1000) { + break; + } + + int priority = current->basePriority; + if (current->type == 1) { + priority -= 0x20; + } + else if (current->type == 2) { + priority -= 0x40; + } + + uint32_t *infobuf = (uint32_t *)(buffer + offset); + infobuf[0] = (uint32_t)current; + infobuf[1] = current->attr & 7; + infobuf[2] = priority; + infobuf[3] = (uint32_t)current->stackBase; + infobuf[4] = (uint32_t)current->stackEnd; + infobuf[5] = (uint32_t)current->entryPoint; + infobuf[6] = namelen; + memcpy(&infobuf[7], name, namelen); + offset += 0x1C + namelen; + + current = current->activeLink.next; + } + + __OSUnlockScheduler(this); + OSRestoreInterrupts(state); + + if (!client->sendall(&offset, 4)) return; + if (!client->sendall(buffer, offset)) return; + } + else if (cmd == COMMAND_GET_STACK_TRACE) { + OSThread *thread; + if (!client->recvall(&thread, 4)) return; + + ExceptionState *state = exceptions.find(thread); + if (state) { + uint32_t sp = state->context.gpr[1]; + uint32_t trace[32]; + int index = 0; + while (checkDataRead(sp, 4)) { + sp = *(uint32_t *)sp; + if (!checkDataRead(sp, 4)) break; + + trace[index] = *(uint32_t *)(sp + 4); + index++; + } + + if (!client->sendall(&index, 4)) return; + if (!client->sendall(trace, index * 4)) return; + } + else { + int index = 0; + if (!client->sendall(&index, 4)) return; + } + } + else if (cmd == COMMAND_TOGGLE_BREAKPOINT) { + uint32_t address; + if (!client->recvall(&address, 4)) return; + + breakpoints.toggle(address); + } + else if (cmd == COMMAND_POKE_REGISTERS) { + OSThread *thread; + if (!client->recvall(&thread, 4)) return; + + uint32_t gpr[32]; + double fpr[32]; + if (!client->recvall(gpr, 4 * 32)) return; + if (!client->recvall(fpr, 8 * 32)) return; + + exceptions.lock(); + ExceptionState *state = exceptions.find(thread); + if (state) { + memcpy(state->context.gpr, gpr, 4 * 32); + memcpy(state->context.fpr, fpr, 8 * 32); + } + exceptions.unlock(); + } + else if (cmd == COMMAND_RECEIVE_MESSAGES) { + OSMessage messages[10]; + + int count = 0; + while (count < 10) { + if (!OSReceiveMessage(&eventQueue, &messages[count], OS_MESSAGE_FLAGS_NONE)) { + break; + } + count++; + } + + if (!client->sendall(&count, 4)) return; + for (int i = 0; i < count; i++) { + if (!client->sendall(&messages[i], sizeof(OSMessage))) return; + if (messages[i].args[0]) { + void *data = (void *)messages[i].args[0]; + size_t length = messages[i].args[1]; + if (!client->sendall(data, length)) return; + } + } + } + else if (cmd == COMMAND_SEND_MESSAGE) { + OSMessage message; + if (!client->recvall(&message, sizeof(OSMessage))) return; + + OSThread *thread = (OSThread *)message.args[0]; + + exceptions.lock(); + ExceptionState *state = exceptions.find(thread); + if (state) { + OSSendMessage(&state->queue, &message, OS_MESSAGE_FLAGS_NONE); + } + exceptions.unlock(); + } + } +} + +void Debugger::threadFunc() { + int result = socket_lib_init(); + if (result < 0) { + OSFatal("Failed to initialize socket library"); + } + + OSSetExceptionCallbackEx(OS_EXCEPTION_MODE_GLOBAL_ALL_CORES, OS_EXCEPTION_TYPE_DSI, dsiHandler); + OSSetExceptionCallbackEx(OS_EXCEPTION_MODE_GLOBAL_ALL_CORES, OS_EXCEPTION_TYPE_ISI, isiHandler); + OSSetExceptionCallbackEx(OS_EXCEPTION_MODE_GLOBAL_ALL_CORES, OS_EXCEPTION_TYPE_PROGRAM, programHandler); + + Server server; + Client client; + + initialized = true; + while (true) { + if (!server.init(Socket::TCP)) continue; + if (!server.bind(1560)) continue; + if (!server.accept(&client)) continue; + connected = true; + mainLoop(&client); + cleanup(); + connected = false; + client.close(); + server.close(); + } +} + +int Debugger::threadEntry(int argc, void *argv) { + debugger->threadFunc(); + return 0; +} + +void Debugger::start() { + initialized = false; + connected = false; + firstTrap = true; + + OSInitMessageQueue(&eventQueue, eventMessages, MESSAGE_COUNT); + + breakpoints.init(); + exceptions.init(); + stepper.init(); + + OSThread *thread = new OSThread(); + char *stack = new char[0x8000]; + + OSCreateThread( + thread, threadEntry, 0, 0, + stack + STACK_SIZE, STACK_SIZE, + 0, 12 + ); + OSSetThreadName(thread, "Debug Server"); + OSResumeThread(thread); + + while (!initialized) { + OSSleepTicks(OSMillisecondsToTicks(20)); + } +} + +Debugger *debugger; diff --git a/src/debugger.h b/src/debugger.h new file mode 100644 index 0000000..a725f8c --- /dev/null +++ b/src/debugger.h @@ -0,0 +1,259 @@ + +#pragma once + +#include "cafe/coreinit.h" +#include "kernel.h" +#include "socket.h" + +#include +#include + +#define MESSAGE_COUNT 10 +#define STACK_SIZE 0x8000 + +#define TRAP 0x7FE00008 + + +template +class Bits { +public: + Bits(uint32_t value) { + this->value = value; + } + + bool operator [](int index) { + return (value >> (N - index - 1)) & 1; + } + +private: + uint32_t value; +}; + + +class ExceptionState { +public: + enum Type { + DSI, + ISI, + PROGRAM + }; + + bool isBreakpoint(); + void resume(); + + Type type; + OSContext context; + + OSThread *thread; + + OSMessageQueue queue; + OSMessage message; +}; + + +class ExceptionMgr { +public: + void init(); + void lock(); + void unlock(); + void cleanup(); + ExceptionState *find(OSThread *thread); + ExceptionState *findOrCreate(OSThread *thread); + +private: + OSMutex mutex; + std::vector list; +}; + + +class BreakPoint { +public: + bool isRange(uint32_t addr, uint32_t length); + + uint32_t address; + uint32_t instruction; + bool isSpecial; +}; + +class SpecialBreakPoint : public BreakPoint { +public: + OSThread *thread; +}; + + +template +class BreakPointList { +public: + size_t size() { + return list.size(); + } + + T *alloc() { + for (int i = 0; i < size(); i++) { + if (list[i].address == 0) { + return &list[i]; + } + } + + T newBp; + newBp.address = 0; + newBp.instruction = 0; + list.push_back(newBp); + return &list.back(); + } + + T *find(uint32_t addr) { + for (int i = 0; i < size(); i++) { + if (list[i].address == addr) { + return &list[i]; + } + } + return nullptr; + } + + T *findRange(uint32_t addr, uint32_t length, int *index) { + int i = *index; + while (i < size()) { + if (list[i].isRange(addr, length)) { + *index = i + 1; + return &list[i]; + } + i++; + } + + if (i > *index) { + *index = i; + } + return nullptr; + } + + T *operator [](int index) { + return &list[index]; + } + + void cleanup() { + for (int i = 0; i < size(); i++) { + if (list[i].address != 0) { + KernelWriteU32(list[i].address, list[i].instruction); + list[i].address = 0; + list[i].instruction = 0; + } + } + } + +private: + std::vector list; +}; + + +class BreakPointMgr { +public: + void init(); + void lock(); + void unlock(); + void cleanup(); + bool isCustom(uint32_t addr); + bool isSoftware(uint32_t addr); + bool isSpecial(uint32_t addr); + void read(void *buffer, uint32_t addr, uint32_t length); + void write(const void *buffer, uint32_t addr, uint32_t length); + void toggle(uint32_t addr); + uint32_t getInstr(uint32_t addr); + BreakPoint *find(uint32_t addr, bool includeSpecial); + BreakPoint *findRange(uint32_t addr, uint32_t length, int *index, bool includeSpecial); + SpecialBreakPoint *findSpecial(uint32_t addr, OSThread *thread); + void clearSpecial(OSThread *thread); + void predictStep(ExceptionState *state, bool stepOver); + +private: + BreakPoint *alloc(); + SpecialBreakPoint *allocSpecial(); + void disable(BreakPoint *bp); + void enable(BreakPoint *bp, uint32_t addr); + + BreakPointList breakpoints; + BreakPointList special; + + OSMutex mutex; +}; + + +class StepMgr { +public: + void init(); + void lock(); + void unlock(); + void singleStep(ExceptionState *state, uint32_t instruction); + void handleBreakPoint(ExceptionState *state); + void adjustAddress(ExceptionState *state); + +private: + static uint32_t buffer[96]; + + uint32_t *alloc(); + void free(int index); + void branchConditional(ExceptionState *state, uint32_t instruction, uint32_t target, bool checkCtr); + + OSMutex mutex; + + uint32_t usedMask; +}; + + +class Debugger { +public: + enum StepCommand { + STEP_CONTINUE, + STEP_INTO, + STEP_OVER + }; + + void start(); + +private: + enum Command { + COMMAND_CLOSE, + COMMAND_READ, + COMMAND_WRITE, + COMMAND_WRITE_CODE, + COMMAND_GET_MODULE_NAME, + COMMAND_GET_MODULE_LIST, + COMMAND_GET_THREAD_LIST, + COMMAND_GET_STACK_TRACE, + COMMAND_TOGGLE_BREAKPOINT, + COMMAND_POKE_REGISTERS, + COMMAND_RECEIVE_MESSAGES, + COMMAND_SEND_MESSAGE + }; + + static int threadEntry(int argc, void *argv); + static bool dsiHandler(OSContext *context); + static bool isiHandler(OSContext *context); + static bool programHandler(OSContext *context); + static void exceptionHandler(OSContext *context, ExceptionState::Type type); + + void threadFunc(); + void mainLoop(Client *client); + void handleException(OSContext *context, ExceptionState::Type type); + void handleFatalCrash(ExceptionState *state); + void handleBreakPoint(ExceptionState *state); + void processBreakPoint(ExceptionState *state); + void resumeBreakPoint(ExceptionState *state); + StepCommand notifyBreak(ExceptionState *state); + void cleanup(); + + bool checkDataRead(uint32_t addr, uint32_t length); + + OSMessageQueue eventQueue; + OSMessage eventMessages[MESSAGE_COUNT]; + + BreakPointMgr breakpoints; + ExceptionMgr exceptions; + StepMgr stepper; + + bool initialized; + bool connected; + bool firstTrap; +}; + +extern Debugger *debugger; diff --git a/src/exceptions.cpp b/src/exceptions.cpp new file mode 100644 index 0000000..79382e3 --- /dev/null +++ b/src/exceptions.cpp @@ -0,0 +1,51 @@ + +#include "cafe/coreinit.h" + +void DumpContext(OSContext *context, const char *excType) { + char buffer[1000]; + snprintf(buffer, 1000, + "%s exception occurred!\n" + "r0 :%08X r1 :%08X r2 :%08X r3 :%08X r4 :%08X\n" + "r5 :%08X r6 :%08X r7 :%08X r8 :%08X r9 :%08X\n" + "r10:%08X r11:%08X r12:%08X r13:%08X r14:%08X\n" + "r15:%08X r16:%08X r17:%08X r18:%08X r19:%08X\n" + "r20:%08X r21:%08X r22:%08X r23:%08X r24:%08X\n" + "r25:%08X r26:%08X r27:%08X r28:%08X r29:%08X\n" + "r30:%08X r31:%08X LR :%08X CTR:%08X XER:%08X\n" + "\n" + "SRR0:%08X SRR1:%08X DSISR:%08X DAR:%08X", + excType, + context->gpr[0], context->gpr[1], context->gpr[2], context->gpr[3], + context->gpr[4], context->gpr[5], context->gpr[6], context->gpr[7], + context->gpr[8], context->gpr[9], context->gpr[10], context->gpr[11], + context->gpr[12], context->gpr[13], context->gpr[14], context->gpr[15], + context->gpr[16], context->gpr[17], context->gpr[18], context->gpr[19], + context->gpr[20], context->gpr[21], context->gpr[22], context->gpr[23], + context->gpr[24], context->gpr[25], context->gpr[26], context->gpr[27], + context->gpr[28], context->gpr[29], context->gpr[30], context->gpr[31], + context->lr, context->ctr, context->xer, context->srr0, context->srr1, + context->dsisr, context->dar + ); + OSFatal(buffer); +} + +bool DSIHandler(OSContext *context) { + DumpContext(context, "A DSI"); + return false; +} + +bool ISIHandler(OSContext *context) { + DumpContext(context, "An ISI"); + return false; +} + +bool ProgramHandler(OSContext *context) { + DumpContext(context, "A program"); + return false; +} + +void InstallExceptionHandlers() { + OSSetExceptionCallbackEx(OS_EXCEPTION_MODE_GLOBAL_ALL_CORES, OS_EXCEPTION_TYPE_DSI, DSIHandler); + OSSetExceptionCallbackEx(OS_EXCEPTION_MODE_GLOBAL_ALL_CORES, OS_EXCEPTION_TYPE_ISI, ISIHandler); + OSSetExceptionCallbackEx(OS_EXCEPTION_MODE_GLOBAL_ALL_CORES, OS_EXCEPTION_TYPE_PROGRAM, ProgramHandler); +} diff --git a/src/exceptions.h b/src/exceptions.h new file mode 100644 index 0000000..b84566a --- /dev/null +++ b/src/exceptions.h @@ -0,0 +1,3 @@ + +void DumpContext(OSContext *context, const char *excType); +void InstallExceptionHandlers(); diff --git a/src/hbl.h b/src/hbl.h new file mode 100644 index 0000000..f6303b7 --- /dev/null +++ b/src/hbl.h @@ -0,0 +1,15 @@ + +#pragma once + +#include + +#define MEM_BASE 0x800000 + +struct OsSpecifics { + uint32_t OSDynLoad_Acquire; + uint32_t OSDynLoad_FindExport; +}; +#define OS_SPECIFICS ((OsSpecifics *)(MEM_BASE + 0x1500)) + +#define EXIT_SUCCESS 0 +#define EXIT_RELAUNCH_ON_LOAD -3 diff --git a/src/input.cpp b/src/input.cpp new file mode 100644 index 0000000..faf034c --- /dev/null +++ b/src/input.cpp @@ -0,0 +1,21 @@ + +#include "cafe/vpad.h" +#include + +uint32_t GetInput(uint32_t mask) { + VPADStatus input; + int error; + VPADRead(0, &input, 1, &error); + return input.pressed & mask; +} + +uint32_t WaitInput(uint32_t mask) { + VPADStatus input; + int error; + while (true) { + VPADRead(0, &input, 1, &error); + if (input.pressed & mask) { + return input.pressed & mask; + } + } +} diff --git a/src/input.h b/src/input.h new file mode 100644 index 0000000..4f59e45 --- /dev/null +++ b/src/input.h @@ -0,0 +1,7 @@ + +#pragma once + +#include + +uint32_t GetInput(uint32_t mask); +uint32_t WaitInput(uint32_t mask); diff --git a/src/kernel.S b/src/kernel.S new file mode 100644 index 0000000..a39f71b --- /dev/null +++ b/src/kernel.S @@ -0,0 +1,28 @@ + +.global SCKernelCopyData +SCKernelCopyData: + // Disable data address translation + mfmsr %r6 + li %r7, 0x10 + andc %r6, %r6, %r7 + mtmsr %r6 + + // Copy data + addi %r3, %r3, -1 + addi %r4, %r4, -1 + mtctr %r5 +SCKernelCopyData_loop: + lbzu %r5, 1(%r4) + stbu %r5, 1(%r3) + bdnz SCKernelCopyData_loop + + // Enable data address translation + ori %r6, %r6, 0x10 + mtmsr %r6 + blr + +.global KernelCopyData +KernelCopyData: + li %r0, 0x2500 + sc + blr diff --git a/src/kernel.cpp b/src/kernel.cpp new file mode 100644 index 0000000..d7037ee --- /dev/null +++ b/src/kernel.cpp @@ -0,0 +1,64 @@ + +#include "cafe/coreinit.h" + +#include + +#define KERN_SYSCALL_TBL1 0xFFE84C70 //Unknown +#define KERN_SYSCALL_TBL2 0xFFE85070 //Games +#define KERN_SYSCALL_TBL3 0xFFE85470 //Loader +#define KERN_SYSCALL_TBL4 0xFFEAAA60 //Home menu +#define KERN_SYSCALL_TBL5 0xFFEAAE60 //Browser + +extern "C" void SCKernelCopyData(uint32_t dst, uint32_t src, uint32_t len); +extern "C" void KernelCopyData(uint32_t dst, uint32_t src, uint32_t len); + +void KernelWrite(uint32_t addr, const void *data, uint32_t length) { + uint32_t dst = OSEffectiveToPhysical(addr); + uint32_t src = OSEffectiveToPhysical((uint32_t)data); + KernelCopyData(dst, src, length); + DCFlushRange((void *)addr, length); + ICInvalidateRange((void *)addr, length); +} + +void KernelWriteU32(uint32_t addr, uint32_t value) { + uint32_t dst = OSEffectiveToPhysical(addr); + uint32_t src = OSEffectiveToPhysical((uint32_t)&value); + KernelCopyData(dst, src, 4); + DCFlushRange((void *)addr, 4); + ICInvalidateRange((void *)addr, 4); +} + +/* Write a 32-bit word with kernel permissions */ +void __attribute__ ((noinline)) kern_write(uint32_t addr, uint32_t value) +{ + asm volatile ( + "li 3,1\n" + "li 4,0\n" + "mr 5,%1\n" + "li 6,0\n" + "li 7,0\n" + "lis 8,1\n" + "mr 9,%0\n" + "mr %1,1\n" + "li 0,0x3500\n" + "sc\n" + "nop\n" + "mr 1,%1\n" + : + : "r"(addr), "r"(value) + : "memory", "ctr", "lr", "0", "3", "4", "5", "6", "7", "8", "9", "10", + "11", "12" + ); +} + +void PatchSyscall(int index, uint32_t addr) { + kern_write(KERN_SYSCALL_TBL1 + index * 4, addr); + kern_write(KERN_SYSCALL_TBL2 + index * 4, addr); + kern_write(KERN_SYSCALL_TBL3 + index * 4, addr); + kern_write(KERN_SYSCALL_TBL4 + index * 4, addr); + kern_write(KERN_SYSCALL_TBL5 + index * 4, addr); +} + +void kernelInitialize() { + PatchSyscall(0x25, (uint32_t)SCKernelCopyData); +} diff --git a/src/kernel.h b/src/kernel.h new file mode 100644 index 0000000..9051bc6 --- /dev/null +++ b/src/kernel.h @@ -0,0 +1,11 @@ + +#pragma once + +#include + +void KernelWrite(uint32_t addr, const void *data, uint32_t length); +void KernelWriteU32(uint32_t addr, uint32_t value); + +void PatchSyscall(int index, void *ptr); + +void kernelInitialize(); diff --git a/src/link.ld b/src/link.ld new file mode 100644 index 0000000..12dd5e9 --- /dev/null +++ b/src/link.ld @@ -0,0 +1,26 @@ + +OUTPUT(diibugger.elf) + +ENTRY(entryPoint) + +SECTIONS { + . = 0x00802000; + .text : { + *(.text*); + } + .rodata : { + *(.rodata*); + } + .data : { + *(.data*); + *(.sdata*); + } + .bss : { + *(.bss*); + *(.sbss*); + } + + /DISCARD/ : { + *(*); + } +} diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..754098b --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,90 @@ + +#include "cafe/coreinit.h" +#include "cafe/sysapp.h" +#include "cafe/nsysnet.h" +#include "cafe/vpad.h" +#include "cafe/nn_save.h" +#include "cafe/nn_act.h" +#include "kernel.h" +#include "hbl.h" + +#include "patches.h" +#include "debugger.h" +#include "exceptions.h" +#include "screen.h" +#include "menu.h" + + +bool GetTitleIdOnDisk(uint64_t *titleId) { + MCPTitleListType title; + uint32_t count = 0; + + int handle = MCP_Open(); + MCP_TitleListByDevice(handle, "odd", &count, &title, sizeof(title)); + MCP_Close(handle); + + if (count > 0) { + *titleId = title.titleId; + return true; + } + return false; +} + +int MenuMain() { + Screen screen; + screen.init(); + + Menu menu(&screen); + + Menu::Option result = menu.show(); + if (result == Menu::Exit) return EXIT_SUCCESS; + else if (result == Menu::LaunchDisk) { + uint64_t titleId; + if (GetTitleIdOnDisk(&titleId)) { + SYSLaunchTitle(titleId); + } + else { + menu.setMessage("Please insert a valid disk"); + } + } + else if (result == Menu::ReturnToMenu) { + SYSLaunchMenu(); + } + return EXIT_RELAUNCH_ON_LOAD; +} + +int DebuggerMain() { + ApplyPatches(); + debugger = new Debugger(); + debugger->start(); + return EXIT_RELAUNCH_ON_LOAD; +} + + +bool firstRun = true; +int start() { + coreinitInitialize(); + kernelInitialize(); + vpadInitialize(); + sysappInitialize(); + nsysnetInitialize(); + nnsaveInitialize(); + nnactInitialize(); + + InstallExceptionHandlers(); + + int result; + if (firstRun) { + result = MenuMain(); + } + else { + result = DebuggerMain(); + } + firstRun = false; + + return result; +} + +extern "C" int entryPoint() { + return start(); +} diff --git a/src/memory.cpp b/src/memory.cpp new file mode 100644 index 0000000..3649531 --- /dev/null +++ b/src/memory.cpp @@ -0,0 +1,28 @@ + +#include "cafe/coreinit.h" + +#include + +void * operator new(size_t size) { + return MEMAllocFromDefaultHeap(size); +} + +void * operator new[](size_t size) { + return MEMAllocFromDefaultHeap(size); +} + +void * operator new(size_t size, int alignment) { + return MEMAllocFromDefaultHeapEx(size, alignment); +} + +void * operator new[](size_t size, int alignment) { + return MEMAllocFromDefaultHeapEx(size, alignment); +} + +void operator delete(void *ptr) { + MEMFreeToDefaultHeap(ptr); +} + +void operator delete(void *ptr, size_t size) { + MEMFreeToDefaultHeap(ptr); +} diff --git a/src/memory.h b/src/memory.h new file mode 100644 index 0000000..dd54872 --- /dev/null +++ b/src/memory.h @@ -0,0 +1,7 @@ + +#pragma once + +#include + +void *operator new(size_t size, int alignment); +void *operator new[](size_t size, int alignment); diff --git a/src/menu.cpp b/src/menu.cpp new file mode 100644 index 0000000..e4f70d9 --- /dev/null +++ b/src/menu.cpp @@ -0,0 +1,44 @@ + +#include "cafe/vpad.h" +#include "menu.h" +#include "screen.h" +#include "input.h" +#include "color.h" + +Menu::Menu(Screen *screen) : screen(screen) { + currentOption = LaunchDisk; + message = nullptr; +} + +Menu::Option Menu::show() { + while (true) { + redraw(); + uint32_t buttons = WaitInput(VPAD_BUTTON_A | VPAD_BUTTON_DOWN | VPAD_BUTTON_UP); + if (buttons & VPAD_BUTTON_A) return (Option)currentOption; + else if (buttons & VPAD_BUTTON_DOWN) { + if (currentOption < 2) currentOption++; + } + else if (buttons & VPAD_BUTTON_UP) { + if (currentOption > 0) currentOption--; + } + } +} + +void Menu::setMessage(const char *message) { + this->message = message; + redraw(); +} + +void Menu::redraw() { + screen->clear(COLOR_BLUE); + screen->drawText(5, 5, "Wii U Debugger"); + screen->drawText(5, 7, "Choose an option:"); + screen->drawText(8, 9, "Install and launch disc"); + screen->drawText(8, 10, "Install and return to system menu"); + screen->drawText(8, 11, "Exit without installing"); + screen->drawText(5, 9 + currentOption, ">"); + if (message) { + screen->drawText(5, 13, message); + } + screen->flip(); +} diff --git a/src/menu.h b/src/menu.h new file mode 100644 index 0000000..e49c428 --- /dev/null +++ b/src/menu.h @@ -0,0 +1,24 @@ + +#pragma once + +#include "screen.h" + +class Menu { + public: + enum Option { + LaunchDisk, + ReturnToMenu, + Exit + }; + + Menu(Screen *screen); + Option show(); + void setMessage(const char *message); + void redraw(); + + private: + Screen *screen; + int currentOption; + + const char *message; +}; diff --git a/src/patches.cpp b/src/patches.cpp new file mode 100644 index 0000000..c013a28 --- /dev/null +++ b/src/patches.cpp @@ -0,0 +1,57 @@ + +#include "cafe/coreinit.h" +#include "kernel.h" + +#include + +int OSSetExceptionCallback_Patch() { + return 0; +} + +int OSSetExceptionCallbackEx_Patch() { + return 0; +} + +bool OSIsDebuggerInitialized_Patch() { + return true; +} + +void Patch(void *funcPtr, void *patchPtr) { + OSDynLoad_NotifyData *sectionInfo = MainRPL->notifyData; + + uint32_t func = (uint32_t)funcPtr; + uint32_t patch = (uint32_t)patchPtr; + if (func < 0x01800000) { //OS function (with trampoline) + for (uint32_t addr = sectionInfo->textAddr; addr < 0x10000000; addr += 4) { + uint32_t *instrs = (uint32_t *)addr; + if (instrs[0] == (0x3D600000 | (func >> 16)) && //lis r11, func@h + instrs[1] == (0x616B0000 | (func & 0xFFFF)) && //ori r11, r11, func@l + instrs[2] == 0x7D6903A6 && //mtctr r11 + instrs[3] == 0x4E800420) //bctr + { + KernelWriteU32(addr, 0x3D600000 | (patch >> 16)); //lis r11, patch@h + KernelWriteU32(addr + 4, 0x616B0000 | (patch & 0xFFFF)); //ori r11, r11, patch@l + } + } + } + else { //Dynamic function + for (uint32_t addr = sectionInfo->textAddr; addr < 0x10000000; addr += 4) { + uint32_t instr = *(uint32_t *)addr; + if ((instr & 0xFC000002) == 0x48000000) { //b or bl + if ((instr & 0x03FFFFFC) == func - addr) { + instr = instr & ~0x03FFFFFC; + instr |= patch; + instr |= 2; //Turn b/bl into ba/bla + KernelWriteU32(addr, instr); + } + } + } + } +} + +void ApplyPatches() { + Patch((void *)OSSetExceptionCallback, (void *)OSSetExceptionCallback_Patch); + Patch((void *)OSSetExceptionCallbackEx, (void *)OSSetExceptionCallbackEx_Patch); + + Patch((void *)OSIsDebuggerInitialized, (void *)OSIsDebuggerInitialized_Patch); +} diff --git a/src/patches.h b/src/patches.h new file mode 100644 index 0000000..ccb6a04 --- /dev/null +++ b/src/patches.h @@ -0,0 +1,4 @@ + +#pragma once + +void ApplyPatches(); diff --git a/src/screen.cpp b/src/screen.cpp new file mode 100644 index 0000000..ffbb1e7 --- /dev/null +++ b/src/screen.cpp @@ -0,0 +1,87 @@ + +#include "cafe/coreinit.h" +#include "screen.h" +#include "memory.h" + +Screen::Screen() : screenBuffer(0) {} +Screen::~Screen() { + if (screenBuffer) { + operator delete(screenBuffer); + } +} + +void Screen::init() { + OSScreenInit(); + + uint32_t bufferSize0 = OSScreenGetBufferSizeEx(0); + uint32_t bufferSize1 = OSScreenGetBufferSizeEx(1); + screenBuffer = operator new(bufferSize0 + bufferSize1, 0x40); + OSScreenSetBufferEx(0, screenBuffer); + OSScreenSetBufferEx(1, (char *)screenBuffer + bufferSize0); + + OSScreenEnableEx(0, 1); + OSScreenEnableEx(1, 1); + OSScreenClearBufferEx(0, 0); + OSScreenClearBufferEx(1, 0); + OSScreenFlipBuffersEx(0); + OSScreenFlipBuffersEx(1); +} + +void Screen::clear(Display screen, uint32_t color) { + OSScreenClearBufferEx(screen, color); +} + +void Screen::drawRect(Display screen, int x1, int y1, int x2, int y2, uint32_t color) { + for (int x = x1; x < x2; x++) { + OSScreenPutPixelEx(screen, x, y1, color); + OSScreenPutPixelEx(screen, x, y2, color); + } + for (int y = y1; y < y2; y++) { + OSScreenPutPixelEx(screen, x1, y, color); + OSScreenPutPixelEx(screen, x2, y, color); + } +} + +void Screen::fillRect(Display screen, int x1, int y1, int x2, int y2, uint32_t color) { + for (int x = x1; x < x2; x++) { + for (int y = y1; y < y2; y++) { + OSScreenPutPixelEx(screen, x, y, color); + } + } +} + +void Screen::drawText(Display screen, int x, int y, const char *text) { + OSScreenPutFontEx(screen, x, y, text); +} + +void Screen::flip(Display screen) { + OSScreenFlipBuffersEx(screen); +} + +int Screen::convx(int x) { return x * 854 / 1280; } +int Screen::convy(int y) { return y * 480 / 720; } + +void Screen::clear(uint32_t color) { + clear(TV, color); + clear(DRC, color); +} + +void Screen::drawRect(int x1, int y1, int x2, int y2, uint32_t color) { + drawRect(TV, x1, y1, x2, y2, color); + drawRect(DRC, convx(x1), convy(y1), convx(x2), convy(y2), color); +} + +void Screen::fillRect(int x1, int y1, int x2, int y2, uint32_t color) { + fillRect(TV, x1, y1, x2, y2, color); + fillRect(DRC, convx(x1), convy(y1), convx(x2), convy(y2), color); +} + +void Screen::drawText(int x, int y, const char *text) { + drawText(TV, x, y, text); + drawText(DRC, x, y, text); +} + +void Screen::flip() { + flip(TV); + flip(DRC); +} diff --git a/src/screen.h b/src/screen.h new file mode 100644 index 0000000..b4d722a --- /dev/null +++ b/src/screen.h @@ -0,0 +1,34 @@ + +#pragma once + +#include + +class Screen { + public: + enum Display { + TV, + DRC + }; + + Screen(); + ~Screen(); + + void init(); + void clear(uint32_t color); + void drawRect(int x1, int y1, int x2, int y2, uint32_t color); + void fillRect(int x1, int y1, int x2, int y2, uint32_t color); + void drawText(int x, int y, const char *text); + void flip(); + + void clear(Display screen, uint32_t color); + void drawRect(Display screen, int x1, int y1, int x2, int y2, uint32_t color); + void fillRect(Display screen, int x1, int y1, int x2, int y2, uint32_t color); + void drawText(Display screen, int x, int y, const char *text); + void flip(Display screen); + + private: + void *screenBuffer; + + int convx(int x); + int convy(int y); +}; diff --git a/src/socket.cpp b/src/socket.cpp new file mode 100644 index 0000000..f57da73 --- /dev/null +++ b/src/socket.cpp @@ -0,0 +1,101 @@ + +#include "cafe/nsysnet.h" + +#include "socket.h" + +Socket::Socket() : sock(-1) {} + +Socket::~Socket() { + if (sock >= 0) { + socketclose(sock); + } +} + +bool Socket::init(Type type) { + if (type == TCP) { + sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); + } + else { + sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + } + return sock >= 0; +} + +bool Socket::close() { + if (sock >= 0) { + int result = socketclose(sock); + sock = -1; + return result == 0; + } + return true; +} + + +bool Client::sendall(const void *data, size_t length) { + size_t sent = 0; + while (sent < length) { + int num = send(sock, data, length - sent, 0); + if (num < 0) { + close(); + return false; + } + + sent += num; + data = (const char *)data + num; + } + return true; +} + +bool Client::recvall(void *data, size_t length) { + size_t received = 0; + while (received < length) { + int num = recv(sock, data, length - received, 0); + if (num <= 0) { + close(); + return false; + } + + received += num; + data = (char *)data + num; + } + return true; +} + + +bool Server::bind(int port) { + uint32_t reuseaddr = 1; + int result = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, 4); + if (result < 0) { + close(); + return false; + } + + sockaddr serverAddr = {0}; + serverAddr.family = AF_INET; + serverAddr.port = port; + serverAddr.addr = 0; + + result = ::bind(sock, &serverAddr, 16); + if (result < 0) { + close(); + return false; + } + return true; +} + +bool Server::accept(Client *client) { + int result = listen(sock, 1); + if (result < 0) { + close(); + return false; + } + + int fd = ::accept(sock, 0, 0); + if (fd < 0) { + close(); + return false; + } + + client->sock = fd; + return true; +} diff --git a/src/socket.h b/src/socket.h new file mode 100644 index 0000000..88ee6ec --- /dev/null +++ b/src/socket.h @@ -0,0 +1,31 @@ + +#pragma once + +#include + +class Socket { +public: + enum Type { + TCP, + UDP + }; + + Socket(); + ~Socket(); + bool init(Type type); + bool close(); + + int sock; +}; + +class Client : public Socket { +public: + bool sendall(const void *data, size_t length); + bool recvall(void *data, size_t length); +}; + +class Server : public Socket { +public: + bool bind(int port); + bool accept(Client *client); +};