diff options
Diffstat (limited to 'src/core')
41 files changed, 1996 insertions, 2117 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index b161c05ba..c733e5d21 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,6 +1,4 @@ set(SRCS - arm/disassembler/arm_disasm.cpp - arm/disassembler/load_symbol_map.cpp arm/dynarmic/arm_dynarmic.cpp arm/dynarmic/arm_dynarmic_cp15.cpp arm/dyncom/arm_dyncom.cpp @@ -109,6 +107,7 @@ set(SRCS hle/service/hid/hid_spvr.cpp hle/service/hid/hid_user.cpp hle/service/http_c.cpp + hle/service/ir/extra_hid.cpp hle/service/ir/ir.cpp hle/service/ir/ir_rst.cpp hle/service/ir/ir_u.cpp @@ -140,6 +139,7 @@ set(SRCS hle/service/nwm/nwm_soc.cpp hle/service/nwm/nwm_tst.cpp hle/service/nwm/nwm_uds.cpp + hle/service/nwm/uds_beacon.cpp hle/service/pm_app.cpp hle/service/ptm/ptm.cpp hle/service/ptm/ptm_gets.cpp @@ -178,8 +178,6 @@ set(SRCS set(HEADERS arm/arm_interface.h - arm/disassembler/arm_disasm.h - arm/disassembler/load_symbol_map.h arm/dynarmic/arm_dynarmic.h arm/dynarmic/arm_dynarmic_cp15.h arm/dyncom/arm_dyncom.h @@ -298,6 +296,7 @@ set(HEADERS hle/service/hid/hid_spvr.h hle/service/hid/hid_user.h hle/service/http_c.h + hle/service/ir/extra_hid.h hle/service/ir/ir.h hle/service/ir/ir_rst.h hle/service/ir/ir_u.h @@ -329,6 +328,7 @@ set(HEADERS hle/service/nwm/nwm_soc.h hle/service/nwm/nwm_tst.h hle/service/nwm/nwm_uds.h + hle/service/nwm/uds_beacon.h hle/service/pm_app.h hle/service/ptm/ptm.h hle/service/ptm/ptm_gets.h diff --git a/src/core/arm/disassembler/arm_disasm.cpp b/src/core/arm/disassembler/arm_disasm.cpp deleted file mode 100644 index 05d6ed1fb..000000000 --- a/src/core/arm/disassembler/arm_disasm.cpp +++ /dev/null @@ -1,1344 +0,0 @@ -// Copyright 2006 The Android Open Source Project - -#include <string> -#include <unordered_set> -#include "common/common_types.h" -#include "common/string_util.h" -#include "core/arm/disassembler/arm_disasm.h" -#include "core/arm/skyeye_common/armsupp.h" - -static const char* cond_names[] = {"eq", "ne", "cs", "cc", "mi", "pl", "vs", "vc", - "hi", "ls", "ge", "lt", "gt", "le", "", "RESERVED"}; - -static const char* opcode_names[] = { - "invalid", "undefined", "adc", "add", "and", "b", "bl", "bic", - "bkpt", "blx", "bx", "cdp", "clrex", "clz", "cmn", "cmp", - "eor", "ldc", "ldm", "ldr", "ldrb", "ldrbt", "ldrex", "ldrexb", - "ldrexd", "ldrexh", "ldrh", "ldrsb", "ldrsh", "ldrt", "mcr", "mla", - "mov", "mrc", "mrs", "msr", "mul", "mvn", "nop", "orr", - "pkh", "pld", "qadd16", "qadd8", "qasx", "qsax", "qsub16", "qsub8", - "rev", "rev16", "revsh", "rsb", "rsc", "sadd16", "sadd8", "sasx", - "sbc", "sel", "sev", "shadd16", "shadd8", "shasx", "shsax", "shsub16", - "shsub8", "smlad", "smlal", "smlald", "smlsd", "smlsld", "smmla", "smmls", - "smmul", "smuad", "smull", "smusd", "ssat", "ssat16", "ssax", "ssub16", - "ssub8", "stc", "stm", "str", "strb", "strbt", "strex", "strexb", - "strexd", "strexh", "strh", "strt", "sub", "swi", "swp", "swpb", - "sxtab", "sxtab16", "sxtah", "sxtb", "sxtb16", "sxth", "teq", "tst", - "uadd16", "uadd8", "uasx", "uhadd16", "uhadd8", "uhasx", "uhsax", "uhsub16", - "uhsub8", "umlal", "umull", "uqadd16", "uqadd8", "uqasx", "uqsax", "uqsub16", - "uqsub8", "usad8", "usada8", "usat", "usat16", "usax", "usub16", "usub8", - "uxtab", "uxtab16", "uxtah", "uxtb", "uxtb16", "uxth", "wfe", "wfi", - "yield", - - "undefined", "adc", "add", "and", "asr", "b", "bic", "bkpt", - "bl", "blx", "bx", "cmn", "cmp", "eor", "ldmia", "ldr", - "ldrb", "ldrh", "ldrsb", "ldrsh", "lsl", "lsr", "mov", "mul", - "mvn", "neg", "orr", "pop", "push", "ror", "sbc", "stmia", - "str", "strb", "strh", "sub", "swi", "tst", - - nullptr}; - -// Indexed by the shift type (bits 6-5) -static const char* shift_names[] = {"LSL", "LSR", "ASR", "ROR"}; - -static const char* cond_to_str(u32 cond) { - return cond_names[cond]; -} - -std::string ARM_Disasm::Disassemble(u32 addr, u32 insn) { - Opcode opcode = Decode(insn); - switch (opcode) { - case OP_INVALID: - return "Invalid"; - case OP_UNDEFINED: - return "Undefined"; - case OP_ADC: - case OP_ADD: - case OP_AND: - case OP_BIC: - case OP_CMN: - case OP_CMP: - case OP_EOR: - case OP_MOV: - case OP_MVN: - case OP_ORR: - case OP_RSB: - case OP_RSC: - case OP_SBC: - case OP_SUB: - case OP_TEQ: - case OP_TST: - return DisassembleALU(opcode, insn); - case OP_B: - case OP_BL: - return DisassembleBranch(addr, opcode, insn); - case OP_BKPT: - return DisassembleBKPT(insn); - case OP_BLX: - // not supported yet - break; - case OP_BX: - return DisassembleBX(insn); - case OP_CDP: - return "cdp"; - case OP_CLREX: - return "clrex"; - case OP_CLZ: - return DisassembleCLZ(insn); - case OP_LDC: - return "ldc"; - case OP_LDM: - case OP_STM: - return DisassembleMemblock(opcode, insn); - case OP_LDR: - case OP_LDRB: - case OP_LDRBT: - case OP_LDRT: - case OP_STR: - case OP_STRB: - case OP_STRBT: - case OP_STRT: - return DisassembleMem(insn); - case OP_LDREX: - case OP_LDREXB: - case OP_LDREXD: - case OP_LDREXH: - case OP_STREX: - case OP_STREXB: - case OP_STREXD: - case OP_STREXH: - return DisassembleREX(opcode, insn); - case OP_LDRH: - case OP_LDRSB: - case OP_LDRSH: - case OP_STRH: - return DisassembleMemHalf(insn); - case OP_MCR: - case OP_MRC: - return DisassembleMCR(opcode, insn); - case OP_MLA: - return DisassembleMLA(opcode, insn); - case OP_MRS: - return DisassembleMRS(insn); - case OP_MSR: - return DisassembleMSR(insn); - case OP_MUL: - return DisassembleMUL(opcode, insn); - case OP_NOP: - case OP_SEV: - case OP_WFE: - case OP_WFI: - case OP_YIELD: - return DisassembleNoOperands(opcode, insn); - case OP_PKH: - return DisassemblePKH(insn); - case OP_PLD: - return DisassemblePLD(insn); - case OP_QADD16: - case OP_QADD8: - case OP_QASX: - case OP_QSAX: - case OP_QSUB16: - case OP_QSUB8: - case OP_SADD16: - case OP_SADD8: - case OP_SASX: - case OP_SHADD16: - case OP_SHADD8: - case OP_SHASX: - case OP_SHSAX: - case OP_SHSUB16: - case OP_SHSUB8: - case OP_SSAX: - case OP_SSUB16: - case OP_SSUB8: - case OP_UADD16: - case OP_UADD8: - case OP_UASX: - case OP_UHADD16: - case OP_UHADD8: - case OP_UHASX: - case OP_UHSAX: - case OP_UHSUB16: - case OP_UHSUB8: - case OP_UQADD16: - case OP_UQADD8: - case OP_UQASX: - case OP_UQSAX: - case OP_UQSUB16: - case OP_UQSUB8: - case OP_USAX: - case OP_USUB16: - case OP_USUB8: - return DisassembleParallelAddSub(opcode, insn); - case OP_REV: - case OP_REV16: - case OP_REVSH: - return DisassembleREV(opcode, insn); - case OP_SEL: - return DisassembleSEL(insn); - case OP_SMLAD: - case OP_SMLALD: - case OP_SMLSD: - case OP_SMLSLD: - case OP_SMMLA: - case OP_SMMLS: - case OP_SMMUL: - case OP_SMUAD: - case OP_SMUSD: - case OP_USAD8: - case OP_USADA8: - return DisassembleMediaMulDiv(opcode, insn); - case OP_SSAT: - case OP_SSAT16: - case OP_USAT: - case OP_USAT16: - return DisassembleSAT(opcode, insn); - case OP_STC: - return "stc"; - case OP_SWI: - return DisassembleSWI(insn); - case OP_SWP: - case OP_SWPB: - return DisassembleSWP(opcode, insn); - case OP_SXTAB: - case OP_SXTAB16: - case OP_SXTAH: - case OP_SXTB: - case OP_SXTB16: - case OP_SXTH: - case OP_UXTAB: - case OP_UXTAB16: - case OP_UXTAH: - case OP_UXTB: - case OP_UXTB16: - case OP_UXTH: - return DisassembleXT(opcode, insn); - case OP_UMLAL: - case OP_UMULL: - case OP_SMLAL: - case OP_SMULL: - return DisassembleUMLAL(opcode, insn); - default: - return "Error"; - } - return nullptr; -} - -std::string ARM_Disasm::DisassembleALU(Opcode opcode, u32 insn) { - static const u8 kNoOperand1 = 1; - static const u8 kNoDest = 2; - static const u8 kNoSbit = 4; - - std::string rn_str; - std::string rd_str; - - u8 flags = 0; - u8 cond = (insn >> 28) & 0xf; - u8 is_immed = (insn >> 25) & 0x1; - u8 bit_s = (insn >> 20) & 1; - u8 rn = (insn >> 16) & 0xf; - u8 rd = (insn >> 12) & 0xf; - u8 immed = insn & 0xff; - - const char* opname = opcode_names[opcode]; - switch (opcode) { - case OP_CMN: - case OP_CMP: - case OP_TEQ: - case OP_TST: - flags = kNoDest | kNoSbit; - break; - case OP_MOV: - case OP_MVN: - flags = kNoOperand1; - break; - default: - break; - } - - // The "mov" instruction ignores the first operand (rn). - rn_str[0] = 0; - if ((flags & kNoOperand1) == 0) { - rn_str = Common::StringFromFormat("r%d, ", rn); - } - - // The following instructions do not write the result register (rd): - // tst, teq, cmp, cmn. - rd_str[0] = 0; - if ((flags & kNoDest) == 0) { - rd_str = Common::StringFromFormat("r%d, ", rd); - } - - const char* sbit_str = ""; - if (bit_s && !(flags & kNoSbit)) - sbit_str = "s"; - - if (is_immed) { - return Common::StringFromFormat("%s%s%s\t%s%s#%u ; 0x%x", opname, cond_to_str(cond), - sbit_str, rd_str.c_str(), rn_str.c_str(), immed, immed); - } - - u8 shift_is_reg = (insn >> 4) & 1; - u8 rotate = (insn >> 8) & 0xf; - u8 rm = insn & 0xf; - u8 shift_type = (insn >> 5) & 0x3; - u8 rs = (insn >> 8) & 0xf; - u8 shift_amount = (insn >> 7) & 0x1f; - u32 rotated_val = immed; - u8 rotate2 = rotate << 1; - rotated_val = (rotated_val >> rotate2) | (rotated_val << (32 - rotate2)); - - if (!shift_is_reg && shift_type == 0 && shift_amount == 0) { - return Common::StringFromFormat("%s%s%s\t%s%sr%d", opname, cond_to_str(cond), sbit_str, - rd_str.c_str(), rn_str.c_str(), rm); - } - - const char* shift_name = shift_names[shift_type]; - if (shift_is_reg) { - return Common::StringFromFormat("%s%s%s\t%s%sr%d, %s r%d", opname, cond_to_str(cond), - sbit_str, rd_str.c_str(), rn_str.c_str(), rm, shift_name, - rs); - } - if (shift_amount == 0) { - if (shift_type == 3) { - return Common::StringFromFormat("%s%s%s\t%s%sr%d, RRX", opname, cond_to_str(cond), - sbit_str, rd_str.c_str(), rn_str.c_str(), rm); - } - shift_amount = 32; - } - return Common::StringFromFormat("%s%s%s\t%s%sr%d, %s #%u", opname, cond_to_str(cond), sbit_str, - rd_str.c_str(), rn_str.c_str(), rm, shift_name, shift_amount); -} - -std::string ARM_Disasm::DisassembleBranch(u32 addr, Opcode opcode, u32 insn) { - u8 cond = (insn >> 28) & 0xf; - u32 offset = insn & 0xffffff; - // Sign-extend the 24-bit offset - if ((offset >> 23) & 1) - offset |= 0xff000000; - - // Pre-compute the left-shift and the prefetch offset - offset <<= 2; - offset += 8; - addr += offset; - const char* opname = opcode_names[opcode]; - return Common::StringFromFormat("%s%s\t0x%x", opname, cond_to_str(cond), addr); -} - -std::string ARM_Disasm::DisassembleBX(u32 insn) { - u8 cond = (insn >> 28) & 0xf; - u8 rn = insn & 0xf; - return Common::StringFromFormat("bx%s\tr%d", cond_to_str(cond), rn); -} - -std::string ARM_Disasm::DisassembleBKPT(u32 insn) { - u8 cond = (insn >> 28) & 0xf; - u32 immed = (((insn >> 8) & 0xfff) << 4) | (insn & 0xf); - return Common::StringFromFormat("bkpt%s\t#%d", cond_to_str(cond), immed); -} - -std::string ARM_Disasm::DisassembleCLZ(u32 insn) { - u8 cond = (insn >> 28) & 0xf; - u8 rd = (insn >> 12) & 0xf; - u8 rm = insn & 0xf; - return Common::StringFromFormat("clz%s\tr%d, r%d", cond_to_str(cond), rd, rm); -} - -std::string ARM_Disasm::DisassembleMediaMulDiv(Opcode opcode, u32 insn) { - u32 cond = BITS(insn, 28, 31); - u32 rd = BITS(insn, 16, 19); - u32 ra = BITS(insn, 12, 15); - u32 rm = BITS(insn, 8, 11); - u32 m = BIT(insn, 5); - u32 rn = BITS(insn, 0, 3); - - std::string cross = ""; - if (m) { - if (opcode == OP_SMMLA || opcode == OP_SMMUL || opcode == OP_SMMLS) - cross = "r"; - else - cross = "x"; - } - - std::string ext_reg = ""; - std::unordered_set<Opcode, std::hash<int>> with_ext_reg = {OP_SMLAD, OP_SMLSD, OP_SMMLA, - OP_SMMLS, OP_USADA8}; - if (with_ext_reg.find(opcode) != with_ext_reg.end()) - ext_reg = Common::StringFromFormat(", r%u", ra); - - std::string rd_low = ""; - if (opcode == OP_SMLALD || opcode == OP_SMLSLD) - rd_low = Common::StringFromFormat("r%u, ", ra); - - return Common::StringFromFormat("%s%s%s\t%sr%u, r%u, r%u%s", opcode_names[opcode], - cross.c_str(), cond_to_str(cond), rd_low.c_str(), rd, rn, rm, - ext_reg.c_str()); -} - -std::string ARM_Disasm::DisassembleMemblock(Opcode opcode, u32 insn) { - std::string tmp_list; - - u8 cond = (insn >> 28) & 0xf; - u8 write_back = (insn >> 21) & 0x1; - u8 bit_s = (insn >> 22) & 0x1; - u8 is_up = (insn >> 23) & 0x1; - u8 is_pre = (insn >> 24) & 0x1; - u8 rn = (insn >> 16) & 0xf; - u16 reg_list = insn & 0xffff; - - const char* opname = opcode_names[opcode]; - - const char* bang = ""; - if (write_back) - bang = "!"; - - const char* carret = ""; - if (bit_s) - carret = "^"; - - const char* comma = ""; - tmp_list[0] = 0; - for (int ii = 0; ii < 16; ++ii) { - if (reg_list & (1 << ii)) { - tmp_list += Common::StringFromFormat("%sr%d", comma, ii); - comma = ","; - } - } - - const char* addr_mode = ""; - if (is_pre) { - if (is_up) { - addr_mode = "ib"; - } else { - addr_mode = "db"; - } - } else { - if (is_up) { - addr_mode = "ia"; - } else { - addr_mode = "da"; - } - } - - return Common::StringFromFormat("%s%s%s\tr%d%s, {%s}%s", opname, cond_to_str(cond), addr_mode, - rn, bang, tmp_list.c_str(), carret); -} - -std::string ARM_Disasm::DisassembleMem(u32 insn) { - u8 cond = (insn >> 28) & 0xf; - u8 is_reg = (insn >> 25) & 0x1; - u8 is_load = (insn >> 20) & 0x1; - u8 write_back = (insn >> 21) & 0x1; - u8 is_byte = (insn >> 22) & 0x1; - u8 is_up = (insn >> 23) & 0x1; - u8 is_pre = (insn >> 24) & 0x1; - u8 rn = (insn >> 16) & 0xf; - u8 rd = (insn >> 12) & 0xf; - u16 offset = insn & 0xfff; - - const char* opname = "ldr"; - if (!is_load) - opname = "str"; - - const char* bang = ""; - if (write_back) - bang = "!"; - - const char* minus = ""; - if (is_up == 0) - minus = "-"; - - const char* byte = ""; - if (is_byte) - byte = "b"; - - if (is_reg == 0) { - if (is_pre) { - if (offset == 0) { - return Common::StringFromFormat("%s%s%s\tr%d, [r%d]", opname, cond_to_str(cond), - byte, rd, rn); - } else { - return Common::StringFromFormat("%s%s%s\tr%d, [r%d, #%s%u]%s", opname, - cond_to_str(cond), byte, rd, rn, minus, offset, - bang); - } - } else { - const char* transfer = ""; - if (write_back) - transfer = "t"; - - return Common::StringFromFormat("%s%s%s%s\tr%d, [r%d], #%s%u", opname, - cond_to_str(cond), byte, transfer, rd, rn, minus, - offset); - } - } - - u8 rm = insn & 0xf; - u8 shift_type = (insn >> 5) & 0x3; - u8 shift_amount = (insn >> 7) & 0x1f; - - const char* shift_name = shift_names[shift_type]; - - if (is_pre) { - if (shift_amount == 0) { - if (shift_type == 0) { - return Common::StringFromFormat("%s%s%s\tr%d, [r%d, %sr%d]%s", opname, - cond_to_str(cond), byte, rd, rn, minus, rm, bang); - } - if (shift_type == 3) { - return Common::StringFromFormat("%s%s%s\tr%d, [r%d, %sr%d, RRX]%s", opname, - cond_to_str(cond), byte, rd, rn, minus, rm, bang); - } - shift_amount = 32; - } - return Common::StringFromFormat("%s%s%s\tr%d, [r%d, %sr%d, %s #%u]%s", opname, - cond_to_str(cond), byte, rd, rn, minus, rm, shift_name, - shift_amount, bang); - } - - const char* transfer = ""; - if (write_back) - transfer = "t"; - - if (shift_amount == 0) { - if (shift_type == 0) { - return Common::StringFromFormat("%s%s%s%s\tr%d, [r%d], %sr%d", opname, - cond_to_str(cond), byte, transfer, rd, rn, minus, rm); - } - if (shift_type == 3) { - return Common::StringFromFormat("%s%s%s%s\tr%d, [r%d], %sr%d, RRX", opname, - cond_to_str(cond), byte, transfer, rd, rn, minus, rm); - } - shift_amount = 32; - } - - return Common::StringFromFormat("%s%s%s%s\tr%d, [r%d], %sr%d, %s #%u", opname, - cond_to_str(cond), byte, transfer, rd, rn, minus, rm, - shift_name, shift_amount); -} - -std::string ARM_Disasm::DisassembleMemHalf(u32 insn) { - u8 cond = (insn >> 28) & 0xf; - u8 is_load = (insn >> 20) & 0x1; - u8 write_back = (insn >> 21) & 0x1; - u8 is_immed = (insn >> 22) & 0x1; - u8 is_up = (insn >> 23) & 0x1; - u8 is_pre = (insn >> 24) & 0x1; - u8 rn = (insn >> 16) & 0xf; - u8 rd = (insn >> 12) & 0xf; - u8 bits_65 = (insn >> 5) & 0x3; - u8 rm = insn & 0xf; - u8 offset = (((insn >> 8) & 0xf) << 4) | (insn & 0xf); - - const char* opname = "ldr"; - if (is_load == 0) - opname = "str"; - - const char* width = ""; - if (bits_65 == 1) - width = "h"; - else if (bits_65 == 2) - width = "sb"; - else - width = "sh"; - - const char* bang = ""; - if (write_back) - bang = "!"; - const char* minus = ""; - if (is_up == 0) - minus = "-"; - - if (is_immed) { - if (is_pre) { - if (offset == 0) { - return Common::StringFromFormat("%s%s%s\tr%d, [r%d]", opname, cond_to_str(cond), - width, rd, rn); - } else { - return Common::StringFromFormat("%s%s%s\tr%d, [r%d, #%s%u]%s", opname, - cond_to_str(cond), width, rd, rn, minus, offset, - bang); - } - } else { - return Common::StringFromFormat("%s%s%s\tr%d, [r%d], #%s%u", opname, cond_to_str(cond), - width, rd, rn, minus, offset); - } - } - - if (is_pre) { - return Common::StringFromFormat("%s%s%s\tr%d, [r%d, %sr%d]%s", opname, cond_to_str(cond), - width, rd, rn, minus, rm, bang); - } else { - return Common::StringFromFormat("%s%s%s\tr%d, [r%d], %sr%d", opname, cond_to_str(cond), - width, rd, rn, minus, rm); - } -} - -std::string ARM_Disasm::DisassembleMCR(Opcode opcode, u32 insn) { - u8 cond = (insn >> 28) & 0xf; - u8 crn = (insn >> 16) & 0xf; - u8 crd = (insn >> 12) & 0xf; - u8 cpnum = (insn >> 8) & 0xf; - u8 opcode2 = (insn >> 5) & 0x7; - u8 crm = insn & 0xf; - - const char* opname = opcode_names[opcode]; - return Common::StringFromFormat("%s%s\t%d, 0, r%d, cr%d, cr%d, {%d}", opname, cond_to_str(cond), - cpnum, crd, crn, crm, opcode2); -} - -std::string ARM_Disasm::DisassembleMLA(Opcode opcode, u32 insn) { - u8 cond = (insn >> 28) & 0xf; - u8 rd = (insn >> 16) & 0xf; - u8 rn = (insn >> 12) & 0xf; - u8 rs = (insn >> 8) & 0xf; - u8 rm = insn & 0xf; - u8 bit_s = (insn >> 20) & 1; - - const char* opname = opcode_names[opcode]; - return Common::StringFromFormat("%s%s%s\tr%d, r%d, r%d, r%d", opname, cond_to_str(cond), - bit_s ? "s" : "", rd, rm, rs, rn); -} - -std::string ARM_Disasm::DisassembleUMLAL(Opcode opcode, u32 insn) { - u8 cond = (insn >> 28) & 0xf; - u8 rdhi = (insn >> 16) & 0xf; - u8 rdlo = (insn >> 12) & 0xf; - u8 rs = (insn >> 8) & 0xf; - u8 rm = insn & 0xf; - u8 bit_s = (insn >> 20) & 1; - - const char* opname = opcode_names[opcode]; - return Common::StringFromFormat("%s%s%s\tr%d, r%d, r%d, r%d", opname, cond_to_str(cond), - bit_s ? "s" : "", rdlo, rdhi, rm, rs); -} - -std::string ARM_Disasm::DisassembleMUL(Opcode opcode, u32 insn) { - u8 cond = (insn >> 28) & 0xf; - u8 rd = (insn >> 16) & 0xf; - u8 rs = (insn >> 8) & 0xf; - u8 rm = insn & 0xf; - u8 bit_s = (insn >> 20) & 1; - - const char* opname = opcode_names[opcode]; - return Common::StringFromFormat("%s%s%s\tr%d, r%d, r%d", opname, cond_to_str(cond), - bit_s ? "s" : "", rd, rm, rs); -} - -std::string ARM_Disasm::DisassembleMRS(u32 insn) { - u8 cond = (insn >> 28) & 0xf; - u8 rd = (insn >> 12) & 0xf; - u8 ps = (insn >> 22) & 1; - - return Common::StringFromFormat("mrs%s\tr%d, %s", cond_to_str(cond), rd, ps ? "spsr" : "cpsr"); -} - -std::string ARM_Disasm::DisassembleMSR(u32 insn) { - char flags[8]; - int flag_index = 0; - u8 cond = (insn >> 28) & 0xf; - u8 is_immed = (insn >> 25) & 0x1; - u8 pd = (insn >> 22) & 1; - u8 mask = (insn >> 16) & 0xf; - - if (mask & 1) - flags[flag_index++] = 'c'; - if (mask & 2) - flags[flag_index++] = 'x'; - if (mask & 4) - flags[flag_index++] = 's'; - if (mask & 8) - flags[flag_index++] = 'f'; - flags[flag_index] = 0; - - if (is_immed) { - u32 immed = insn & 0xff; - u8 rotate = (insn >> 8) & 0xf; - u8 rotate2 = rotate << 1; - u32 rotated_val = (immed >> rotate2) | (immed << (32 - rotate2)); - return Common::StringFromFormat("msr%s\t%s_%s, #0x%x", cond_to_str(cond), - pd ? "spsr" : "cpsr", flags, rotated_val); - } - - u8 rm = insn & 0xf; - - return Common::StringFromFormat("msr%s\t%s_%s, r%d", cond_to_str(cond), pd ? "spsr" : "cpsr", - flags, rm); -} - -std::string ARM_Disasm::DisassembleNoOperands(Opcode opcode, u32 insn) { - u32 cond = BITS(insn, 28, 31); - return Common::StringFromFormat("%s%s", opcode_names[opcode], cond_to_str(cond)); -} - -std::string ARM_Disasm::DisassembleParallelAddSub(Opcode opcode, u32 insn) { - u32 cond = BITS(insn, 28, 31); - u32 rn = BITS(insn, 16, 19); - u32 rd = BITS(insn, 12, 15); - u32 rm = BITS(insn, 0, 3); - - return Common::StringFromFormat("%s%s\tr%u, r%u, r%u", opcode_names[opcode], cond_to_str(cond), - rd, rn, rm); -} - -std::string ARM_Disasm::DisassemblePKH(u32 insn) { - u32 cond = BITS(insn, 28, 31); - u32 rn = BITS(insn, 16, 19); - u32 rd = BITS(insn, 12, 15); - u32 imm5 = BITS(insn, 7, 11); - u32 tb = BIT(insn, 6); - u32 rm = BITS(insn, 0, 3); - - std::string suffix = tb ? "tb" : "bt"; - std::string shift = ""; - - if (tb && imm5 == 0) - imm5 = 32; - - if (imm5 > 0) { - shift = tb ? ", ASR" : ", LSL"; - shift += " #" + std::to_string(imm5); - } - - return Common::StringFromFormat("pkh%s%s\tr%u, r%u, r%u%s", suffix.c_str(), cond_to_str(cond), - rd, rn, rm, shift.c_str()); -} - -std::string ARM_Disasm::DisassemblePLD(u32 insn) { - u8 is_reg = (insn >> 25) & 0x1; - u8 is_up = (insn >> 23) & 0x1; - u8 rn = (insn >> 16) & 0xf; - - const char* minus = ""; - if (is_up == 0) - minus = "-"; - - if (is_reg) { - u8 rm = insn & 0xf; - return Common::StringFromFormat("pld\t[r%d, %sr%d]", rn, minus, rm); - } - - u16 offset = insn & 0xfff; - if (offset == 0) { - return Common::StringFromFormat("pld\t[r%d]", rn); - } else { - return Common::StringFromFormat("pld\t[r%d, #%s%u]", rn, minus, offset); - } -} - -std::string ARM_Disasm::DisassembleREV(Opcode opcode, u32 insn) { - u32 cond = BITS(insn, 28, 31); - u32 rd = BITS(insn, 12, 15); - u32 rm = BITS(insn, 0, 3); - - return Common::StringFromFormat("%s%s\tr%u, r%u", opcode_names[opcode], cond_to_str(cond), rd, - rm); -} - -std::string ARM_Disasm::DisassembleREX(Opcode opcode, u32 insn) { - u32 rn = BITS(insn, 16, 19); - u32 rd = BITS(insn, 12, 15); - u32 rt = BITS(insn, 0, 3); - u32 cond = BITS(insn, 28, 31); - - switch (opcode) { - case OP_STREX: - case OP_STREXB: - case OP_STREXH: - return Common::StringFromFormat("%s%s\tr%d, r%d, [r%d]", opcode_names[opcode], - cond_to_str(cond), rd, rt, rn); - case OP_STREXD: - return Common::StringFromFormat("%s%s\tr%d, r%d, r%d, [r%d]", opcode_names[opcode], - cond_to_str(cond), rd, rt, rt + 1, rn); - - // for LDREX instructions, rd corresponds to Rt from reference manual - case OP_LDREX: - case OP_LDREXB: - case OP_LDREXH: - return Common::StringFromFormat("%s%s\tr%d, [r%d]", opcode_names[opcode], cond_to_str(cond), - rd, rn); - case OP_LDREXD: - return Common::StringFromFormat("%s%s\tr%d, r%d, [r%d]", opcode_names[opcode], - cond_to_str(cond), rd, rd + 1, rn); - default: - return opcode_names[OP_UNDEFINED]; - } -} - -std::string ARM_Disasm::DisassembleSAT(Opcode opcode, u32 insn) { - u32 cond = BITS(insn, 28, 31); - u32 sat_imm = BITS(insn, 16, 20); - u32 rd = BITS(insn, 12, 15); - u32 imm5 = BITS(insn, 7, 11); - u32 sh = BIT(insn, 6); - u32 rn = BITS(insn, 0, 3); - - std::string shift_part = ""; - bool opcode_has_shift = (opcode == OP_SSAT) || (opcode == OP_USAT); - if (opcode_has_shift && !(sh == 0 && imm5 == 0)) { - if (sh == 0) - shift_part += ", LSL #"; - else - shift_part += ", ASR #"; - - if (imm5 == 0) - imm5 = 32; - shift_part += std::to_string(imm5); - } - - if (opcode == OP_SSAT || opcode == OP_SSAT16) - sat_imm++; - - return Common::StringFromFormat("%s%s\tr%u, #%u, r%u%s", opcode_names[opcode], - cond_to_str(cond), rd, sat_imm, rn, shift_part.c_str()); -} - -std::string ARM_Disasm::DisassembleSEL(u32 insn) { - u32 cond = BITS(insn, 28, 31); - u32 rn = BITS(insn, 16, 19); - u32 rd = BITS(insn, 12, 15); - u32 rm = BITS(insn, 0, 3); - - return Common::StringFromFormat("%s%s\tr%u, r%u, r%u", opcode_names[OP_SEL], cond_to_str(cond), - rd, rn, rm); -} - -std::string ARM_Disasm::DisassembleSWI(u32 insn) { - u8 cond = (insn >> 28) & 0xf; - u32 sysnum = insn & 0x00ffffff; - - return Common::StringFromFormat("swi%s 0x%x", cond_to_str(cond), sysnum); -} - -std::string ARM_Disasm::DisassembleSWP(Opcode opcode, u32 insn) { - u8 cond = (insn >> 28) & 0xf; - u8 rn = (insn >> 16) & 0xf; - u8 rd = (insn >> 12) & 0xf; - u8 rm = insn & 0xf; - - const char* opname = opcode_names[opcode]; - return Common::StringFromFormat("%s%s\tr%d, r%d, [r%d]", opname, cond_to_str(cond), rd, rm, rn); -} - -std::string ARM_Disasm::DisassembleXT(Opcode opcode, u32 insn) { - u32 cond = BITS(insn, 28, 31); - u32 rn = BITS(insn, 16, 19); - u32 rd = BITS(insn, 12, 15); - u32 rotate = BITS(insn, 10, 11); - u32 rm = BITS(insn, 0, 3); - - std::string rn_part = ""; - static std::unordered_set<Opcode, std::hash<int>> extend_with_add = { - OP_SXTAB, OP_SXTAB16, OP_SXTAH, OP_UXTAB, OP_UXTAB16, OP_UXTAH}; - if (extend_with_add.find(opcode) != extend_with_add.end()) - rn_part = ", r" + std::to_string(rn); - - std::string rotate_part = ""; - if (rotate != 0) - rotate_part = ", ROR #" + std::to_string(rotate << 3); - - return Common::StringFromFormat("%s%s\tr%u%s, r%u%s", opcode_names[opcode], cond_to_str(cond), - rd, rn_part.c_str(), rm, rotate_part.c_str()); -} - -Opcode ARM_Disasm::Decode(u32 insn) { - u32 bits27_26 = (insn >> 26) & 0x3; - switch (bits27_26) { - case 0x0: - return Decode00(insn); - case 0x1: - return Decode01(insn); - case 0x2: - return Decode10(insn); - case 0x3: - return Decode11(insn); - } - return OP_INVALID; -} - -Opcode ARM_Disasm::Decode00(u32 insn) { - u8 bit25 = (insn >> 25) & 0x1; - u8 bit4 = (insn >> 4) & 0x1; - if (bit25 == 0 && bit4 == 1) { - if ((insn & 0x0ffffff0) == 0x012fff10) { - // Bx instruction - return OP_BX; - } - if ((insn & 0x0ff000f0) == 0x01600010) { - // Clz instruction - return OP_CLZ; - } - if ((insn & 0xfff000f0) == 0xe1200070) { - // Bkpt instruction - return OP_BKPT; - } - u32 bits7_4 = (insn >> 4) & 0xf; - if (bits7_4 == 0x9) { - u32 bit24 = BIT(insn, 24); - if (bit24) { - return DecodeSyncPrimitive(insn); - } - // One of the multiply instructions - return DecodeMUL(insn); - } - - u8 bit7 = (insn >> 7) & 0x1; - if (bit7 == 1) { - // One of the load/store halfword/byte instructions - return DecodeLDRH(insn); - } - } - - u32 op1 = BITS(insn, 20, 24); - if (bit25 && (op1 == 0x12 || op1 == 0x16)) { - // One of the MSR (immediate) and hints instructions - return DecodeMSRImmAndHints(insn); - } - - // One of the data processing instructions - return DecodeALU(insn); -} - -Opcode ARM_Disasm::Decode01(u32 insn) { - u8 is_reg = (insn >> 25) & 0x1; - u8 bit4 = (insn >> 4) & 0x1; - if (is_reg == 1 && bit4 == 1) - return DecodeMedia(insn); - u8 is_load = (insn >> 20) & 0x1; - u8 is_byte = (insn >> 22) & 0x1; - if ((insn & 0xfd70f000) == 0xf550f000) { - // Pre-load - return OP_PLD; - } - if (insn == 0xf57ff01f) { - // Clear-Exclusive - return OP_CLREX; - } - if (is_load) { - if (is_byte) { - // Load byte - return OP_LDRB; - } - // Load word - return OP_LDR; - } - if (is_byte) { - // Store byte - return OP_STRB; - } - // Store word - return OP_STR; -} - -Opcode ARM_Disasm::Decode10(u32 insn) { - u8 bit25 = (insn >> 25) & 0x1; - if (bit25 == 0) { - // LDM/STM - u8 is_load = (insn >> 20) & 0x1; - if (is_load) - return OP_LDM; - return OP_STM; - } - - // Branch with link - if ((insn >> 24) & 1) - return OP_BL; - - return OP_B; -} - -Opcode ARM_Disasm::Decode11(u32 insn) { - u8 bit25 = (insn >> 25) & 0x1; - if (bit25 == 0) { - // LDC, SDC - u8 is_load = (insn >> 20) & 0x1; - if (is_load) { - // LDC - return OP_LDC; - } - // STC - return OP_STC; - } - - u8 bit24 = (insn >> 24) & 0x1; - if (bit24 == 0x1) { - // SWI - return OP_SWI; - } - - u8 bit4 = (insn >> 4) & 0x1; - u8 cpnum = (insn >> 8) & 0xf; - - if (cpnum == 15) { - // Special case for coprocessor 15 - u8 opcode = (insn >> 21) & 0x7; - if (bit4 == 0 || opcode != 0) { - // This is an unexpected bit pattern. Create an undefined - // instruction in case this is ever executed. - return OP_UNDEFINED; - } - - // MRC, MCR - u8 is_mrc = (insn >> 20) & 0x1; - if (is_mrc) - return OP_MRC; - return OP_MCR; - } - - if (bit4 == 0) { - // CDP - return OP_CDP; - } - // MRC, MCR - u8 is_mrc = (insn >> 20) & 0x1; - if (is_mrc) - return OP_MRC; - return OP_MCR; -} - -Opcode ARM_Disasm::DecodeSyncPrimitive(u32 insn) { - u32 op = BITS(insn, 20, 23); - u32 bit22 = BIT(insn, 22); - switch (op) { - case 0x0: - if (bit22) - return OP_SWPB; - return OP_SWP; - case 0x8: - return OP_STREX; - case 0x9: - return OP_LDREX; - case 0xA: - return OP_STREXD; - case 0xB: - return OP_LDREXD; - case 0xC: - return OP_STREXB; - case 0xD: - return OP_LDREXB; - case 0xE: - return OP_STREXH; - case 0xF: - return OP_LDREXH; - default: - return OP_UNDEFINED; - } -} - -Opcode ARM_Disasm::DecodeParallelAddSub(u32 insn) { - u32 op1 = BITS(insn, 20, 21); - u32 op2 = BITS(insn, 5, 7); - u32 is_unsigned = BIT(insn, 22); - - if (op1 == 0x0 || op2 == 0x5 || op2 == 0x6) - return OP_UNDEFINED; - - // change op1 range from [1, 3] to range [0, 2] - op1--; - - // change op2 range from [0, 4] U {7} to range [0, 5] - if (op2 == 0x7) - op2 = 0x5; - - static std::vector<Opcode> opcodes = { - // op1 = 0 - OP_SADD16, OP_UADD16, OP_SASX, OP_UASX, OP_SSAX, OP_USAX, OP_SSUB16, OP_USUB16, OP_SADD8, - OP_UADD8, OP_SSUB8, OP_USUB8, - // op1 = 1 - OP_QADD16, OP_UQADD16, OP_QASX, OP_UQASX, OP_QSAX, OP_UQSAX, OP_QSUB16, OP_UQSUB16, - OP_QADD8, OP_UQADD8, OP_QSUB8, OP_UQSUB8, - // op1 = 2 - OP_SHADD16, OP_UHADD16, OP_SHASX, OP_UHASX, OP_SHSAX, OP_UHSAX, OP_SHSUB16, OP_UHSUB16, - OP_SHADD8, OP_UHADD8, OP_SHSUB8, OP_UHSUB8}; - - u32 opcode_index = op1 * 12 + op2 * 2 + is_unsigned; - return opcodes[opcode_index]; -} - -Opcode ARM_Disasm::DecodePackingSaturationReversal(u32 insn) { - u32 op1 = BITS(insn, 20, 22); - u32 a = BITS(insn, 16, 19); - u32 op2 = BITS(insn, 5, 7); - - switch (op1) { - case 0x0: - if (BIT(op2, 0) == 0) - return OP_PKH; - if (op2 == 0x3 && a != 0xf) - return OP_SXTAB16; - if (op2 == 0x3 && a == 0xf) - return OP_SXTB16; - if (op2 == 0x5) - return OP_SEL; - break; - case 0x2: - if (BIT(op2, 0) == 0) - return OP_SSAT; - if (op2 == 0x1) - return OP_SSAT16; - if (op2 == 0x3 && a != 0xf) - return OP_SXTAB; - if (op2 == 0x3 && a == 0xf) - return OP_SXTB; - break; - case 0x3: - if (op2 == 0x1) - return OP_REV; - if (BIT(op2, 0) == 0) - return OP_SSAT; - if (op2 == 0x3 && a != 0xf) - return OP_SXTAH; - if (op2 == 0x3 && a == 0xf) - return OP_SXTH; - if (op2 == 0x5) - return OP_REV16; - break; - case 0x4: - if (op2 == 0x3 && a != 0xf) - return OP_UXTAB16; - if (op2 == 0x3 && a == 0xf) - return OP_UXTB16; - break; - case 0x6: - if (BIT(op2, 0) == 0) - return OP_USAT; - if (op2 == 0x1) - return OP_USAT16; - if (op2 == 0x3 && a != 0xf) - return OP_UXTAB; - if (op2 == 0x3 && a == 0xf) - return OP_UXTB; - break; - case 0x7: - if (BIT(op2, 0) == 0) - return OP_USAT; - if (op2 == 0x3 && a != 0xf) - return OP_UXTAH; - if (op2 == 0x3 && a == 0xf) - return OP_UXTH; - if (op2 == 0x5) - return OP_REVSH; - break; - default: - break; - } - - return OP_UNDEFINED; -} - -Opcode ARM_Disasm::DecodeMUL(u32 insn) { - u8 bit24 = (insn >> 24) & 0x1; - if (bit24 != 0) { - // This is an unexpected bit pattern. Create an undefined - // instruction in case this is ever executed. - return OP_UNDEFINED; - } - u8 bit23 = (insn >> 23) & 0x1; - u8 bit22_U = (insn >> 22) & 0x1; - u8 bit21_A = (insn >> 21) & 0x1; - if (bit23 == 0) { - // 32-bit multiply - if (bit22_U != 0) { - // This is an unexpected bit pattern. Create an undefined - // instruction in case this is ever executed. - return OP_UNDEFINED; - } - if (bit21_A == 0) - return OP_MUL; - return OP_MLA; - } - // 64-bit multiply - if (bit22_U == 0) { - // Unsigned multiply long - if (bit21_A == 0) - return OP_UMULL; - return OP_UMLAL; - } - // Signed multiply long - if (bit21_A == 0) - return OP_SMULL; - return OP_SMLAL; -} - -Opcode ARM_Disasm::DecodeMSRImmAndHints(u32 insn) { - u32 op = BIT(insn, 22); - u32 op1 = BITS(insn, 16, 19); - u32 op2 = BITS(insn, 0, 7); - - if (op == 0 && op1 == 0) { - switch (op2) { - case 0x0: - return OP_NOP; - case 0x1: - return OP_YIELD; - case 0x2: - return OP_WFE; - case 0x3: - return OP_WFI; - case 0x4: - return OP_SEV; - default: - return OP_UNDEFINED; - } - } - - return OP_MSR; -} - -Opcode ARM_Disasm::DecodeMediaMulDiv(u32 insn) { - u32 op1 = BITS(insn, 20, 22); - u32 op2_h = BITS(insn, 6, 7); - u32 a = BITS(insn, 12, 15); - - switch (op1) { - case 0x0: - if (op2_h == 0x0) { - if (a != 0xf) - return OP_SMLAD; - else - return OP_SMUAD; - } else if (op2_h == 0x1) { - if (a != 0xf) - return OP_SMLSD; - else - return OP_SMUSD; - } - break; - case 0x4: - if (op2_h == 0x0) - return OP_SMLALD; - else if (op2_h == 0x1) - return OP_SMLSLD; - break; - case 0x5: - if (op2_h == 0x0) { - if (a != 0xf) - return OP_SMMLA; - else - return OP_SMMUL; - } else if (op2_h == 0x3) { - return OP_SMMLS; - } - break; - default: - break; - } - - return OP_UNDEFINED; -} - -Opcode ARM_Disasm::DecodeMedia(u32 insn) { - u32 op1 = BITS(insn, 20, 24); - u32 rd = BITS(insn, 12, 15); - u32 op2 = BITS(insn, 5, 7); - - switch (BITS(op1, 3, 4)) { - case 0x0: - // unsigned and signed parallel addition and subtraction - return DecodeParallelAddSub(insn); - case 0x1: - // Packing, unpacking, saturation, and reversal - return DecodePackingSaturationReversal(insn); - case 0x2: - // Signed multiply, signed and unsigned divide - return DecodeMediaMulDiv(insn); - case 0x3: - if (op2 == 0 && rd == 0xf) - return OP_USAD8; - if (op2 == 0 && rd != 0xf) - return OP_USADA8; - break; - default: - break; - } - - return OP_UNDEFINED; -} - -Opcode ARM_Disasm::DecodeLDRH(u32 insn) { - u8 is_load = (insn >> 20) & 0x1; - u8 bits_65 = (insn >> 5) & 0x3; - if (is_load) { - if (bits_65 == 0x1) { - // Load unsigned halfword - return OP_LDRH; - } else if (bits_65 == 0x2) { - // Load signed byte - return OP_LDRSB; - } - // Signed halfword - if (bits_65 != 0x3) { - // This is an unexpected bit pattern. Create an undefined - // instruction in case this is ever executed. - return OP_UNDEFINED; - } - // Load signed halfword - return OP_LDRSH; - } - // Store halfword - if (bits_65 != 0x1) { - // This is an unexpected bit pattern. Create an undefined - // instruction in case this is ever executed. - return OP_UNDEFINED; - } - // Store halfword - return OP_STRH; -} - -Opcode ARM_Disasm::DecodeALU(u32 insn) { - u8 is_immed = (insn >> 25) & 0x1; - u8 opcode = (insn >> 21) & 0xf; - u8 bit_s = (insn >> 20) & 1; - u8 shift_is_reg = (insn >> 4) & 1; - u8 bit7 = (insn >> 7) & 1; - if (!is_immed && shift_is_reg && (bit7 != 0)) { - // This is an unexpected bit pattern. Create an undefined - // instruction in case this is ever executed. - return OP_UNDEFINED; - } - switch (opcode) { - case 0x0: - return OP_AND; - case 0x1: - return OP_EOR; - case 0x2: - return OP_SUB; - case 0x3: - return OP_RSB; - case 0x4: - return OP_ADD; - case 0x5: - return OP_ADC; - case 0x6: - return OP_SBC; - case 0x7: - return OP_RSC; - case 0x8: - if (bit_s) - return OP_TST; - return OP_MRS; - case 0x9: - if (bit_s) - return OP_TEQ; - return OP_MSR; - case 0xa: - if (bit_s) - return OP_CMP; - return OP_MRS; - case 0xb: - if (bit_s) - return OP_CMN; - return OP_MSR; - case 0xc: - return OP_ORR; - case 0xd: - return OP_MOV; - case 0xe: - return OP_BIC; - case 0xf: - return OP_MVN; - } - // Unreachable - return OP_INVALID; -} diff --git a/src/core/arm/disassembler/arm_disasm.h b/src/core/arm/disassembler/arm_disasm.h deleted file mode 100644 index 300e228ed..000000000 --- a/src/core/arm/disassembler/arm_disasm.h +++ /dev/null @@ -1,238 +0,0 @@ -// Copyright 2006 The Android Open Source Project - -#pragma once - -#include <string> -#include "common/common_types.h" - -// Note: this list of opcodes must match the list used to initialize -// the opflags[] array in opcode.cpp. -enum Opcode { - OP_INVALID, - OP_UNDEFINED, - OP_ADC, - OP_ADD, - OP_AND, - OP_B, - OP_BL, - OP_BIC, - OP_BKPT, - OP_BLX, - OP_BX, - OP_CDP, - OP_CLREX, - OP_CLZ, - OP_CMN, - OP_CMP, - OP_EOR, - OP_LDC, - OP_LDM, - OP_LDR, - OP_LDRB, - OP_LDRBT, - OP_LDREX, - OP_LDREXB, - OP_LDREXD, - OP_LDREXH, - OP_LDRH, - OP_LDRSB, - OP_LDRSH, - OP_LDRT, - OP_MCR, - OP_MLA, - OP_MOV, - OP_MRC, - OP_MRS, - OP_MSR, - OP_MUL, - OP_MVN, - OP_NOP, - OP_ORR, - OP_PKH, - OP_PLD, - OP_QADD16, - OP_QADD8, - OP_QASX, - OP_QSAX, - OP_QSUB16, - OP_QSUB8, - OP_REV, - OP_REV16, - OP_REVSH, - OP_RSB, - OP_RSC, - OP_SADD16, - OP_SADD8, - OP_SASX, - OP_SBC, - OP_SEL, - OP_SEV, - OP_SHADD16, - OP_SHADD8, - OP_SHASX, - OP_SHSAX, - OP_SHSUB16, - OP_SHSUB8, - OP_SMLAD, - OP_SMLAL, - OP_SMLALD, - OP_SMLSD, - OP_SMLSLD, - OP_SMMLA, - OP_SMMLS, - OP_SMMUL, - OP_SMUAD, - OP_SMULL, - OP_SMUSD, - OP_SSAT, - OP_SSAT16, - OP_SSAX, - OP_SSUB16, - OP_SSUB8, - OP_STC, - OP_STM, - OP_STR, - OP_STRB, - OP_STRBT, - OP_STREX, - OP_STREXB, - OP_STREXD, - OP_STREXH, - OP_STRH, - OP_STRT, - OP_SUB, - OP_SWI, - OP_SWP, - OP_SWPB, - OP_SXTAB, - OP_SXTAB16, - OP_SXTAH, - OP_SXTB, - OP_SXTB16, - OP_SXTH, - OP_TEQ, - OP_TST, - OP_UADD16, - OP_UADD8, - OP_UASX, - OP_UHADD16, - OP_UHADD8, - OP_UHASX, - OP_UHSAX, - OP_UHSUB16, - OP_UHSUB8, - OP_UMLAL, - OP_UMULL, - OP_UQADD16, - OP_UQADD8, - OP_UQASX, - OP_UQSAX, - OP_UQSUB16, - OP_UQSUB8, - OP_USAD8, - OP_USADA8, - OP_USAT, - OP_USAT16, - OP_USAX, - OP_USUB16, - OP_USUB8, - OP_UXTAB, - OP_UXTAB16, - OP_UXTAH, - OP_UXTB, - OP_UXTB16, - OP_UXTH, - OP_WFE, - OP_WFI, - OP_YIELD, - - // Define thumb opcodes - OP_THUMB_UNDEFINED, - OP_THUMB_ADC, - OP_THUMB_ADD, - OP_THUMB_AND, - OP_THUMB_ASR, - OP_THUMB_B, - OP_THUMB_BIC, - OP_THUMB_BKPT, - OP_THUMB_BL, - OP_THUMB_BLX, - OP_THUMB_BX, - OP_THUMB_CMN, - OP_THUMB_CMP, - OP_THUMB_EOR, - OP_THUMB_LDMIA, - OP_THUMB_LDR, - OP_THUMB_LDRB, - OP_THUMB_LDRH, - OP_THUMB_LDRSB, - OP_THUMB_LDRSH, - OP_THUMB_LSL, - OP_THUMB_LSR, - OP_THUMB_MOV, - OP_THUMB_MUL, - OP_THUMB_MVN, - OP_THUMB_NEG, - OP_THUMB_ORR, - OP_THUMB_POP, - OP_THUMB_PUSH, - OP_THUMB_ROR, - OP_THUMB_SBC, - OP_THUMB_STMIA, - OP_THUMB_STR, - OP_THUMB_STRB, - OP_THUMB_STRH, - OP_THUMB_SUB, - OP_THUMB_SWI, - OP_THUMB_TST, - - OP_END // must be last -}; - -class ARM_Disasm { -public: - static std::string Disassemble(u32 addr, u32 insn); - static Opcode Decode(u32 insn); - -private: - static Opcode Decode00(u32 insn); - static Opcode Decode01(u32 insn); - static Opcode Decode10(u32 insn); - static Opcode Decode11(u32 insn); - static Opcode DecodeSyncPrimitive(u32 insn); - static Opcode DecodeParallelAddSub(u32 insn); - static Opcode DecodePackingSaturationReversal(u32 insn); - static Opcode DecodeMUL(u32 insn); - static Opcode DecodeMSRImmAndHints(u32 insn); - static Opcode DecodeMediaMulDiv(u32 insn); - static Opcode DecodeMedia(u32 insn); - static Opcode DecodeLDRH(u32 insn); - static Opcode DecodeALU(u32 insn); - - static std::string DisassembleALU(Opcode opcode, u32 insn); - static std::string DisassembleBranch(u32 addr, Opcode opcode, u32 insn); - static std::string DisassembleBX(u32 insn); - static std::string DisassembleBKPT(u32 insn); - static std::string DisassembleCLZ(u32 insn); - static std::string DisassembleMediaMulDiv(Opcode opcode, u32 insn); - static std::string DisassembleMemblock(Opcode opcode, u32 insn); - static std::string DisassembleMem(u32 insn); - static std::string DisassembleMemHalf(u32 insn); - static std::string DisassembleMCR(Opcode opcode, u32 insn); - static std::string DisassembleMLA(Opcode opcode, u32 insn); - static std::string DisassembleUMLAL(Opcode opcode, u32 insn); - static std::string DisassembleMUL(Opcode opcode, u32 insn); - static std::string DisassembleMRS(u32 insn); - static std::string DisassembleMSR(u32 insn); - static std::string DisassembleNoOperands(Opcode opcode, u32 insn); - static std::string DisassembleParallelAddSub(Opcode opcode, u32 insn); - static std::string DisassemblePKH(u32 insn); - static std::string DisassemblePLD(u32 insn); - static std::string DisassembleREV(Opcode opcode, u32 insn); - static std::string DisassembleREX(Opcode opcode, u32 insn); - static std::string DisassembleSAT(Opcode opcode, u32 insn); - static std::string DisassembleSEL(u32 insn); - static std::string DisassembleSWI(u32 insn); - static std::string DisassembleSWP(Opcode opcode, u32 insn); - static std::string DisassembleXT(Opcode opcode, u32 insn); -}; diff --git a/src/core/arm/disassembler/load_symbol_map.cpp b/src/core/arm/disassembler/load_symbol_map.cpp deleted file mode 100644 index 6863c103a..000000000 --- a/src/core/arm/disassembler/load_symbol_map.cpp +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#include <sstream> -#include <string> -#include <vector> -#include "common/file_util.h" -#include "common/symbols.h" -#include "core/arm/disassembler/load_symbol_map.h" - -/* - * Loads a symbol map file for use with the disassembler - * @param filename String filename path of symbol map file - */ -void LoadSymbolMap(std::string filename) { - std::ifstream infile(filename); - - std::string address_str, function_name, line; - u32 size; - - while (std::getline(infile, line)) { - std::istringstream iss(line); - if (!(iss >> address_str >> size >> function_name)) { - break; // Error parsing - } - u32 address = std::stoul(address_str, nullptr, 16); - - Symbols::Add(address, function_name, size, 2); - } -} diff --git a/src/core/arm/disassembler/load_symbol_map.h b/src/core/arm/disassembler/load_symbol_map.h deleted file mode 100644 index d28c551c3..000000000 --- a/src/core/arm/disassembler/load_symbol_map.h +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2014 Citra Emulator Project -// Licensed under GPLv2 or any later version -// Refer to the license.txt file included. - -#pragma once - -#include <string> - -/* - * Loads a symbol map file for use with the disassembler - * @param filename String filename path of symbol map file - */ -void LoadSymbolMap(std::string filename); diff --git a/src/core/arm/dyncom/arm_dyncom_dec.cpp b/src/core/arm/dyncom/arm_dyncom_dec.cpp index 64dcaae08..dcfcd6561 100644 --- a/src/core/arm/dyncom/arm_dyncom_dec.cpp +++ b/src/core/arm/dyncom/arm_dyncom_dec.cpp @@ -415,7 +415,7 @@ const InstructionSetEncodingItem arm_exclusion_code[] = { }; // clang-format on -ARMDecodeStatus DecodeARMInstruction(u32 instr, s32* idx) { +ARMDecodeStatus DecodeARMInstruction(u32 instr, int* idx) { int n = 0; int base = 0; int instr_slots = sizeof(arm_instruction) / sizeof(InstructionSetEncodingItem); diff --git a/src/core/arm/dyncom/arm_dyncom_dec.h b/src/core/arm/dyncom/arm_dyncom_dec.h index 2fb7ac37c..1dcf7ecd1 100644 --- a/src/core/arm/dyncom/arm_dyncom_dec.h +++ b/src/core/arm/dyncom/arm_dyncom_dec.h @@ -8,7 +8,7 @@ enum class ARMDecodeStatus { SUCCESS, FAILURE }; -ARMDecodeStatus DecodeARMInstruction(u32 instr, s32* idx); +ARMDecodeStatus DecodeARMInstruction(u32 instr, int* idx); struct InstructionSetEncodingItem { const char* name; diff --git a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp index 273bc8167..f4fbb8d04 100644 --- a/src/core/arm/dyncom/arm_dyncom_interpreter.cpp +++ b/src/core/arm/dyncom/arm_dyncom_interpreter.cpp @@ -5,11 +5,11 @@ #define CITRA_IGNORE_EXIT(x) #include <algorithm> +#include <cinttypes> #include <cstdio> #include "common/common_types.h" #include "common/logging/log.h" #include "common/microprofile.h" -#include "core/arm/disassembler/arm_disasm.h" #include "core/arm/dyncom/arm_dyncom_dec.h" #include "core/arm/dyncom/arm_dyncom_interpreter.h" #include "core/arm/dyncom/arm_dyncom_run.h" @@ -808,8 +808,8 @@ MICROPROFILE_DEFINE(DynCom_Decode, "DynCom", "Decode", MP_RGB(255, 64, 64)); static unsigned int InterpreterTranslateInstruction(const ARMul_State* cpu, const u32 phys_addr, ARM_INST_PTR& inst_base) { - unsigned int inst_size = 4; - unsigned int inst = Memory::Read32(phys_addr & 0xFFFFFFFC); + u32 inst_size = 4; + u32 inst = Memory::Read32(phys_addr & 0xFFFFFFFC); // If we are in Thumb mode, we'll translate one Thumb instruction to the corresponding ARM // instruction @@ -827,11 +827,10 @@ static unsigned int InterpreterTranslateInstruction(const ARMul_State* cpu, cons int idx; if (DecodeARMInstruction(inst, &idx) == ARMDecodeStatus::FAILURE) { - std::string disasm = ARM_Disasm::Disassemble(phys_addr, inst); - LOG_ERROR(Core_ARM11, "Decode failure.\tPC : [0x%x]\tInstruction : %s [%x]", phys_addr, - disasm.c_str(), inst); - LOG_ERROR(Core_ARM11, "cpsr=0x%x, cpu->TFlag=%d, r15=0x%x", cpu->Cpsr, cpu->TFlag, - cpu->Reg[15]); + LOG_ERROR(Core_ARM11, "Decode failure.\tPC: [0x%08" PRIX32 "]\tInstruction: %08" PRIX32, + phys_addr, inst); + LOG_ERROR(Core_ARM11, "cpsr=0x%" PRIX32 ", cpu->TFlag=%d, r15=0x%08" PRIX32, cpu->Cpsr, + cpu->TFlag, cpu->Reg[15]); CITRA_IGNORE_EXIT(-1); } inst_base = arm_instruction_trans[idx](inst, idx); diff --git a/src/core/arm/skyeye_common/vfp/vfp_helper.h b/src/core/arm/skyeye_common/vfp/vfp_helper.h index 5e14345ce..1eba71b48 100644 --- a/src/core/arm/skyeye_common/vfp/vfp_helper.h +++ b/src/core/arm/skyeye_common/vfp/vfp_helper.h @@ -291,7 +291,7 @@ inline s32 vfp_single_pack(const vfp_single* s) { return (s32)val; } -u32 vfp_single_normaliseround(ARMul_State* state, int sd, vfp_single* vs, u32 fpscr, +u32 vfp_single_normaliseround(ARMul_State* state, int sd, vfp_single* vs, u32 fpscr, u32 exceptions, const char* func); // Double-precision @@ -429,5 +429,5 @@ inline u32 fls(u32 x) { u32 vfp_double_multiply(vfp_double* vdd, vfp_double* vdn, vfp_double* vdm, u32 fpscr); u32 vfp_double_add(vfp_double* vdd, vfp_double* vdn, vfp_double* vdm, u32 fpscr); -u32 vfp_double_normaliseround(ARMul_State* state, int dd, vfp_double* vd, u32 fpscr, +u32 vfp_double_normaliseround(ARMul_State* state, int dd, vfp_double* vd, u32 fpscr, u32 exceptions, const char* func); diff --git a/src/core/arm/skyeye_common/vfp/vfpdouble.cpp b/src/core/arm/skyeye_common/vfp/vfpdouble.cpp index 2886f351f..7b035f56a 100644 --- a/src/core/arm/skyeye_common/vfp/vfpdouble.cpp +++ b/src/core/arm/skyeye_common/vfp/vfpdouble.cpp @@ -82,11 +82,10 @@ static void vfp_double_normalise_denormal(struct vfp_double* vd) { } u32 vfp_double_normaliseround(ARMul_State* state, int dd, struct vfp_double* vd, u32 fpscr, - const char* func) { + u32 exceptions, const char* func) { u64 significand, incr; int exponent, shift, underflow; u32 rmode; - u32 exceptions = 0; vfp_double_dump("pack: in", vd); @@ -360,7 +359,8 @@ static u32 vfp_double_fsqrt(ARMul_State* state, int dd, int unused, int dm, u32 } vdd.significand = vfp_shiftright64jamming(vdd.significand, 1); - exceptions |= vfp_double_normaliseround(state, dd, &vdd, fpscr, "fsqrt"); + exceptions |= vfp_double_normaliseround(state, dd, &vdd, fpscr, 0, "fsqrt"); + return exceptions; } @@ -492,8 +492,7 @@ static u32 vfp_double_fcvts(ARMul_State* state, int sd, int unused, int dm, u32 else vsd.exponent = vdm.exponent - (1023 - 127); - exceptions |= vfp_single_normaliseround(state, sd, &vsd, fpscr, "fcvts"); - return exceptions; + return vfp_single_normaliseround(state, sd, &vsd, fpscr, exceptions, "fcvts"); pack_nan: vfp_put_float(state, vfp_single_pack(&vsd), sd); @@ -502,7 +501,6 @@ pack_nan: static u32 vfp_double_fuito(ARMul_State* state, int dd, int unused, int dm, u32 fpscr) { struct vfp_double vdm; - u32 exceptions = 0; u32 m = vfp_get_float(state, dm); LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__); @@ -510,13 +508,11 @@ static u32 vfp_double_fuito(ARMul_State* state, int dd, int unused, int dm, u32 vdm.exponent = 1023 + 63 - 1; vdm.significand = (u64)m; - exceptions |= vfp_double_normaliseround(state, dd, &vdm, fpscr, "fuito"); - return exceptions; + return vfp_double_normaliseround(state, dd, &vdm, fpscr, 0, "fuito"); } static u32 vfp_double_fsito(ARMul_State* state, int dd, int unused, int dm, u32 fpscr) { struct vfp_double vdm; - u32 exceptions = 0; u32 m = vfp_get_float(state, dm); LOG_TRACE(Core_ARM11, "In %s", __FUNCTION__); @@ -524,8 +520,7 @@ static u32 vfp_double_fsito(ARMul_State* state, int dd, int unused, int dm, u32 vdm.exponent = 1023 + 63 - 1; vdm.significand = vdm.sign ? (~m + 1) : m; - exceptions |= vfp_double_normaliseround(state, dd, &vdm, fpscr, "fsito"); - return exceptions; + return vfp_double_normaliseround(state, dd, &vdm, fpscr, 0, "fsito"); } static u32 vfp_double_ftoui(ARMul_State* state, int sd, int unused, int dm, u32 fpscr) { @@ -912,8 +907,7 @@ static u32 vfp_double_multiply_accumulate(ARMul_State* state, int dd, int dn, in exceptions |= vfp_double_add(&vdd, &vdn, &vdp, fpscr); - exceptions |= vfp_double_normaliseround(state, dd, &vdd, fpscr, func); - return exceptions; + return vfp_double_normaliseround(state, dd, &vdd, fpscr, exceptions, func); } /* @@ -970,9 +964,7 @@ static u32 vfp_double_fmul(ARMul_State* state, int dd, int dn, int dm, u32 fpscr vfp_double_normalise_denormal(&vdm); exceptions |= vfp_double_multiply(&vdd, &vdn, &vdm, fpscr); - - exceptions |= vfp_double_normaliseround(state, dd, &vdd, fpscr, "fmul"); - return exceptions; + return vfp_double_normaliseround(state, dd, &vdd, fpscr, exceptions, "fmul"); } /* @@ -994,8 +986,7 @@ static u32 vfp_double_fnmul(ARMul_State* state, int dd, int dn, int dm, u32 fpsc exceptions |= vfp_double_multiply(&vdd, &vdn, &vdm, fpscr); vdd.sign = vfp_sign_negate(vdd.sign); - exceptions |= vfp_double_normaliseround(state, dd, &vdd, fpscr, "fnmul"); - return exceptions; + return vfp_double_normaliseround(state, dd, &vdd, fpscr, exceptions, "fnmul"); } /* @@ -1016,8 +1007,7 @@ static u32 vfp_double_fadd(ARMul_State* state, int dd, int dn, int dm, u32 fpscr exceptions |= vfp_double_add(&vdd, &vdn, &vdm, fpscr); - exceptions |= vfp_double_normaliseround(state, dd, &vdd, fpscr, "fadd"); - return exceptions; + return vfp_double_normaliseround(state, dd, &vdd, fpscr, exceptions, "fadd"); } /* @@ -1043,8 +1033,7 @@ static u32 vfp_double_fsub(ARMul_State* state, int dd, int dn, int dm, u32 fpscr exceptions |= vfp_double_add(&vdd, &vdn, &vdm, fpscr); - exceptions |= vfp_double_normaliseround(state, dd, &vdd, fpscr, "fsub"); - return exceptions; + return vfp_double_normaliseround(state, dd, &vdd, fpscr, exceptions, "fsub"); } /* @@ -1126,9 +1115,7 @@ static u32 vfp_double_fdiv(ARMul_State* state, int dd, int dn, int dm, u32 fpscr } vdd.significand |= (reml != 0); } - - exceptions |= vfp_double_normaliseround(state, dd, &vdd, fpscr, "fdiv"); - return exceptions; + return vfp_double_normaliseround(state, dd, &vdd, fpscr, 0, "fdiv"); vdn_nan: exceptions |= vfp_propagate_nan(&vdd, &vdn, &vdm, fpscr); @@ -1154,8 +1141,7 @@ infinity: invalid: vfp_put_double(state, vfp_double_pack(&vfp_double_default_qnan), dd); - exceptions |= FPSCR_IOC; - return exceptions; + return FPSCR_IOC; } static struct op fops[] = { @@ -1230,7 +1216,7 @@ u32 vfp_double_cpdo(ARMul_State* state, u32 inst, u32 fpscr) { except = fop->fn(state, dest, dn, dm, fpscr); LOG_TRACE(Core_ARM11, "VFP: itr%d: exceptions=%08x", vecitr >> FPSCR_LENGTH_BIT, except); - exceptions |= except; + exceptions |= except & ~VFP_NAN_FLAG; /* * CHECK: It appears to be undefined whether we stop when diff --git a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp index 1590d89a4..6b4cb8efa 100644 --- a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp +++ b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp @@ -83,10 +83,9 @@ static void vfp_single_normalise_denormal(struct vfp_single* vs) { } u32 vfp_single_normaliseround(ARMul_State* state, int sd, struct vfp_single* vs, u32 fpscr, - const char* func) { + u32 exceptions, const char* func) { u32 significand, incr, rmode; int exponent, shift, underflow; - u32 exceptions = 0; vfp_single_dump("pack: in", vs); @@ -394,7 +393,8 @@ static u32 vfp_single_fsqrt(ARMul_State* state, int sd, int unused, s32 m, u32 f } vsd.significand = vfp_shiftright32jamming(vsd.significand, 1); - exceptions |= vfp_single_normaliseround(state, sd, &vsd, fpscr, "fsqrt"); + exceptions |= vfp_single_normaliseround(state, sd, &vsd, fpscr, 0, "fsqrt"); + return exceptions; } @@ -515,8 +515,7 @@ static u32 vfp_single_fcvtd(ARMul_State* state, int dd, int unused, s32 m, u32 f else vdd.exponent = vsm.exponent + (1023 - 127); - exceptions |= vfp_double_normaliseround(state, dd, &vdd, fpscr, "fcvtd"); - return exceptions; + return vfp_double_normaliseround(state, dd, &vdd, fpscr, exceptions, "fcvtd"); pack_nan: vfp_put_double(state, vfp_double_pack(&vdd), dd); @@ -525,26 +524,22 @@ pack_nan: static u32 vfp_single_fuito(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr) { struct vfp_single vs; - u32 exceptions = 0; vs.sign = 0; vs.exponent = 127 + 31 - 1; vs.significand = (u32)m; - exceptions |= vfp_single_normaliseround(state, sd, &vs, fpscr, "fuito"); - return exceptions; + return vfp_single_normaliseround(state, sd, &vs, fpscr, 0, "fuito"); } static u32 vfp_single_fsito(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr) { struct vfp_single vs; - u32 exceptions = 0; vs.sign = (m & 0x80000000) >> 16; vs.exponent = 127 + 31 - 1; vs.significand = vs.sign ? -m : m; - exceptions |= vfp_single_normaliseround(state, sd, &vs, fpscr, "fsito"); - return exceptions; + return vfp_single_normaliseround(state, sd, &vs, fpscr, 0, "fsito"); } static u32 vfp_single_ftoui(ARMul_State* state, int sd, int unused, s32 m, u32 fpscr) { @@ -936,8 +931,7 @@ static u32 vfp_single_multiply_accumulate(ARMul_State* state, int sd, int sn, s3 exceptions |= vfp_single_add(&vsd, &vsn, &vsp, fpscr); - exceptions |= vfp_single_normaliseround(state, sd, &vsd, fpscr, func); - return exceptions; + return vfp_single_normaliseround(state, sd, &vsd, fpscr, exceptions, func); } /* @@ -948,10 +942,8 @@ static u32 vfp_single_multiply_accumulate(ARMul_State* state, int sd, int sn, s3 * sd = sd + (sn * sm) */ static u32 vfp_single_fmac(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) { - u32 exceptions = 0; LOG_TRACE(Core_ARM11, "s%u = %08x", sn, sd); - exceptions |= vfp_single_multiply_accumulate(state, sd, sn, m, fpscr, 0, "fmac"); - return exceptions; + return vfp_single_multiply_accumulate(state, sd, sn, m, fpscr, 0, "fmac"); } /* @@ -999,9 +991,7 @@ static u32 vfp_single_fmul(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) vfp_single_normalise_denormal(&vsm); exceptions |= vfp_single_multiply(&vsd, &vsn, &vsm, fpscr); - - exceptions |= vfp_single_normaliseround(state, sd, &vsd, fpscr, "fmul"); - return exceptions; + return vfp_single_normaliseround(state, sd, &vsd, fpscr, exceptions, "fmul"); } /* @@ -1024,9 +1014,7 @@ static u32 vfp_single_fnmul(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr exceptions |= vfp_single_multiply(&vsd, &vsn, &vsm, fpscr); vsd.sign = vfp_sign_negate(vsd.sign); - - exceptions |= vfp_single_normaliseround(state, sd, &vsd, fpscr, "fnmul"); - return exceptions; + return vfp_single_normaliseround(state, sd, &vsd, fpscr, exceptions, "fnmul"); } /* @@ -1052,8 +1040,7 @@ static u32 vfp_single_fadd(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) exceptions |= vfp_single_add(&vsd, &vsn, &vsm, fpscr); - exceptions |= vfp_single_normaliseround(state, sd, &vsd, fpscr, "fadd"); - return exceptions; + return vfp_single_normaliseround(state, sd, &vsd, fpscr, exceptions, "fadd"); } /* @@ -1062,12 +1049,22 @@ static u32 vfp_single_fadd(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) static u32 vfp_single_fsub(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) { LOG_TRACE(Core_ARM11, "s%u = %08x", sn, sd); /* - * Subtraction is addition with one sign inverted. + * Subtraction is addition with one sign inverted. Unpack the second operand to perform FTZ if + * necessary, we can't let fadd do this because a denormal in m might get flushed to +0 in FTZ + * mode, and the resulting sign of 0 OP +0 differs between fadd and fsub. We do not need to do + * this for n because +0 OP 0 is always +0 for both fadd and fsub. */ + struct vfp_single vsm; + u32 exceptions = vfp_single_unpack(&vsm, m, fpscr); + if (exceptions & FPSCR_IDC) { + // The value was flushed to zero, re-pack it. + m = vfp_single_pack(&vsm); + } + if (m != 0x7FC00000) // Only negate if m isn't NaN. m = vfp_single_packed_negate(m); - return vfp_single_fadd(state, sd, sn, m, fpscr); + return vfp_single_fadd(state, sd, sn, m, fpscr) | exceptions; } /* @@ -1148,8 +1145,7 @@ static u32 vfp_single_fdiv(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) if ((vsd.significand & 0x3f) == 0) vsd.significand |= ((u64)vsm.significand * vsd.significand != (u64)vsn.significand << 32); - exceptions |= vfp_single_normaliseround(state, sd, &vsd, fpscr, "fdiv"); - return exceptions; + return vfp_single_normaliseround(state, sd, &vsd, fpscr, 0, "fdiv"); vsn_nan: exceptions |= vfp_propagate_nan(&vsd, &vsn, &vsm, fpscr); @@ -1175,8 +1171,7 @@ infinity: invalid: vfp_put_float(state, vfp_single_pack(&vfp_single_default_qnan), sd); - exceptions |= FPSCR_IOC; - return exceptions; + return FPSCR_IOC; } static struct op fops[] = { @@ -1246,7 +1241,7 @@ u32 vfp_single_cpdo(ARMul_State* state, u32 inst, u32 fpscr) { except = fop->fn(state, dest, sn, m, fpscr); LOG_TRACE(Core_ARM11, "itr%d: exceptions=%08x", vecitr >> FPSCR_LENGTH_BIT, except); - exceptions |= except; + exceptions |= except & ~VFP_NAN_FLAG; /* * CHECK: It appears to be undefined whether we stop when diff --git a/src/core/core.cpp b/src/core/core.cpp index 140ff6451..881f1e93c 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp @@ -13,11 +13,11 @@ #include "core/core_timing.h" #include "core/gdbstub/gdbstub.h" #include "core/hle/kernel/kernel.h" -#include "core/hle/kernel/memory.h" #include "core/hle/kernel/thread.h" #include "core/hle/service/service.h" #include "core/hw/hw.h" #include "core/loader/loader.h" +#include "core/memory_setup.h" #include "core/settings.h" #include "video_core/video_core.h" @@ -123,7 +123,8 @@ void System::Reschedule() { } System::ResultStatus System::Init(EmuWindow* emu_window, u32 system_mode) { - Memory::Init(); + Memory::InitMemoryMap(); + LOG_DEBUG(HW_Memory, "initialized OK"); if (Settings::values.use_cpu_jit) { cpu_core = std::make_unique<ARM_Dynarmic>(USER32MODE); diff --git a/src/core/hle/kernel/memory.cpp b/src/core/hle/kernel/memory.cpp index 33c165197..8250a90b5 100644 --- a/src/core/hle/kernel/memory.cpp +++ b/src/core/hle/kernel/memory.cpp @@ -2,11 +2,13 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cinttypes> #include <map> #include <memory> #include <utility> #include <vector> #include "audio_core/audio_core.h" +#include "common/assert.h" #include "common/common_types.h" #include "common/logging/log.h" #include "core/hle/config_mem.h" @@ -92,52 +94,96 @@ MemoryRegionInfo* GetMemoryRegion(MemoryRegion region) { UNREACHABLE(); } } -} - -namespace Memory { -namespace { +std::array<u8, Memory::VRAM_SIZE> vram; +std::array<u8, Memory::N3DS_EXTRA_RAM_SIZE> n3ds_extra_ram; + +void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping) { + using namespace Memory; + + struct MemoryArea { + VAddr vaddr_base; + PAddr paddr_base; + u32 size; + }; + + // The order of entries in this array is important. The VRAM and IO VAddr ranges overlap, and + // VRAM must be tried first. + static constexpr MemoryArea memory_areas[] = { + {VRAM_VADDR, VRAM_PADDR, VRAM_SIZE}, + {IO_AREA_VADDR, IO_AREA_PADDR, IO_AREA_SIZE}, + {DSP_RAM_VADDR, DSP_RAM_PADDR, DSP_RAM_SIZE}, + {N3DS_EXTRA_RAM_VADDR, N3DS_EXTRA_RAM_PADDR, N3DS_EXTRA_RAM_SIZE - 0x20000}, + }; + + VAddr mapping_limit = mapping.address + mapping.size; + if (mapping_limit < mapping.address) { + LOG_CRITICAL(Loader, "Mapping size overflowed: address=0x%08" PRIX32 " size=0x%" PRIX32, + mapping.address, mapping.size); + return; + } -struct MemoryArea { - u32 base; - u32 size; - const char* name; -}; + auto area = + std::find_if(std::begin(memory_areas), std::end(memory_areas), [&](const auto& area) { + return mapping.address >= area.vaddr_base && + mapping_limit <= area.vaddr_base + area.size; + }); + if (area == std::end(memory_areas)) { + LOG_ERROR(Loader, "Unhandled special mapping: address=0x%08" PRIX32 " size=0x%" PRIX32 + " read_only=%d unk_flag=%d", + mapping.address, mapping.size, mapping.read_only, mapping.unk_flag); + return; + } -// We don't declare the IO regions in here since its handled by other means. -static MemoryArea memory_areas[] = { - {VRAM_VADDR, VRAM_SIZE, "VRAM"}, // Video memory (VRAM) -}; -} + u32 offset_into_region = mapping.address - area->vaddr_base; + if (area->paddr_base == IO_AREA_PADDR) { + LOG_ERROR(Loader, "MMIO mappings are not supported yet. phys_addr=0x%08" PRIX32, + area->paddr_base + offset_into_region); + return; + } -void Init() { - InitMemoryMap(); - LOG_DEBUG(HW_Memory, "initialized OK"); -} + // TODO(yuriks): Use GetPhysicalPointer when that becomes independent of the virtual + // mappings. + u8* target_pointer = nullptr; + switch (area->paddr_base) { + case VRAM_PADDR: + target_pointer = vram.data(); + break; + case DSP_RAM_PADDR: + target_pointer = AudioCore::GetDspMemory().data(); + break; + case N3DS_EXTRA_RAM_PADDR: + target_pointer = n3ds_extra_ram.data(); + break; + default: + UNREACHABLE(); + } -void InitLegacyAddressSpace(Kernel::VMManager& address_space) { - using namespace Kernel; + // TODO(yuriks): This flag seems to have some other effect, but it's unknown what + MemoryState memory_state = mapping.unk_flag ? MemoryState::Static : MemoryState::IO; - for (MemoryArea& area : memory_areas) { - auto block = std::make_shared<std::vector<u8>>(area.size); - address_space - .MapMemoryBlock(area.base, std::move(block), 0, area.size, MemoryState::Private) - .Unwrap(); - } + auto vma = address_space + .MapBackingMemory(mapping.address, target_pointer + offset_into_region, + mapping.size, memory_state) + .MoveFrom(); + address_space.Reprotect(vma, + mapping.read_only ? VMAPermission::Read : VMAPermission::ReadWrite); +} +void MapSharedPages(VMManager& address_space) { auto cfg_mem_vma = address_space - .MapBackingMemory(CONFIG_MEMORY_VADDR, (u8*)&ConfigMem::config_mem, - CONFIG_MEMORY_SIZE, MemoryState::Shared) + .MapBackingMemory(Memory::CONFIG_MEMORY_VADDR, + reinterpret_cast<u8*>(&ConfigMem::config_mem), + Memory::CONFIG_MEMORY_SIZE, MemoryState::Shared) .MoveFrom(); address_space.Reprotect(cfg_mem_vma, VMAPermission::Read); auto shared_page_vma = address_space - .MapBackingMemory(SHARED_PAGE_VADDR, (u8*)&SharedPage::shared_page, - SHARED_PAGE_SIZE, MemoryState::Shared) + .MapBackingMemory(Memory::SHARED_PAGE_VADDR, + reinterpret_cast<u8*>(&SharedPage::shared_page), + Memory::SHARED_PAGE_SIZE, MemoryState::Shared) .MoveFrom(); address_space.Reprotect(shared_page_vma, VMAPermission::Read); - - AudioCore::AddAddressSpace(address_space); } -} // namespace +} // namespace Kernel diff --git a/src/core/hle/kernel/memory.h b/src/core/hle/kernel/memory.h index 4e1856a41..08c1a9989 100644 --- a/src/core/hle/kernel/memory.h +++ b/src/core/hle/kernel/memory.h @@ -23,11 +23,7 @@ struct MemoryRegionInfo { void MemoryInit(u32 mem_type); void MemoryShutdown(); MemoryRegionInfo* GetMemoryRegion(MemoryRegion region); -} -namespace Memory { - -void Init(); -void InitLegacyAddressSpace(Kernel::VMManager& address_space); - -} // namespace +void HandleSpecialMapping(VMManager& address_space, const AddressMapping& mapping); +void MapSharedPages(VMManager& address_space); +} // namespace Kernel diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index ba80fe7f8..32cb25fb7 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -35,7 +35,6 @@ SharedPtr<Process> Process::Create(SharedPtr<CodeSet> code_set) { process->codeset = std::move(code_set); process->flags.raw = 0; process->flags.memory_region.Assign(MemoryRegion::APPLICATION); - Memory::InitLegacyAddressSpace(process->vm_manager); return process; } @@ -78,8 +77,15 @@ void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) { AddressMapping mapping; mapping.address = descriptor << 12; - mapping.size = (end_desc << 12) - mapping.address; - mapping.writable = (descriptor & (1 << 20)) != 0; + VAddr end_address = end_desc << 12; + + if (mapping.address < end_address) { + mapping.size = end_address - mapping.address; + } else { + mapping.size = 0; + } + + mapping.read_only = (descriptor & (1 << 20)) != 0; mapping.unk_flag = (end_desc & (1 << 20)) != 0; address_mappings.push_back(mapping); @@ -88,8 +94,10 @@ void Process::ParseKernelCaps(const u32* kernel_caps, size_t len) { AddressMapping mapping; mapping.address = descriptor << 12; mapping.size = Memory::PAGE_SIZE; - mapping.writable = true; // TODO: Not sure if correct + mapping.read_only = false; mapping.unk_flag = false; + + address_mappings.push_back(mapping); } else if ((type & 0xFE0) == 0xFC0) { // 0x01FF // Kernel version kernel_version = descriptor & 0xFFFF; @@ -131,6 +139,12 @@ void Process::Run(s32 main_thread_priority, u32 stack_size) { misc_memory_used += stack_size; memory_region->used += stack_size; + // Map special address mappings + MapSharedPages(vm_manager); + for (const auto& mapping : address_mappings) { + HandleSpecialMapping(vm_manager, mapping); + } + vm_manager.LogLayout(Log::Level::Debug); Kernel::SetupMainThread(codeset->entrypoint, main_thread_priority); } @@ -138,6 +152,7 @@ void Process::Run(s32 main_thread_priority, u32 stack_size) { VAddr Process::GetLinearHeapAreaAddress() const { return kernel_version < 0x22C ? Memory::LINEAR_HEAP_VADDR : Memory::NEW_LINEAR_HEAP_VADDR; } + VAddr Process::GetLinearHeapBase() const { return GetLinearHeapAreaAddress() + memory_region->base; } diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index b566950b0..b52211d2a 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -20,7 +20,7 @@ struct AddressMapping { // Address and size must be page-aligned VAddr address; u32 size; - bool writable; + bool read_only; bool unk_flag; }; diff --git a/src/core/hle/service/cfg/cfg.cpp b/src/core/hle/service/cfg/cfg.cpp index 4ddb1bc90..8c8c1ec77 100644 --- a/src/core/hle/service/cfg/cfg.cpp +++ b/src/core/hle/service/cfg/cfg.cpp @@ -4,6 +4,7 @@ #include <algorithm> #include <array> +#include <cryptopp/osrng.h> #include <cryptopp/sha.h> #include "common/file_util.h" #include "common/logging/log.h" @@ -50,6 +51,7 @@ enum ConfigBlockID { SoundOutputModeBlockID = 0x00070001, ConsoleUniqueID1BlockID = 0x00090000, ConsoleUniqueID2BlockID = 0x00090001, + ConsoleUniqueID3BlockID = 0x00090002, UsernameBlockID = 0x000A0000, BirthdayBlockID = 0x000A0001, LanguageBlockID = 0x000A0002, @@ -86,7 +88,6 @@ struct ConsoleCountryInfo { static_assert(sizeof(ConsoleCountryInfo) == 4, "ConsoleCountryInfo must be exactly 4 bytes"); } -static const u64 CONSOLE_UNIQUE_ID = 0xDEADC0DE; static const ConsoleModelInfo CONSOLE_MODEL = {NINTENDO_3DS_XL, {0, 0, 0}}; static const u8 CONSOLE_LANGUAGE = LANGUAGE_EN; static const UsernameBlock CONSOLE_USERNAME_BLOCK = {u"CITRA", 0, 0}; @@ -438,13 +439,22 @@ ResultCode FormatConfig() { if (!res.IsSuccess()) return res; - res = CreateConfigInfoBlk(ConsoleUniqueID1BlockID, sizeof(CONSOLE_UNIQUE_ID), 0xE, - &CONSOLE_UNIQUE_ID); + u32 random_number; + u64 console_id; + GenerateConsoleUniqueId(random_number, console_id); + + u64_le console_id_le = console_id; + res = CreateConfigInfoBlk(ConsoleUniqueID1BlockID, sizeof(console_id_le), 0xE, &console_id_le); if (!res.IsSuccess()) return res; - res = CreateConfigInfoBlk(ConsoleUniqueID2BlockID, sizeof(CONSOLE_UNIQUE_ID), 0xE, - &CONSOLE_UNIQUE_ID); + res = CreateConfigInfoBlk(ConsoleUniqueID2BlockID, sizeof(console_id_le), 0xE, &console_id_le); + if (!res.IsSuccess()) + return res; + + u32_le random_number_le = random_number; + res = CreateConfigInfoBlk(ConsoleUniqueID3BlockID, sizeof(random_number_le), 0xE, + &random_number_le); if (!res.IsSuccess()) return res; @@ -663,5 +673,40 @@ SoundOutputMode GetSoundOutputMode() { return static_cast<SoundOutputMode>(block); } +void GenerateConsoleUniqueId(u32& random_number, u64& console_id) { + CryptoPP::AutoSeededRandomPool rng; + random_number = rng.GenerateWord32(0, 0xFFFF); + u64_le local_friend_code_seed; + rng.GenerateBlock(reinterpret_cast<byte*>(&local_friend_code_seed), + sizeof(local_friend_code_seed)); + console_id = (local_friend_code_seed & 0x3FFFFFFFF) | (static_cast<u64>(random_number) << 48); +} + +ResultCode SetConsoleUniqueId(u32 random_number, u64 console_id) { + u64_le console_id_le = console_id; + ResultCode res = + SetConfigInfoBlock(ConsoleUniqueID1BlockID, sizeof(console_id_le), 0xE, &console_id_le); + if (!res.IsSuccess()) + return res; + + res = SetConfigInfoBlock(ConsoleUniqueID2BlockID, sizeof(console_id_le), 0xE, &console_id_le); + if (!res.IsSuccess()) + return res; + + u32_le random_number_le = random_number; + res = SetConfigInfoBlock(ConsoleUniqueID3BlockID, sizeof(random_number_le), 0xE, + &random_number_le); + if (!res.IsSuccess()) + return res; + + return RESULT_SUCCESS; +} + +u64 GetConsoleUniqueId() { + u64_le console_id_le; + GetConfigInfoBlock(ConsoleUniqueID2BlockID, sizeof(console_id_le), 0xE, &console_id_le); + return console_id_le; +} + } // namespace CFG } // namespace Service diff --git a/src/core/hle/service/cfg/cfg.h b/src/core/hle/service/cfg/cfg.h index 618c9647e..1659ebf32 100644 --- a/src/core/hle/service/cfg/cfg.h +++ b/src/core/hle/service/cfg/cfg.h @@ -342,5 +342,26 @@ void SetSoundOutputMode(SoundOutputMode mode); */ SoundOutputMode GetSoundOutputMode(); +/** + * Generates a new random console unique id. + * @param random_number a random generated 16bit number stored at 0x90002, used for generating the + * console_id + * @param console_id the randomly created console id + */ +void GenerateConsoleUniqueId(u32& random_number, u64& console_id); + +/** + * Sets the random_number and the console unique id in the config savegame. + * @param random_number the random_number to set + * @param console_id the console id to set + */ +ResultCode SetConsoleUniqueId(u32 random_number, u64 console_id); + +/** + * Gets the console unique id from config savegame. + * @returns the console unique id + */ +u64 GetConsoleUniqueId(); + } // namespace CFG } // namespace Service diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index b19e831fe..64d01cdd7 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp @@ -53,30 +53,29 @@ static std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton:: buttons; static std::unique_ptr<Input::AnalogDevice> circle_pad; -static PadState GetCirclePadDirectionState(s16 circle_pad_x, s16 circle_pad_y) { +DirectionState GetStickDirectionState(s16 circle_pad_x, s16 circle_pad_y) { // 30 degree and 60 degree are angular thresholds for directions constexpr float TAN30 = 0.577350269f; constexpr float TAN60 = 1 / TAN30; // a circle pad radius greater than 40 will trigger circle pad direction constexpr int CIRCLE_PAD_THRESHOLD_SQUARE = 40 * 40; - PadState state; - state.hex = 0; + DirectionState state{false, false, false, false}; if (circle_pad_x * circle_pad_x + circle_pad_y * circle_pad_y > CIRCLE_PAD_THRESHOLD_SQUARE) { float t = std::abs(static_cast<float>(circle_pad_y) / circle_pad_x); if (circle_pad_x != 0 && t < TAN60) { if (circle_pad_x > 0) - state.circle_right.Assign(1); + state.right = true; else - state.circle_left.Assign(1); + state.left = true; } if (circle_pad_x == 0 || t > TAN30) { if (circle_pad_y > 0) - state.circle_up.Assign(1); + state.up = true; else - state.circle_down.Assign(1); + state.down = true; } } @@ -125,7 +124,11 @@ static void UpdatePadCallback(u64 userdata, int cycles_late) { constexpr int MAX_CIRCLEPAD_POS = 0x9C; // Max value for a circle pad position s16 circle_pad_x = static_cast<s16>(circle_pad_x_f * MAX_CIRCLEPAD_POS); s16 circle_pad_y = static_cast<s16>(circle_pad_y_f * MAX_CIRCLEPAD_POS); - state.hex |= GetCirclePadDirectionState(circle_pad_x, circle_pad_y).hex; + const DirectionState direction = GetStickDirectionState(circle_pad_x, circle_pad_y); + state.circle_up.Assign(direction.up); + state.circle_down.Assign(direction.down); + state.circle_left.Assign(direction.left); + state.circle_right.Assign(direction.right); mem->pad.current_state.hex = state.hex; mem->pad.index = next_pad_index; diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h index b505cdcd5..1ef972e70 100644 --- a/src/core/hle/service/hid/hid.h +++ b/src/core/hle/service/hid/hid.h @@ -176,6 +176,16 @@ ASSERT_REG_POSITION(touch.index_reset_ticks, 0x2A); #undef ASSERT_REG_POSITION #endif // !defined(_MSC_VER) +struct DirectionState { + bool up; + bool down; + bool left; + bool right; +}; + +/// Translates analog stick axes to directions. This is exposed for ir_rst module to use. +DirectionState GetStickDirectionState(s16 circle_pad_x, s16 circle_pad_y); + /** * HID::GetIPCHandles service function * Inputs: diff --git a/src/core/hle/service/ir/extra_hid.cpp b/src/core/hle/service/ir/extra_hid.cpp new file mode 100644 index 000000000..e7acc17a5 --- /dev/null +++ b/src/core/hle/service/ir/extra_hid.cpp @@ -0,0 +1,231 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/alignment.h" +#include "common/bit_field.h" +#include "common/string_util.h" +#include "core/core_timing.h" +#include "core/hle/service/ir/extra_hid.h" +#include "core/settings.h" + +namespace Service { +namespace IR { + +enum class RequestID : u8 { + /** + * ConfigureHIDPolling request + * Starts HID input polling, or changes the polling interval if it is already started. + * Inputs: + * byte 0: request ID + * byte 1: polling interval in ms + * byte 2: unknown + */ + ConfigureHIDPolling = 1, + + /** + * ReadCalibrationData request + * Reads the calibration data stored in circle pad pro. + * Inputs: + * byte 0: request ID + * byte 1: expected response time in ms? + * byte 2-3: data offset (aligned to 0x10) + * byte 4-5: data size (aligned to 0x10) + */ + ReadCalibrationData = 2, + + // TODO(wwylele): there are three more request types (id = 3, 4 and 5) +}; + +enum class ResponseID : u8 { + + /** + * PollHID response + * Sends current HID status + * Output: + * byte 0: response ID + * byte 1-3: Right circle pad position. This three bytes are two little-endian 12-bit + * fields. The first one is for x-axis and the second one is for y-axis. + * byte 4: bit[0:4] battery level; bit[5] ZL button; bit[6] ZR button; bit[7] R button + * Note that for the three button fields, the bit is set when the button is NOT pressed. + * byte 5: unknown + */ + PollHID = 0x10, + + /** + * ReadCalibrationData response + * Sends the calibration data reads from circle pad pro. + * Output: + * byte 0: resonse ID + * byte 1-2: data offset (aligned to 0x10) + * byte 3-4: data size (aligned to 0x10) + * byte 5-...: calibration data + */ + ReadCalibrationData = 0x11, +}; + +ExtraHID::ExtraHID(SendFunc send_func) : IRDevice(send_func) { + LoadInputDevices(); + + // The data below was retrieved from a New 3DS + // TODO(wwylele): this data is probably writable (via request 3?) and thus should be saved to + // and loaded from somewhere. + calibration_data = std::array<u8, 0x40>{{ + // 0x00 + 0x00, 0x00, 0x08, 0x80, 0x85, 0xEB, 0x11, 0x3F, + // 0x08 + 0x85, 0xEB, 0x11, 0x3F, 0xFF, 0xFF, 0xFF, 0xF5, + // 0x10 + 0xFF, 0x00, 0x08, 0x80, 0x85, 0xEB, 0x11, 0x3F, + // 0x18 + 0x85, 0xEB, 0x11, 0x3F, 0xFF, 0xFF, 0xFF, 0x65, + // 0x20 + 0xFF, 0x00, 0x08, 0x80, 0x85, 0xEB, 0x11, 0x3F, + // 0x28 + 0x85, 0xEB, 0x11, 0x3F, 0xFF, 0xFF, 0xFF, 0x65, + // 0x30 + 0xFF, 0x00, 0x08, 0x80, 0x85, 0xEB, 0x11, 0x3F, + // 0x38 + 0x85, 0xEB, 0x11, 0x3F, 0xFF, 0xFF, 0xFF, 0x65, + }}; + + hid_polling_callback_id = + CoreTiming::RegisterEvent("ExtraHID::SendHIDStatus", [this](u64, int cycles_late) { + SendHIDStatus(); + CoreTiming::ScheduleEvent(msToCycles(hid_period) - cycles_late, + hid_polling_callback_id); + }); +} + +ExtraHID::~ExtraHID() { + OnDisconnect(); +} + +void ExtraHID::OnConnect() {} + +void ExtraHID::OnDisconnect() { + CoreTiming::UnscheduleEvent(hid_polling_callback_id, 0); +} + +void ExtraHID::HandleConfigureHIDPollingRequest(const std::vector<u8>& request) { + if (request.size() != 3) { + LOG_ERROR(Service_IR, "Wrong request size (%zu): %s", request.size(), + Common::ArrayToString(request.data(), request.size()).c_str()); + return; + } + + // Change HID input polling interval + CoreTiming::UnscheduleEvent(hid_polling_callback_id, 0); + hid_period = request[1]; + CoreTiming::ScheduleEvent(msToCycles(hid_period), hid_polling_callback_id); +} + +void ExtraHID::HandleReadCalibrationDataRequest(const std::vector<u8>& request_buf) { + struct ReadCalibrationDataRequest { + RequestID request_id; + u8 expected_response_time; + u16_le offset; + u16_le size; + }; + static_assert(sizeof(ReadCalibrationDataRequest) == 6, + "ReadCalibrationDataRequest has wrong size"); + + if (request_buf.size() != sizeof(ReadCalibrationDataRequest)) { + LOG_ERROR(Service_IR, "Wrong request size (%zu): %s", request_buf.size(), + Common::ArrayToString(request_buf.data(), request_buf.size()).c_str()); + return; + } + + ReadCalibrationDataRequest request; + std::memcpy(&request, request_buf.data(), sizeof(request)); + + const u16 offset = Common::AlignDown(request.offset, 16); + const u16 size = Common::AlignDown(request.size, 16); + + if (offset + size > calibration_data.size()) { + LOG_ERROR(Service_IR, "Read beyond the end of calibration data! (offset=%u, size=%u)", + offset, size); + return; + } + + std::vector<u8> response(5); + response[0] = static_cast<u8>(ResponseID::ReadCalibrationData); + std::memcpy(&response[1], &request.offset, sizeof(request.offset)); + std::memcpy(&response[3], &request.size, sizeof(request.size)); + response.insert(response.end(), calibration_data.begin() + offset, + calibration_data.begin() + offset + size); + Send(response); +} + +void ExtraHID::OnReceive(const std::vector<u8>& data) { + switch (static_cast<RequestID>(data[0])) { + case RequestID::ConfigureHIDPolling: + HandleConfigureHIDPollingRequest(data); + break; + case RequestID::ReadCalibrationData: + HandleReadCalibrationDataRequest(data); + break; + default: + LOG_ERROR(Service_IR, "Unknown request: %s", + Common::ArrayToString(data.data(), data.size()).c_str()); + break; + } +} + +void ExtraHID::SendHIDStatus() { + if (is_device_reload_pending.exchange(false)) + LoadInputDevices(); + + struct { + union { + BitField<0, 8, u32_le> header; + BitField<8, 12, u32_le> c_stick_x; + BitField<20, 12, u32_le> c_stick_y; + } c_stick; + union { + BitField<0, 5, u8> battery_level; + BitField<5, 1, u8> zl_not_held; + BitField<6, 1, u8> zr_not_held; + BitField<7, 1, u8> r_not_held; + } buttons; + u8 unknown; + } response; + static_assert(sizeof(response) == 6, "HID status response has wrong size!"); + + constexpr int C_STICK_CENTER = 0x800; + // TODO(wwylele): this value is not accurately measured. We currently assume that the axis can + // take values in the whole range of a 12-bit integer. + constexpr int C_STICK_RADIUS = 0x7FF; + + float x, y; + std::tie(x, y) = c_stick->GetStatus(); + + response.c_stick.header.Assign(static_cast<u8>(ResponseID::PollHID)); + response.c_stick.c_stick_x.Assign(static_cast<u32>(C_STICK_CENTER + C_STICK_RADIUS * x)); + response.c_stick.c_stick_y.Assign(static_cast<u32>(C_STICK_CENTER + C_STICK_RADIUS * y)); + response.buttons.battery_level.Assign(0x1F); + response.buttons.zl_not_held.Assign(!zl->GetStatus()); + response.buttons.zr_not_held.Assign(!zr->GetStatus()); + response.buttons.r_not_held.Assign(1); + response.unknown = 0; + + std::vector<u8> response_buffer(sizeof(response)); + memcpy(response_buffer.data(), &response, sizeof(response)); + Send(response_buffer); +} + +void ExtraHID::RequestInputDevicesReload() { + is_device_reload_pending.store(true); +} + +void ExtraHID::LoadInputDevices() { + zl = Input::CreateDevice<Input::ButtonDevice>( + Settings::values.buttons[Settings::NativeButton::ZL]); + zr = Input::CreateDevice<Input::ButtonDevice>( + Settings::values.buttons[Settings::NativeButton::ZR]); + c_stick = Input::CreateDevice<Input::AnalogDevice>( + Settings::values.analogs[Settings::NativeAnalog::CStick]); +} + +} // namespace IR +} // namespace Service diff --git a/src/core/hle/service/ir/extra_hid.h b/src/core/hle/service/ir/extra_hid.h new file mode 100644 index 000000000..a2459a73a --- /dev/null +++ b/src/core/hle/service/ir/extra_hid.h @@ -0,0 +1,48 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <atomic> +#include "core/frontend/input.h" +#include "core/hle/service/ir/ir_user.h" + +namespace Service { +namespace IR { + +/** + * An IRDevice emulating Circle Pad Pro or New 3DS additional HID hardware. + * This device sends periodic udates at a rate configured by the 3DS, and sends calibration data if + * requested. + */ +class ExtraHID final : public IRDevice { +public: + explicit ExtraHID(SendFunc send_func); + ~ExtraHID(); + + void OnConnect() override; + void OnDisconnect() override; + void OnReceive(const std::vector<u8>& data) override; + + /// Requests input devices reload from current settings. Called when the input settings change. + void RequestInputDevicesReload(); + +private: + void SendHIDStatus(); + void HandleConfigureHIDPollingRequest(const std::vector<u8>& request); + void HandleReadCalibrationDataRequest(const std::vector<u8>& request); + void LoadInputDevices(); + + u8 hid_period; + int hid_polling_callback_id; + std::array<u8, 0x40> calibration_data; + std::unique_ptr<Input::ButtonDevice> zl; + std::unique_ptr<Input::ButtonDevice> zr; + std::unique_ptr<Input::AnalogDevice> c_stick; + std::atomic<bool> is_device_reload_pending; +}; + +} // namespace IR +} // namespace Service diff --git a/src/core/hle/service/ir/ir.cpp b/src/core/hle/service/ir/ir.cpp index 7ac34a990..f06dd552f 100644 --- a/src/core/hle/service/ir/ir.cpp +++ b/src/core/hle/service/ir/ir.cpp @@ -25,6 +25,11 @@ void Shutdown() { ShutdownRST(); } +void ReloadInputDevices() { + ReloadInputDevicesUser(); + ReloadInputDevicesRST(); +} + } // namespace IR } // namespace Service diff --git a/src/core/hle/service/ir/ir.h b/src/core/hle/service/ir/ir.h index c741498e2..6be3e950c 100644 --- a/src/core/hle/service/ir/ir.h +++ b/src/core/hle/service/ir/ir.h @@ -16,5 +16,8 @@ void Init(); /// Shutdown IR service void Shutdown(); +/// Reload input devices. Used when input configuration changed +void ReloadInputDevices(); + } // namespace IR } // namespace Service diff --git a/src/core/hle/service/ir/ir_rst.cpp b/src/core/hle/service/ir/ir_rst.cpp index 3f1275c53..53807cd91 100644 --- a/src/core/hle/service/ir/ir_rst.cpp +++ b/src/core/hle/service/ir/ir_rst.cpp @@ -2,16 +2,135 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <atomic> +#include "common/bit_field.h" +#include "core/core_timing.h" +#include "core/frontend/input.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/shared_memory.h" +#include "core/hle/service/hid/hid.h" #include "core/hle/service/ir/ir.h" #include "core/hle/service/ir/ir_rst.h" +#include "core/settings.h" namespace Service { namespace IR { -static Kernel::SharedPtr<Kernel::Event> handle_event; +union PadState { + u32_le hex; + + BitField<14, 1, u32_le> zl; + BitField<15, 1, u32_le> zr; + + BitField<24, 1, u32_le> c_stick_right; + BitField<25, 1, u32_le> c_stick_left; + BitField<26, 1, u32_le> c_stick_up; + BitField<27, 1, u32_le> c_stick_down; +}; + +struct PadDataEntry { + PadState current_state; + PadState delta_additions; + PadState delta_removals; + + s16_le c_stick_x; + s16_le c_stick_y; +}; + +struct SharedMem { + u64_le index_reset_ticks; ///< CPU tick count for when HID module updated entry index 0 + u64_le index_reset_ticks_previous; ///< Previous `index_reset_ticks` + u32_le index; + INSERT_PADDING_WORDS(1); + std::array<PadDataEntry, 8> entries; ///< Last 8 pad entries +}; + +static_assert(sizeof(SharedMem) == 0x98, "SharedMem has wrong size!"); + +static Kernel::SharedPtr<Kernel::Event> update_event; static Kernel::SharedPtr<Kernel::SharedMemory> shared_memory; +static u32 next_pad_index; +static int update_callback_id; +static std::unique_ptr<Input::ButtonDevice> zl_button; +static std::unique_ptr<Input::ButtonDevice> zr_button; +static std::unique_ptr<Input::AnalogDevice> c_stick; +static std::atomic<bool> is_device_reload_pending; +static bool raw_c_stick; +static int update_period; + +static void LoadInputDevices() { + zl_button = Input::CreateDevice<Input::ButtonDevice>( + Settings::values.buttons[Settings::NativeButton::ZL]); + zr_button = Input::CreateDevice<Input::ButtonDevice>( + Settings::values.buttons[Settings::NativeButton::ZR]); + c_stick = Input::CreateDevice<Input::AnalogDevice>( + Settings::values.analogs[Settings::NativeAnalog::CStick]); +} + +static void UnloadInputDevices() { + zl_button = nullptr; + zr_button = nullptr; + c_stick = nullptr; +} + +static void UpdateCallback(u64 userdata, int cycles_late) { + SharedMem* mem = reinterpret_cast<SharedMem*>(shared_memory->GetPointer()); + + if (is_device_reload_pending.exchange(false)) + LoadInputDevices(); + + PadState state; + state.zl.Assign(zl_button->GetStatus()); + state.zr.Assign(zr_button->GetStatus()); + + // Get current c-stick position and update c-stick direction + float c_stick_x_f, c_stick_y_f; + std::tie(c_stick_x_f, c_stick_y_f) = c_stick->GetStatus(); + constexpr int MAX_CSTICK_RADIUS = 0x9C; // Max value for a c-stick radius + const s16 c_stick_x = static_cast<s16>(c_stick_x_f * MAX_CSTICK_RADIUS); + const s16 c_stick_y = static_cast<s16>(c_stick_y_f * MAX_CSTICK_RADIUS); + + if (!raw_c_stick) { + const HID::DirectionState direction = HID::GetStickDirectionState(c_stick_x, c_stick_y); + state.c_stick_up.Assign(direction.up); + state.c_stick_down.Assign(direction.down); + state.c_stick_left.Assign(direction.left); + state.c_stick_right.Assign(direction.right); + } + + // TODO (wwylele): implement raw C-stick data for raw_c_stick = true + + const u32 last_entry_index = mem->index; + mem->index = next_pad_index; + next_pad_index = (next_pad_index + 1) % mem->entries.size(); + + // Get the previous Pad state + PadState old_state{mem->entries[last_entry_index].current_state}; + + // Compute bitmask with 1s for bits different from the old state + PadState changed = {state.hex ^ old_state.hex}; + + // Get the current Pad entry + PadDataEntry& pad_entry = mem->entries[mem->index]; + + // Update entry properties + pad_entry.current_state.hex = state.hex; + pad_entry.delta_additions.hex = changed.hex & state.hex; + pad_entry.delta_removals.hex = changed.hex & old_state.hex; + pad_entry.c_stick_x = c_stick_x; + pad_entry.c_stick_y = c_stick_y; + + // If we just updated index 0, provide a new timestamp + if (mem->index == 0) { + mem->index_reset_ticks_previous = mem->index_reset_ticks; + mem->index_reset_ticks = CoreTiming::GetTicks(); + } + + update_event->Signal(); + + // Reschedule recurrent event + CoreTiming::ScheduleEvent(msToCycles(update_period) - cycles_late, update_callback_id); +} /** * IR::GetHandles service function @@ -22,18 +141,52 @@ static Kernel::SharedPtr<Kernel::SharedMemory> shared_memory; * 4 : Event handle */ static void GetHandles(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x01, 0, 0); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 3); + rb.Push(RESULT_SUCCESS); + rb.PushMoveHandles(Kernel::g_handle_table.Create(Service::IR::shared_memory).MoveFrom(), + Kernel::g_handle_table.Create(Service::IR::update_event).MoveFrom()); +} + +/** + * IR::Initialize service function + * Inputs: + * 1 : pad state update period in ms + * 2 : bool output raw c-stick data + */ +static void Initialize(Interface* self) { + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x02, 2, 0); + update_period = static_cast<int>(rp.Pop<u32>()); + raw_c_stick = rp.Pop<bool>(); - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = 0x4000000; - cmd_buff[3] = Kernel::g_handle_table.Create(Service::IR::shared_memory).MoveFrom(); - cmd_buff[4] = Kernel::g_handle_table.Create(Service::IR::handle_event).MoveFrom(); + if (raw_c_stick) + LOG_ERROR(Service_IR, "raw C-stick data is not implemented!"); + + next_pad_index = 0; + is_device_reload_pending.store(true); + CoreTiming::ScheduleEvent(msToCycles(update_period), update_callback_id); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); + + LOG_DEBUG(Service_IR, "called. update_period=%d, raw_c_stick=%d", update_period, raw_c_stick); +} + +static void Shutdown(Interface* self) { + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x03, 1, 0); + + CoreTiming::UnscheduleEvent(update_callback_id, 0); + UnloadInputDevices(); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); + LOG_DEBUG(Service_IR, "called"); } const Interface::FunctionInfo FunctionTable[] = { {0x00010000, GetHandles, "GetHandles"}, - {0x00020080, nullptr, "Initialize"}, - {0x00030000, nullptr, "Shutdown"}, + {0x00020080, Initialize, "Initialize"}, + {0x00030000, Shutdown, "Shutdown"}, {0x00090000, nullptr, "WriteToTwoFields"}, }; @@ -43,17 +196,24 @@ IR_RST_Interface::IR_RST_Interface() { void InitRST() { using namespace Kernel; - + // Note: these two kernel objects are even available before Initialize service function is + // called. shared_memory = - SharedMemory::Create(nullptr, 0x1000, MemoryPermission::ReadWrite, - MemoryPermission::ReadWrite, 0, MemoryRegion::BASE, "IR:SharedMemory"); + SharedMemory::Create(nullptr, 0x1000, MemoryPermission::ReadWrite, MemoryPermission::Read, + 0, MemoryRegion::BASE, "IRRST:SharedMemory"); + update_event = Event::Create(ResetType::OneShot, "IRRST:UpdateEvent"); - handle_event = Event::Create(ResetType::OneShot, "IR:HandleEvent"); + update_callback_id = CoreTiming::RegisterEvent("IRRST:UpdateCallBack", UpdateCallback); } void ShutdownRST() { shared_memory = nullptr; - handle_event = nullptr; + update_event = nullptr; + UnloadInputDevices(); +} + +void ReloadInputDevicesRST() { + is_device_reload_pending.store(true); } } // namespace IR diff --git a/src/core/hle/service/ir/ir_rst.h b/src/core/hle/service/ir/ir_rst.h index 75b732627..d932bb7e5 100644 --- a/src/core/hle/service/ir/ir_rst.h +++ b/src/core/hle/service/ir/ir_rst.h @@ -21,5 +21,8 @@ public: void InitRST(); void ShutdownRST(); +/// Reload input devices. Used when input configuration changed +void ReloadInputDevicesRST(); + } // namespace IR } // namespace Service diff --git a/src/core/hle/service/ir/ir_user.cpp b/src/core/hle/service/ir/ir_user.cpp index b326d7fc7..226af0083 100644 --- a/src/core/hle/service/ir/ir_user.cpp +++ b/src/core/hle/service/ir/ir_user.cpp @@ -2,110 +2,481 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <memory> +#include <boost/crc.hpp> +#include <boost/optional.hpp> +#include "common/string_util.h" +#include "common/swap.h" #include "core/hle/kernel/event.h" #include "core/hle/kernel/shared_memory.h" +#include "core/hle/service/ir/extra_hid.h" #include "core/hle/service/ir/ir.h" #include "core/hle/service/ir/ir_user.h" namespace Service { namespace IR { -static Kernel::SharedPtr<Kernel::Event> conn_status_event; -static Kernel::SharedPtr<Kernel::SharedMemory> transfer_shared_memory; +// This is a header that will present in the ir:USER shared memory if it is initialized with +// InitializeIrNopShared service function. Otherwise the shared memory doesn't have this header if +// it is initialized with InitializeIrNop service function. +struct SharedMemoryHeader { + u32_le latest_receive_error_result; + u32_le latest_send_error_result; + // TODO(wwylele): for these fields below, make them enum when the meaning of values is known. + u8 connection_status; + u8 trying_to_connect_status; + u8 connection_role; + u8 machine_id; + u8 connected; + u8 network_id; + u8 initialized; + u8 unknown; + + // This is not the end of the shared memory. It is followed by a receive buffer and a send + // buffer. We handle receive buffer in the BufferManager class. For the send buffer, because + // games usually don't access it, we don't emulate it. +}; +static_assert(sizeof(SharedMemoryHeader) == 16, "SharedMemoryHeader has wrong size!"); + +/** + * A manager of the send/receive buffers in the shared memory. Currently it is only used for the + * receive buffer. + * + * A buffer consists of three parts: + * - BufferInfo: stores available count of packets, and their position in the PacketInfo + * circular queue. + * - PacketInfo circular queue: stores the position of each avaiable packets in the Packet data + * buffer. Each entry is a pair of {offset, size}. + * - Packet data circular buffer: stores the actual data of packets. + * + * IR packets can be put into and get from the buffer. + * + * When a new packet is put into the buffer, its data is put into the data circular buffer, + * following the end of previous packet data. A new entry is also added to the PacketInfo circular + * queue pointing to the added packet data. Then BufferInfo is updated. + * + * Packets can be released from the other end of the buffer. When releasing a packet, the front + * entry in thePacketInfo circular queue is removed, and as a result the corresponding memory in the + * data circular buffer is also released. BufferInfo is updated as well. + * + * The client application usually has a similar manager constructed over the same shared memory + * region, performing the same put/get/release operation. This way the client and the service + * communicate via a pair of manager of the same buffer. + * + * TODO(wwylele): implement Get function, which is used by ReceiveIrnop service function. + */ +class BufferManager { +public: + BufferManager(Kernel::SharedPtr<Kernel::SharedMemory> shared_memory_, u32 info_offset_, + u32 buffer_offset_, u32 max_packet_count_, u32 buffer_size) + : shared_memory(shared_memory_), info_offset(info_offset_), buffer_offset(buffer_offset_), + max_packet_count(max_packet_count_), + max_data_size(buffer_size - sizeof(PacketInfo) * max_packet_count_) { + UpdateBufferInfo(); + } + + /** + * Puts a packet to the head of the buffer. + * @params packet The data of the packet to put. + * @returns whether the operation is successful. + */ + bool Put(const std::vector<u8>& packet) { + if (info.packet_count == max_packet_count) + return false; + + u32 write_offset; + + // finds free space offset in data buffer + if (info.packet_count == 0) { + write_offset = 0; + if (packet.size() > max_data_size) + return false; + } else { + const u32 last_index = (info.end_index + max_packet_count - 1) % max_packet_count; + const PacketInfo first = GetPacketInfo(info.begin_index); + const PacketInfo last = GetPacketInfo(last_index); + write_offset = (last.offset + last.size) % max_data_size; + const u32 free_space = (first.offset + max_data_size - write_offset) % max_data_size; + if (packet.size() > free_space) + return false; + } + + // writes packet info + PacketInfo packet_info{write_offset, static_cast<u32>(packet.size())}; + SetPacketInfo(info.end_index, packet_info); + + // writes packet data + for (size_t i = 0; i < packet.size(); ++i) { + *GetDataBufferPointer((write_offset + i) % max_data_size) = packet[i]; + } + + // updates buffer info + info.end_index++; + info.end_index %= max_packet_count; + info.packet_count++; + UpdateBufferInfo(); + return true; + } + + /** + * Release packets from the tail of the buffer + * @params count Numbers of packets to release. + * @returns whether the operation is successful. + */ + bool Release(u32 count) { + if (info.packet_count < count) + return false; + + info.packet_count -= count; + info.begin_index += count; + info.begin_index %= max_packet_count; + UpdateBufferInfo(); + return true; + } + +private: + struct BufferInfo { + u32_le begin_index; + u32_le end_index; + u32_le packet_count; + u32_le unknown; + }; + static_assert(sizeof(BufferInfo) == 16, "BufferInfo has wrong size!"); + + struct PacketInfo { + u32_le offset; + u32_le size; + }; + static_assert(sizeof(PacketInfo) == 8, "PacketInfo has wrong size!"); + + u8* GetPacketInfoPointer(u32 index) { + return shared_memory->GetPointer(buffer_offset + sizeof(PacketInfo) * index); + } + + void SetPacketInfo(u32 index, const PacketInfo& packet_info) { + memcpy(GetPacketInfoPointer(index), &packet_info, sizeof(PacketInfo)); + } + + PacketInfo GetPacketInfo(u32 index) { + PacketInfo packet_info; + memcpy(&packet_info, GetPacketInfoPointer(index), sizeof(PacketInfo)); + return packet_info; + } + + u8* GetDataBufferPointer(u32 offset) { + return shared_memory->GetPointer(buffer_offset + sizeof(PacketInfo) * max_packet_count + + offset); + } + + void UpdateBufferInfo() { + if (info_offset) { + memcpy(shared_memory->GetPointer(info_offset), &info, sizeof(info)); + } + } + + BufferInfo info{0, 0, 0, 0}; + Kernel::SharedPtr<Kernel::SharedMemory> shared_memory; + u32 info_offset; + u32 buffer_offset; + u32 max_packet_count; + u32 max_data_size; +}; + +static Kernel::SharedPtr<Kernel::Event> conn_status_event, send_event, receive_event; +static Kernel::SharedPtr<Kernel::SharedMemory> shared_memory; +static std::unique_ptr<ExtraHID> extra_hid; +static IRDevice* connected_device; +static boost::optional<BufferManager> receive_buffer; + +/// Wraps the payload into packet and puts it to the receive buffer +static void PutToReceive(const std::vector<u8>& payload) { + LOG_TRACE(Service_IR, "called, data=%s", + Common::ArrayToString(payload.data(), payload.size()).c_str()); + size_t size = payload.size(); + + std::vector<u8> packet; + + // Builds packet header. For the format info: + // https://www.3dbrew.org/wiki/IRUSER_Shared_Memory#Packet_structure + + // fixed value + packet.push_back(0xA5); + // destination network ID + u8 network_id = *(shared_memory->GetPointer(offsetof(SharedMemoryHeader, network_id))); + packet.push_back(network_id); + + // puts the size info. + // The highest bit of the first byte is unknown, which is set to zero here. The second highest + // bit is a flag that determines whether the size info is in extended form. If the packet size + // can be represent within 6 bits, the short form (1 byte) of size info is chosen, the size is + // put to the lower bits of this byte, and the flag is clear. If the packet size cannot be + // represent within 6 bits, the extended form (2 bytes) is chosen, the lower 8 bits of the size + // is put to the second byte, the higher bits of the size is put to the lower bits of the first + // byte, and the flag is set. Note that the packet size must be within 14 bits due to this + // format restriction, or it will overlap with the flag bit. + if (size < 0x40) { + packet.push_back(static_cast<u8>(size)); + } else if (size < 0x4000) { + packet.push_back(static_cast<u8>(size >> 8) | 0x40); + packet.push_back(static_cast<u8>(size)); + } else { + ASSERT(false); + } + + // puts the payload + packet.insert(packet.end(), payload.begin(), payload.end()); + + // calculates CRC and puts to the end + packet.push_back(boost::crc<8, 0x07, 0, 0, false, false>(packet.data(), packet.size())); + + if (receive_buffer->Put(packet)) { + receive_event->Signal(); + } else { + LOG_ERROR(Service_IR, "receive buffer is full!"); + } +} /** * IR::InitializeIrNopShared service function + * Initializes ir:USER service with a user provided shared memory. The shared memory is configured + * to shared mode (with SharedMemoryHeader at the beginning of the shared memory). * Inputs: - * 1 : Size of transfer buffer + * 1 : Size of shared memory * 2 : Recv buffer size - * 3 : unknown + * 3 : Recv buffer packet count * 4 : Send buffer size - * 5 : unknown + * 5 : Send buffer packet count * 6 : BaudRate (u8) - * 7 : 0 - * 8 : Handle of transfer shared memory + * 7 : 0 (Handle descriptor) + * 8 : Handle of shared memory * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ static void InitializeIrNopShared(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x18, 6, 2); + const u32 shared_buff_size = rp.Pop<u32>(); + const u32 recv_buff_size = rp.Pop<u32>(); + const u32 recv_buff_packet_count = rp.Pop<u32>(); + const u32 send_buff_size = rp.Pop<u32>(); + const u32 send_buff_packet_count = rp.Pop<u32>(); + const u8 baud_rate = rp.Pop<u8>(); + const Kernel::Handle handle = rp.PopHandle(); - u32 transfer_buff_size = cmd_buff[1]; - u32 recv_buff_size = cmd_buff[2]; - u32 unk1 = cmd_buff[3]; - u32 send_buff_size = cmd_buff[4]; - u32 unk2 = cmd_buff[5]; - u8 baud_rate = cmd_buff[6] & 0xFF; - Kernel::Handle handle = cmd_buff[8]; + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - if (Kernel::g_handle_table.IsValid(handle)) { - transfer_shared_memory = Kernel::g_handle_table.Get<Kernel::SharedMemory>(handle); - transfer_shared_memory->name = "IR:TransferSharedMemory"; + shared_memory = Kernel::g_handle_table.Get<Kernel::SharedMemory>(handle); + if (!shared_memory) { + LOG_CRITICAL(Service_IR, "invalid shared memory handle 0x%08X", handle); + rb.Push(ResultCode(ErrorDescription::InvalidHandle, ErrorModule::OS, + ErrorSummary::WrongArgument, ErrorLevel::Permanent)); + return; } + shared_memory->name = "IR_USER: shared memory"; - cmd_buff[1] = RESULT_SUCCESS.raw; + receive_buffer = + BufferManager(shared_memory, 0x10, 0x20, recv_buff_packet_count, recv_buff_size); + SharedMemoryHeader shared_memory_init{}; + shared_memory_init.initialized = 1; + std::memcpy(shared_memory->GetPointer(), &shared_memory_init, sizeof(SharedMemoryHeader)); - LOG_WARNING(Service_IR, "(STUBBED) called, transfer_buff_size=%d, recv_buff_size=%d, " - "unk1=%d, send_buff_size=%d, unk2=%d, baud_rate=%u, handle=0x%08X", - transfer_buff_size, recv_buff_size, unk1, send_buff_size, unk2, baud_rate, handle); + rb.Push(RESULT_SUCCESS); + + LOG_INFO(Service_IR, "called, shared_buff_size=%u, recv_buff_size=%u, " + "recv_buff_packet_count=%u, send_buff_size=%u, " + "send_buff_packet_count=%u, baud_rate=%u, handle=0x%08X", + shared_buff_size, recv_buff_size, recv_buff_packet_count, send_buff_size, + send_buff_packet_count, baud_rate, handle); } /** * IR::RequireConnection service function + * Searches for an IR device and connects to it. After connecting to the device, applications can + * use SendIrNop function, ReceiveIrNop function (or read from the buffer directly) to communicate + * with the device. * Inputs: - * 1 : unknown (u8), looks like always 1 + * 1 : device ID? always 1 for circle pad pro * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ static void RequireConnection(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x06, 1, 0); + const u8 device_id = rp.Pop<u8>(); + + u8* shared_memory_ptr = shared_memory->GetPointer(); + if (device_id == 1) { + // These values are observed on a New 3DS. The meaning of them is unclear. + // TODO (wwylele): should assign network_id a (random?) number + shared_memory_ptr[offsetof(SharedMemoryHeader, connection_status)] = 2; + shared_memory_ptr[offsetof(SharedMemoryHeader, connection_role)] = 2; + shared_memory_ptr[offsetof(SharedMemoryHeader, connected)] = 1; + + connected_device = extra_hid.get(); + connected_device->OnConnect(); + conn_status_event->Signal(); + } else { + LOG_WARNING(Service_IR, "unknown device id %u. Won't connect.", device_id); + shared_memory_ptr[offsetof(SharedMemoryHeader, connection_status)] = 1; + shared_memory_ptr[offsetof(SharedMemoryHeader, trying_to_connect_status)] = 2; + } + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); + + LOG_INFO(Service_IR, "called, device_id = %u", device_id); +} + +/** + * IR::GetReceiveEvent service function + * Gets an event that is signaled when a packet is received from the IR device. + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : 0 (Handle descriptor) + * 3 : Receive event handle + */ +void GetReceiveEvent(Interface* self) { + IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x0A, 1, 2); - conn_status_event->Signal(); + rb.Push(RESULT_SUCCESS); + rb.PushCopyHandles(Kernel::g_handle_table.Create(Service::IR::receive_event).MoveFrom()); + + LOG_INFO(Service_IR, "called"); +} + +/** + * IR::GetSendEvent service function + * Gets an event that is signaled when the sending of a packet is complete + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : 0 (Handle descriptor) + * 3 : Send event handle + */ +void GetSendEvent(Interface* self) { + IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x0B, 1, 2); - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); + rb.PushCopyHandles(Kernel::g_handle_table.Create(Service::IR::send_event).MoveFrom()); - LOG_WARNING(Service_IR, "(STUBBED) called"); + LOG_INFO(Service_IR, "called"); } /** * IR::Disconnect service function + * Disconnects from the current connected IR device. * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ static void Disconnect(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + if (connected_device) { + connected_device->OnDisconnect(); + connected_device = nullptr; + conn_status_event->Signal(); + } + + u8* shared_memory_ptr = shared_memory->GetPointer(); + shared_memory_ptr[offsetof(SharedMemoryHeader, connection_status)] = 0; + shared_memory_ptr[offsetof(SharedMemoryHeader, connected)] = 0; - cmd_buff[1] = RESULT_SUCCESS.raw; + IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x09, 1, 0); + rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_IR, "(STUBBED) called"); + LOG_INFO(Service_IR, "called"); } /** * IR::GetConnectionStatusEvent service function + * Gets an event that is signaled when the connection status is changed * Outputs: * 1 : Result of function, 0 on success, otherwise error code - * 2 : Connection Status Event handle + * 2 : 0 (Handle descriptor) + * 3 : Connection Status Event handle */ static void GetConnectionStatusEvent(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x0C, 1, 2); - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[3] = Kernel::g_handle_table.Create(Service::IR::conn_status_event).MoveFrom(); + rb.Push(RESULT_SUCCESS); + rb.PushCopyHandles(Kernel::g_handle_table.Create(Service::IR::conn_status_event).MoveFrom()); - LOG_WARNING(Service_IR, "(STUBBED) called"); + LOG_INFO(Service_IR, "called"); } /** * IR::FinalizeIrNop service function + * Finalize ir:USER service. * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ static void FinalizeIrNop(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + if (connected_device) { + connected_device->OnDisconnect(); + connected_device = nullptr; + } + + shared_memory = nullptr; + receive_buffer = boost::none; + + IPC::RequestBuilder rb(Kernel::GetCommandBuffer(), 0x02, 1, 0); + rb.Push(RESULT_SUCCESS); + + LOG_INFO(Service_IR, "called"); +} + +/** + * IR::SendIrNop service function + * Sends a packet to the connected IR device + * Inpus: + * 1 : Size of data to send + * 2 : 2 + (size << 14) (Static buffer descriptor) + * 3 : Data buffer address + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void SendIrNop(Interface* self) { + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x0D, 1, 2); + const u32 size = rp.Pop<u32>(); + const VAddr address = rp.PopStaticBuffer(); + + std::vector<u8> buffer(size); + Memory::ReadBlock(address, buffer.data(), size); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + if (connected_device) { + connected_device->OnReceive(buffer); + send_event->Signal(); + rb.Push(RESULT_SUCCESS); + } else { + LOG_ERROR(Service_IR, "not connected"); + rb.Push(ResultCode(static_cast<ErrorDescription>(13), ErrorModule::IR, + ErrorSummary::InvalidState, ErrorLevel::Status)); + } + + LOG_TRACE(Service_IR, "called, data=%s", Common::ArrayToString(buffer.data(), size).c_str()); +} + +/** + * IR::ReleaseReceivedData function + * Release a specified amount of packet from the receive buffer. This is called after the + * application reads received packet from the buffer directly, to release the buffer space for + * future packets. + * Inpus: + * 1 : Number of packets to release + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + */ +static void ReleaseReceivedData(Interface* self) { + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x19, 1, 0); + u32 count = rp.Pop<u32>(); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; + if (receive_buffer->Release(count)) { + rb.Push(RESULT_SUCCESS); + } else { + LOG_ERROR(Service_IR, "failed to release %u packets", count); + rb.Push(ResultCode(ErrorDescription::NoData, ErrorModule::IR, ErrorSummary::NotFound, + ErrorLevel::Status)); + } - LOG_WARNING(Service_IR, "(STUBBED) called"); + LOG_TRACE(Service_IR, "called, count=%u", count); } const Interface::FunctionInfo FunctionTable[] = { @@ -118,10 +489,10 @@ const Interface::FunctionInfo FunctionTable[] = { {0x000702C0, nullptr, "AutoConnection"}, {0x00080000, nullptr, "AnyConnection"}, {0x00090000, Disconnect, "Disconnect"}, - {0x000A0000, nullptr, "GetReceiveEvent"}, - {0x000B0000, nullptr, "GetSendEvent"}, + {0x000A0000, GetReceiveEvent, "GetReceiveEvent"}, + {0x000B0000, GetSendEvent, "GetSendEvent"}, {0x000C0000, GetConnectionStatusEvent, "GetConnectionStatusEvent"}, - {0x000D0042, nullptr, "SendIrNop"}, + {0x000D0042, SendIrNop, "SendIrNop"}, {0x000E0042, nullptr, "SendIrNopLarge"}, {0x000F0040, nullptr, "ReceiveIrnop"}, {0x00100042, nullptr, "ReceiveIrnopLarge"}, @@ -133,7 +504,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00160000, nullptr, "GetSendSizeFreeAndUsed"}, {0x00170000, nullptr, "GetConnectionRole"}, {0x00180182, InitializeIrNopShared, "InitializeIrNopShared"}, - {0x00190040, nullptr, "ReleaseReceivedData"}, + {0x00190040, ReleaseReceivedData, "ReleaseReceivedData"}, {0x001A0040, nullptr, "SetOwnMachineId"}, }; @@ -144,13 +515,43 @@ IR_User_Interface::IR_User_Interface() { void InitUser() { using namespace Kernel; - transfer_shared_memory = nullptr; + shared_memory = nullptr; + conn_status_event = Event::Create(ResetType::OneShot, "IR:ConnectionStatusEvent"); + send_event = Event::Create(ResetType::OneShot, "IR:SendEvent"); + receive_event = Event::Create(ResetType::OneShot, "IR:ReceiveEvent"); + + receive_buffer = boost::none; + + extra_hid = std::make_unique<ExtraHID>(PutToReceive); + + connected_device = nullptr; } void ShutdownUser() { - transfer_shared_memory = nullptr; + if (connected_device) { + connected_device->OnDisconnect(); + connected_device = nullptr; + } + + extra_hid = nullptr; + receive_buffer = boost::none; + shared_memory = nullptr; conn_status_event = nullptr; + send_event = nullptr; + receive_event = nullptr; +} + +void ReloadInputDevicesUser() { + if (extra_hid) + extra_hid->RequestInputDevicesReload(); +} + +IRDevice::IRDevice(SendFunc send_func_) : send_func(send_func_) {} +IRDevice::~IRDevice() = default; + +void IRDevice::Send(const std::vector<u8>& data) { + send_func(data); } } // namespace IR diff --git a/src/core/hle/service/ir/ir_user.h b/src/core/hle/service/ir/ir_user.h index 3849bd923..930650406 100644 --- a/src/core/hle/service/ir/ir_user.h +++ b/src/core/hle/service/ir/ir_user.h @@ -4,11 +4,41 @@ #pragma once +#include <functional> #include "core/hle/service/service.h" namespace Service { namespace IR { +/// An interface representing a device that can communicate with 3DS via ir:USER service +class IRDevice { +public: + /** + * A function object that implements the method to send data to the 3DS, which takes a vector of + * data to send. + */ + using SendFunc = std::function<void(const std::vector<u8>& data)>; + + explicit IRDevice(SendFunc send_func); + virtual ~IRDevice(); + + /// Called when connected with 3DS + virtual void OnConnect() = 0; + + /// Called when disconnected from 3DS + virtual void OnDisconnect() = 0; + + /// Called when data is received from the 3DS. This is invoked by the ir:USER send function. + virtual void OnReceive(const std::vector<u8>& data) = 0; + +protected: + /// Sends data to the 3DS. The actual sending method is specified in the constructor + void Send(const std::vector<u8>& data); + +private: + const SendFunc send_func; +}; + class IR_User_Interface : public Service::Interface { public: IR_User_Interface(); @@ -21,5 +51,8 @@ public: void InitUser(); void ShutdownUser(); +/// Reload input devices. Used when input configuration changed +void ReloadInputDevicesUser(); + } // namespace IR } // namespace Service diff --git a/src/core/hle/service/ldr_ro/ldr_ro.cpp b/src/core/hle/service/ldr_ro/ldr_ro.cpp index 7af76676b..d1e6d869f 100644 --- a/src/core/hle/service/ldr_ro/ldr_ro.cpp +++ b/src/core/hle/service/ldr_ro/ldr_ro.cpp @@ -40,9 +40,6 @@ static const ResultCode ERROR_INVALID_MEMORY_STATE = // 0xD8A12C08 static const ResultCode ERROR_NOT_LOADED = // 0xD8A12C0D ResultCode(static_cast<ErrorDescription>(13), ErrorModule::RO, ErrorSummary::InvalidState, ErrorLevel::Permanent); -static const ResultCode ERROR_INVALID_DESCRIPTOR = // 0xD9001830 - ResultCode(ErrorDescription::OS_InvalidBufferDescriptor, ErrorModule::OS, - ErrorSummary::WrongArgument, ErrorLevel::Permanent); static MemorySynchronizer memory_synchronizer; @@ -71,66 +68,61 @@ static bool VerifyBufferState(VAddr buffer_ptr, u32 size) { * 1 : Result of function, 0 on success, otherwise error code */ static void Initialize(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - VAddr crs_buffer_ptr = cmd_buff[1]; - u32 crs_size = cmd_buff[2]; - VAddr crs_address = cmd_buff[3]; - u32 descriptor = cmd_buff[4]; - u32 process = cmd_buff[5]; - - LOG_DEBUG(Service_LDR, "called, crs_buffer_ptr=0x%08X, crs_address=0x%08X, crs_size=0x%X, " - "descriptor=0x%08X, process=0x%08X", - crs_buffer_ptr, crs_address, crs_size, descriptor, process); - - if (descriptor != 0) { - LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor); - cmd_buff[0] = IPC::MakeHeader(0, 1, 0); - cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw; - return; - } + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x01, 3, 2); + VAddr crs_buffer_ptr = rp.Pop<u32>(); + u32 crs_size = rp.Pop<u32>(); + VAddr crs_address = rp.Pop<u32>(); + // TODO (wwylele): RO service checks the descriptor here and return error 0xD9001830 for + // incorrect descriptor. This error return should be probably built in IPC::RequestParser. + // All other service functions below have the same issue. + Kernel::Handle process = rp.PopHandle(); + + LOG_DEBUG(Service_LDR, + "called, crs_buffer_ptr=0x%08X, crs_address=0x%08X, crs_size=0x%X, process=0x%08X", + crs_buffer_ptr, crs_address, crs_size, process); - cmd_buff[0] = IPC::MakeHeader(1, 1, 0); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (loaded_crs != 0) { LOG_ERROR(Service_LDR, "Already initialized"); - cmd_buff[1] = ERROR_ALREADY_INITIALIZED.raw; + rb.Push(ERROR_ALREADY_INITIALIZED); return; } if (crs_size < CRO_HEADER_SIZE) { LOG_ERROR(Service_LDR, "CRS is too small"); - cmd_buff[1] = ERROR_BUFFER_TOO_SMALL.raw; + rb.Push(ERROR_BUFFER_TOO_SMALL); return; } if (crs_buffer_ptr & Memory::PAGE_MASK) { LOG_ERROR(Service_LDR, "CRS original address is not aligned"); - cmd_buff[1] = ERROR_MISALIGNED_ADDRESS.raw; + rb.Push(ERROR_MISALIGNED_ADDRESS); return; } if (crs_address & Memory::PAGE_MASK) { LOG_ERROR(Service_LDR, "CRS mapping address is not aligned"); - cmd_buff[1] = ERROR_MISALIGNED_ADDRESS.raw; + rb.Push(ERROR_MISALIGNED_ADDRESS); return; } if (crs_size & Memory::PAGE_MASK) { LOG_ERROR(Service_LDR, "CRS size is not aligned"); - cmd_buff[1] = ERROR_MISALIGNED_SIZE.raw; + rb.Push(ERROR_MISALIGNED_SIZE); return; } if (!VerifyBufferState(crs_buffer_ptr, crs_size)) { LOG_ERROR(Service_LDR, "CRS original buffer is in invalid state"); - cmd_buff[1] = ERROR_INVALID_MEMORY_STATE.raw; + rb.Push(ERROR_INVALID_MEMORY_STATE); return; } if (crs_address < Memory::PROCESS_IMAGE_VADDR || crs_address + crs_size > Memory::PROCESS_IMAGE_VADDR_END) { LOG_ERROR(Service_LDR, "CRS mapping address is not in the process image region"); - cmd_buff[1] = ERROR_ILLEGAL_ADDRESS.raw; + rb.Push(ERROR_ILLEGAL_ADDRESS); return; } @@ -145,7 +137,7 @@ static void Initialize(Interface* self) { .Code(); if (result.IsError()) { LOG_ERROR(Service_LDR, "Error mapping memory block %08X", result.raw); - cmd_buff[1] = result.raw; + rb.Push(result); return; } @@ -153,7 +145,7 @@ static void Initialize(Interface* self) { Kernel::VMAPermission::Read); if (result.IsError()) { LOG_ERROR(Service_LDR, "Error reprotecting memory block %08X", result.raw); - cmd_buff[1] = result.raw; + rb.Push(result); return; } @@ -172,7 +164,7 @@ static void Initialize(Interface* self) { result = crs.Rebase(0, crs_size, 0, 0, 0, 0, true); if (result.IsError()) { LOG_ERROR(Service_LDR, "Error rebasing CRS 0x%08X", result.raw); - cmd_buff[1] = result.raw; + rb.Push(result); return; } @@ -180,7 +172,7 @@ static void Initialize(Interface* self) { loaded_crs = crs_address; - cmd_buff[1] = RESULT_SUCCESS.raw; + rb.Push(RESULT_SUCCESS); } /** @@ -196,25 +188,17 @@ static void Initialize(Interface* self) { * 1 : Result of function, 0 on success, otherwise error code */ static void LoadCRR(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 crr_buffer_ptr = cmd_buff[1]; - u32 crr_size = cmd_buff[2]; - u32 descriptor = cmd_buff[3]; - u32 process = cmd_buff[4]; - - if (descriptor != 0) { - LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor); - cmd_buff[0] = IPC::MakeHeader(0, 1, 0); - cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw; - return; - } + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x02, 2, 2); + VAddr crr_buffer_ptr = rp.Pop<u32>(); + u32 crr_size = rp.Pop<u32>(); + Kernel::Handle process = rp.PopHandle(); - cmd_buff[0] = IPC::MakeHeader(2, 1, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; // No error + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_LDR, "(STUBBED) called, crr_buffer_ptr=0x%08X, crr_size=0x%08X, " - "descriptor=0x%08X, process=0x%08X", - crr_buffer_ptr, crr_size, descriptor, process); + LOG_WARNING(Service_LDR, + "(STUBBED) called, crr_buffer_ptr=0x%08X, crr_size=0x%08X, process=0x%08X", + crr_buffer_ptr, crr_size, process); } /** @@ -229,24 +213,15 @@ static void LoadCRR(Interface* self) { * 1 : Result of function, 0 on success, otherwise error code */ static void UnloadCRR(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 crr_buffer_ptr = cmd_buff[1]; - u32 descriptor = cmd_buff[2]; - u32 process = cmd_buff[3]; - - if (descriptor != 0) { - LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor); - cmd_buff[0] = IPC::MakeHeader(0, 1, 0); - cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw; - return; - } + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x03, 1, 2); + u32 crr_buffer_ptr = rp.Pop<u32>(); + Kernel::Handle process = rp.PopHandle(); - cmd_buff[0] = IPC::MakeHeader(3, 1, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; // No error + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); - LOG_WARNING(Service_LDR, - "(STUBBED) called, crr_buffer_ptr=0x%08X, descriptor=0x%08X, process=0x%08X", - crr_buffer_ptr, descriptor, process); + LOG_WARNING(Service_LDR, "(STUBBED) called, crr_buffer_ptr=0x%08X, process=0x%08X", + crr_buffer_ptr, process); } /** @@ -276,87 +251,85 @@ static void UnloadCRR(Interface* self) { * There is a dispatcher template below. */ static void LoadCRO(Interface* self, bool link_on_load_bug_fix) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - VAddr cro_buffer_ptr = cmd_buff[1]; - VAddr cro_address = cmd_buff[2]; - u32 cro_size = cmd_buff[3]; - VAddr data_segment_address = cmd_buff[4]; - u32 zero = cmd_buff[5]; - u32 data_segment_size = cmd_buff[6]; - u32 bss_segment_address = cmd_buff[7]; - u32 bss_segment_size = cmd_buff[8]; - bool auto_link = (cmd_buff[9] & 0xFF) != 0; - u32 fix_level = cmd_buff[10]; - VAddr crr_address = cmd_buff[11]; - u32 descriptor = cmd_buff[12]; - u32 process = cmd_buff[13]; - - LOG_DEBUG(Service_LDR, - "called (%s), cro_buffer_ptr=0x%08X, cro_address=0x%08X, cro_size=0x%X, " - "data_segment_address=0x%08X, zero=%d, data_segment_size=0x%X, " - "bss_segment_address=0x%08X, bss_segment_size=0x%X, " - "auto_link=%s, fix_level=%d, crr_address=0x%08X, descriptor=0x%08X, process=0x%08X", + IPC::RequestParser rp(Kernel::GetCommandBuffer(), link_on_load_bug_fix ? 0x09 : 0x04, 11, 2); + VAddr cro_buffer_ptr = rp.Pop<u32>(); + VAddr cro_address = rp.Pop<u32>(); + u32 cro_size = rp.Pop<u32>(); + VAddr data_segment_address = rp.Pop<u32>(); + u32 zero = rp.Pop<u32>(); + u32 data_segment_size = rp.Pop<u32>(); + u32 bss_segment_address = rp.Pop<u32>(); + u32 bss_segment_size = rp.Pop<u32>(); + bool auto_link = rp.Pop<bool>(); + u32 fix_level = rp.Pop<u32>(); + VAddr crr_address = rp.Pop<u32>(); + Kernel::Handle process = rp.PopHandle(); + + LOG_DEBUG(Service_LDR, "called (%s), cro_buffer_ptr=0x%08X, cro_address=0x%08X, cro_size=0x%X, " + "data_segment_address=0x%08X, zero=%d, data_segment_size=0x%X, " + "bss_segment_address=0x%08X, bss_segment_size=0x%X, auto_link=%s, " + "fix_level=%d, crr_address=0x%08X, process=0x%08X", link_on_load_bug_fix ? "new" : "old", cro_buffer_ptr, cro_address, cro_size, data_segment_address, zero, data_segment_size, bss_segment_address, bss_segment_size, - auto_link ? "true" : "false", fix_level, crr_address, descriptor, process); - - if (descriptor != 0) { - LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor); - cmd_buff[0] = IPC::MakeHeader(0, 1, 0); - cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw; - return; - } + auto_link ? "true" : "false", fix_level, crr_address, process); - cmd_buff[0] = IPC::MakeHeader(link_on_load_bug_fix ? 9 : 4, 2, 0); + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); if (loaded_crs == 0) { LOG_ERROR(Service_LDR, "Not initialized"); - cmd_buff[1] = ERROR_NOT_INITIALIZED.raw; + rb.Push(ERROR_NOT_INITIALIZED); + rb.Push<u32>(0); return; } if (cro_size < CRO_HEADER_SIZE) { LOG_ERROR(Service_LDR, "CRO too small"); - cmd_buff[1] = ERROR_BUFFER_TOO_SMALL.raw; + rb.Push(ERROR_BUFFER_TOO_SMALL); + rb.Push<u32>(0); return; } if (cro_buffer_ptr & Memory::PAGE_MASK) { LOG_ERROR(Service_LDR, "CRO original address is not aligned"); - cmd_buff[1] = ERROR_MISALIGNED_ADDRESS.raw; + rb.Push(ERROR_MISALIGNED_ADDRESS); + rb.Push<u32>(0); return; } if (cro_address & Memory::PAGE_MASK) { LOG_ERROR(Service_LDR, "CRO mapping address is not aligned"); - cmd_buff[1] = ERROR_MISALIGNED_ADDRESS.raw; + rb.Push(ERROR_MISALIGNED_ADDRESS); + rb.Push<u32>(0); return; } if (cro_size & Memory::PAGE_MASK) { LOG_ERROR(Service_LDR, "CRO size is not aligned"); - cmd_buff[1] = ERROR_MISALIGNED_SIZE.raw; + rb.Push(ERROR_MISALIGNED_SIZE); + rb.Push<u32>(0); return; } if (!VerifyBufferState(cro_buffer_ptr, cro_size)) { LOG_ERROR(Service_LDR, "CRO original buffer is in invalid state"); - cmd_buff[1] = ERROR_INVALID_MEMORY_STATE.raw; + rb.Push(ERROR_INVALID_MEMORY_STATE); + rb.Push<u32>(0); return; } if (cro_address < Memory::PROCESS_IMAGE_VADDR || cro_address + cro_size > Memory::PROCESS_IMAGE_VADDR_END) { LOG_ERROR(Service_LDR, "CRO mapping address is not in the process image region"); - cmd_buff[1] = ERROR_ILLEGAL_ADDRESS.raw; + rb.Push(ERROR_ILLEGAL_ADDRESS); + rb.Push<u32>(0); return; } if (zero) { LOG_ERROR(Service_LDR, "Zero is not zero %d", zero); - cmd_buff[1] = ResultCode(static_cast<ErrorDescription>(29), ErrorModule::RO, - ErrorSummary::Internal, ErrorLevel::Usage) - .raw; + rb.Push(ResultCode(static_cast<ErrorDescription>(29), ErrorModule::RO, + ErrorSummary::Internal, ErrorLevel::Usage)); + rb.Push<u32>(0); return; } @@ -371,7 +344,8 @@ static void LoadCRO(Interface* self, bool link_on_load_bug_fix) { .Code(); if (result.IsError()) { LOG_ERROR(Service_LDR, "Error mapping memory block %08X", result.raw); - cmd_buff[1] = result.raw; + rb.Push(result); + rb.Push<u32>(0); return; } @@ -380,7 +354,8 @@ static void LoadCRO(Interface* self, bool link_on_load_bug_fix) { if (result.IsError()) { LOG_ERROR(Service_LDR, "Error reprotecting memory block %08X", result.raw); Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size); - cmd_buff[1] = result.raw; + rb.Push(result); + rb.Push<u32>(0); return; } @@ -400,7 +375,8 @@ static void LoadCRO(Interface* self, bool link_on_load_bug_fix) { if (result.IsError()) { LOG_ERROR(Service_LDR, "Error verifying CRO in CRR %08X", result.raw); Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size); - cmd_buff[1] = result.raw; + rb.Push(result); + rb.Push<u32>(0); return; } @@ -409,7 +385,8 @@ static void LoadCRO(Interface* self, bool link_on_load_bug_fix) { if (result.IsError()) { LOG_ERROR(Service_LDR, "Error rebasing CRO %08X", result.raw); Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size); - cmd_buff[1] = result.raw; + rb.Push(result); + rb.Push<u32>(0); return; } @@ -417,7 +394,8 @@ static void LoadCRO(Interface* self, bool link_on_load_bug_fix) { if (result.IsError()) { LOG_ERROR(Service_LDR, "Error linking CRO %08X", result.raw); Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size); - cmd_buff[1] = result.raw; + rb.Push(result); + rb.Push<u32>(0); return; } @@ -435,7 +413,8 @@ static void LoadCRO(Interface* self, bool link_on_load_bug_fix) { if (result.IsError()) { LOG_ERROR(Service_LDR, "Error unmapping memory block %08X", result.raw); Kernel::g_current_process->vm_manager.UnmapRange(cro_address, cro_size); - cmd_buff[1] = result.raw; + rb.Push(result); + rb.Push<u32>(0); return; } } @@ -453,7 +432,8 @@ static void LoadCRO(Interface* self, bool link_on_load_bug_fix) { if (result.IsError()) { LOG_ERROR(Service_LDR, "Error reprotecting memory block %08X", result.raw); Kernel::g_current_process->vm_manager.UnmapRange(cro_address, fix_size); - cmd_buff[1] = result.raw; + rb.Push(result); + rb.Push<u32>(0); return; } } @@ -463,8 +443,7 @@ static void LoadCRO(Interface* self, bool link_on_load_bug_fix) { LOG_INFO(Service_LDR, "CRO \"%s\" loaded at 0x%08X, fixed_end=0x%08X", cro.ModuleName().data(), cro_address, cro_address + fix_size); - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = fix_size; + rb.Push(RESULT_SUCCESS, fix_size); } template <bool link_on_load_bug_fix> @@ -486,43 +465,35 @@ static void LoadCRO(Interface* self) { * 1 : Result of function, 0 on success, otherwise error code */ static void UnloadCRO(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - VAddr cro_address = cmd_buff[1]; - u32 zero = cmd_buff[2]; - VAddr cro_buffer_ptr = cmd_buff[3]; - u32 descriptor = cmd_buff[4]; - u32 process = cmd_buff[5]; - - LOG_DEBUG(Service_LDR, "called, cro_address=0x%08X, zero=%d, cro_buffer_ptr=0x%08X, " - "descriptor=0x%08X, process=0x%08X", - cro_address, zero, cro_buffer_ptr, descriptor, process); - - if (descriptor != 0) { - LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor); - cmd_buff[0] = IPC::MakeHeader(0, 1, 0); - cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw; - return; - } + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x05, 3, 2); + VAddr cro_address = rp.Pop<u32>(); + u32 zero = rp.Pop<u32>(); + VAddr cro_buffer_ptr = rp.Pop<u32>(); + Kernel::Handle process = rp.PopHandle(); + + LOG_DEBUG(Service_LDR, + "called, cro_address=0x%08X, zero=%d, cro_buffer_ptr=0x%08X, process=0x%08X", + cro_address, zero, cro_buffer_ptr, process); CROHelper cro(cro_address); - cmd_buff[0] = IPC::MakeHeader(5, 1, 0); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (loaded_crs == 0) { LOG_ERROR(Service_LDR, "Not initialized"); - cmd_buff[1] = ERROR_NOT_INITIALIZED.raw; + rb.Push(ERROR_NOT_INITIALIZED); return; } if (cro_address & Memory::PAGE_MASK) { LOG_ERROR(Service_LDR, "CRO address is not aligned"); - cmd_buff[1] = ERROR_MISALIGNED_ADDRESS.raw; + rb.Push(ERROR_MISALIGNED_ADDRESS); return; } if (!cro.IsLoaded()) { LOG_ERROR(Service_LDR, "Invalid or not loaded CRO"); - cmd_buff[1] = ERROR_NOT_LOADED.raw; + rb.Push(ERROR_NOT_LOADED); return; } @@ -535,7 +506,7 @@ static void UnloadCRO(Interface* self) { ResultCode result = cro.Unlink(loaded_crs); if (result.IsError()) { LOG_ERROR(Service_LDR, "Error unlinking CRO %08X", result.raw); - cmd_buff[1] = result.raw; + rb.Push(result); return; } @@ -545,7 +516,7 @@ static void UnloadCRO(Interface* self) { result = cro.ClearRelocations(); if (result.IsError()) { LOG_ERROR(Service_LDR, "Error clearing relocations %08X", result.raw); - cmd_buff[1] = result.raw; + rb.Push(result); return; } } @@ -565,7 +536,7 @@ static void UnloadCRO(Interface* self) { Core::CPU().ClearInstructionCache(); - cmd_buff[1] = result.raw; + rb.Push(result); } /** @@ -580,40 +551,31 @@ static void UnloadCRO(Interface* self) { * 1 : Result of function, 0 on success, otherwise error code */ static void LinkCRO(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - VAddr cro_address = cmd_buff[1]; - u32 descriptor = cmd_buff[2]; - u32 process = cmd_buff[3]; - - LOG_DEBUG(Service_LDR, "called, cro_address=0x%08X, descriptor=0x%08X, process=0x%08X", - cro_address, descriptor, process); - - if (descriptor != 0) { - LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor); - cmd_buff[0] = IPC::MakeHeader(0, 1, 0); - cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw; - return; - } + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x06, 1, 2); + VAddr cro_address = rp.Pop<u32>(); + Kernel::Handle process = rp.PopHandle(); + + LOG_DEBUG(Service_LDR, "called, cro_address=0x%08X, process=0x%08X", cro_address, process); CROHelper cro(cro_address); - cmd_buff[0] = IPC::MakeHeader(6, 1, 0); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (loaded_crs == 0) { LOG_ERROR(Service_LDR, "Not initialized"); - cmd_buff[1] = ERROR_NOT_INITIALIZED.raw; + rb.Push(ERROR_NOT_INITIALIZED); return; } if (cro_address & Memory::PAGE_MASK) { LOG_ERROR(Service_LDR, "CRO address is not aligned"); - cmd_buff[1] = ERROR_MISALIGNED_ADDRESS.raw; + rb.Push(ERROR_MISALIGNED_ADDRESS); return; } if (!cro.IsLoaded()) { LOG_ERROR(Service_LDR, "Invalid or not loaded CRO"); - cmd_buff[1] = ERROR_NOT_LOADED.raw; + rb.Push(ERROR_NOT_LOADED); return; } @@ -627,7 +589,7 @@ static void LinkCRO(Interface* self) { memory_synchronizer.SynchronizeOriginalMemory(); Core::CPU().ClearInstructionCache(); - cmd_buff[1] = result.raw; + rb.Push(result); } /** @@ -642,40 +604,31 @@ static void LinkCRO(Interface* self) { * 1 : Result of function, 0 on success, otherwise error code */ static void UnlinkCRO(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - VAddr cro_address = cmd_buff[1]; - u32 descriptor = cmd_buff[2]; - u32 process = cmd_buff[3]; - - LOG_DEBUG(Service_LDR, "called, cro_address=0x%08X, descriptor=0x%08X, process=0x%08X", - cro_address, descriptor, process); - - if (descriptor != 0) { - LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor); - cmd_buff[0] = IPC::MakeHeader(0, 1, 0); - cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw; - return; - } + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x07, 1, 2); + VAddr cro_address = rp.Pop<u32>(); + Kernel::Handle process = rp.PopHandle(); + + LOG_DEBUG(Service_LDR, "called, cro_address=0x%08X, process=0x%08X", cro_address, process); CROHelper cro(cro_address); - cmd_buff[0] = IPC::MakeHeader(7, 1, 0); + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (loaded_crs == 0) { LOG_ERROR(Service_LDR, "Not initialized"); - cmd_buff[1] = ERROR_NOT_INITIALIZED.raw; + rb.Push(ERROR_NOT_INITIALIZED); return; } if (cro_address & Memory::PAGE_MASK) { LOG_ERROR(Service_LDR, "CRO address is not aligned"); - cmd_buff[1] = ERROR_MISALIGNED_ADDRESS.raw; + rb.Push(ERROR_MISALIGNED_ADDRESS); return; } if (!cro.IsLoaded()) { LOG_ERROR(Service_LDR, "Invalid or not loaded CRO"); - cmd_buff[1] = ERROR_NOT_LOADED.raw; + rb.Push(ERROR_NOT_LOADED); return; } @@ -689,7 +642,7 @@ static void UnlinkCRO(Interface* self) { memory_synchronizer.SynchronizeOriginalMemory(); Core::CPU().ClearInstructionCache(); - cmd_buff[1] = result.raw; + rb.Push(result); } /** @@ -704,29 +657,21 @@ static void UnlinkCRO(Interface* self) { * 1 : Result of function, 0 on success, otherwise error code */ static void Shutdown(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - VAddr crs_buffer_ptr = cmd_buff[1]; - u32 descriptor = cmd_buff[2]; - u32 process = cmd_buff[3]; - - LOG_DEBUG(Service_LDR, "called, crs_buffer_ptr=0x%08X, descriptor=0x%08X, process=0x%08X", - crs_buffer_ptr, descriptor, process); - - if (descriptor != 0) { - LOG_ERROR(Service_LDR, "IPC handle descriptor failed validation (0x%X)", descriptor); - cmd_buff[0] = IPC::MakeHeader(0, 1, 0); - cmd_buff[1] = ERROR_INVALID_DESCRIPTOR.raw; - return; - } + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x08, 1, 2); + VAddr crs_buffer_ptr = rp.Pop<u32>(); + Kernel::Handle process = rp.PopHandle(); + + LOG_DEBUG(Service_LDR, "called, crs_buffer_ptr=0x%08X, process=0x%08X", crs_buffer_ptr, + process); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); if (loaded_crs == 0) { LOG_ERROR(Service_LDR, "Not initialized"); - cmd_buff[1] = ERROR_NOT_INITIALIZED.raw; + rb.Push(ERROR_NOT_INITIALIZED); return; } - cmd_buff[0] = IPC::MakeHeader(8, 1, 0); - CROHelper crs(loaded_crs); crs.Unrebase(true); @@ -744,7 +689,7 @@ static void Shutdown(Interface* self) { } loaded_crs = 0; - cmd_buff[1] = result.raw; + rb.Push(result); } const Interface::FunctionInfo FunctionTable[] = { diff --git a/src/core/hle/service/nwm/nwm_uds.cpp b/src/core/hle/service/nwm/nwm_uds.cpp index ef6c5ebe3..581816e81 100644 --- a/src/core/hle/service/nwm/nwm_uds.cpp +++ b/src/core/hle/service/nwm/nwm_uds.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <array> #include <cstring> #include <unordered_map> #include <vector> @@ -12,6 +13,7 @@ #include "core/hle/kernel/shared_memory.h" #include "core/hle/result.h" #include "core/hle/service/nwm/nwm_uds.h" +#include "core/hle/service/nwm/uds_beacon.h" #include "core/memory.h" namespace Service { @@ -27,10 +29,12 @@ static Kernel::SharedPtr<Kernel::SharedMemory> recv_buffer_memory; // Connection status of this 3DS. static ConnectionStatus connection_status{}; -// Node information about the current 3DS. -// TODO(Subv): Keep an array of all nodes connected to the network, -// that data has to be retransmitted in every beacon frame. -static NodeInfo node_info; +/* Node information about the current network. + * The amount of elements in this vector is always the maximum number + * of nodes specified in the network configuration. + * The first node is always the host, so this always contains at least 1 entry. + */ +static NodeList node_info(1); // Mapping of bind node ids to their respective events. static std::unordered_map<u32, Kernel::SharedPtr<Kernel::Event>> bind_node_events; @@ -82,29 +86,70 @@ static void Shutdown(Interface* self) { * 1 : Result of function, 0 on success, otherwise error code */ static void RecvBeaconBroadcastData(Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); - u32 out_buffer_size = cmd_buff[1]; - u32 unk1 = cmd_buff[2]; - u32 unk2 = cmd_buff[3]; - u32 mac_address = cmd_buff[4]; + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x0F, 16, 4); - u32 unk3 = cmd_buff[6]; + u32 out_buffer_size = rp.Pop<u32>(); + u32 unk1 = rp.Pop<u32>(); + u32 unk2 = rp.Pop<u32>(); - u32 wlan_comm_id = cmd_buff[15]; - u32 ctr_gen_id = cmd_buff[16]; - u32 value = cmd_buff[17]; - u32 input_handle = cmd_buff[18]; - u32 new_buffer_size = cmd_buff[19]; - u32 out_buffer_ptr = cmd_buff[20]; + MacAddress mac_address; + rp.PopRaw(mac_address); - cmd_buff[1] = RESULT_SUCCESS.raw; + rp.Skip(9, false); - LOG_WARNING(Service_NWM, - "(STUBBED) called out_buffer_size=0x%08X, unk1=0x%08X, unk2=0x%08X," - "mac_address=0x%08X, unk3=0x%08X, wlan_comm_id=0x%08X, ctr_gen_id=0x%08X," - "value=%u, input_handle=0x%08X, new_buffer_size=0x%08X, out_buffer_ptr=0x%08X", - out_buffer_size, unk1, unk2, mac_address, unk3, wlan_comm_id, ctr_gen_id, value, - input_handle, new_buffer_size, out_buffer_ptr); + u32 wlan_comm_id = rp.Pop<u32>(); + u32 id = rp.Pop<u32>(); + Kernel::Handle input_handle = rp.PopHandle(); + + size_t desc_size; + const VAddr out_buffer_ptr = rp.PopMappedBuffer(&desc_size); + ASSERT(desc_size == out_buffer_size); + + VAddr current_buffer_pos = out_buffer_ptr; + u32 total_size = sizeof(BeaconDataReplyHeader); + + // Retrieve all beacon frames that were received from the desired mac address. + std::deque<WifiPacket> beacons = + GetReceivedPackets(WifiPacket::PacketType::Beacon, mac_address); + + BeaconDataReplyHeader data_reply_header{}; + data_reply_header.total_entries = beacons.size(); + data_reply_header.max_output_size = out_buffer_size; + + Memory::WriteBlock(current_buffer_pos, &data_reply_header, sizeof(BeaconDataReplyHeader)); + current_buffer_pos += sizeof(BeaconDataReplyHeader); + + // Write each of the received beacons into the buffer + for (const auto& beacon : beacons) { + BeaconEntryHeader entry{}; + // TODO(Subv): Figure out what this size is used for. + entry.unk_size = sizeof(BeaconEntryHeader) + beacon.data.size(); + entry.total_size = sizeof(BeaconEntryHeader) + beacon.data.size(); + entry.wifi_channel = beacon.channel; + entry.header_size = sizeof(BeaconEntryHeader); + entry.mac_address = beacon.transmitter_address; + + ASSERT(current_buffer_pos < out_buffer_ptr + out_buffer_size); + + Memory::WriteBlock(current_buffer_pos, &entry, sizeof(BeaconEntryHeader)); + current_buffer_pos += sizeof(BeaconEntryHeader); + + Memory::WriteBlock(current_buffer_pos, beacon.data.data(), beacon.data.size()); + current_buffer_pos += beacon.data.size(); + + total_size += sizeof(BeaconEntryHeader) + beacon.data.size(); + } + + // Update the total size in the structure and write it to the buffer again. + data_reply_header.total_size = total_size; + Memory::WriteBlock(out_buffer_ptr, &data_reply_header, sizeof(BeaconDataReplyHeader)); + + IPC::RequestBuilder rb = rp.MakeBuilder(1, 0); + rb.Push(RESULT_SUCCESS); + + LOG_DEBUG(Service_NWM, "called out_buffer_size=0x%08X, wlan_comm_id=0x%08X, id=0x%08X," + "input_handle=0x%08X, out_buffer_ptr=0x%08X, unk1=0x%08X, unk2=0x%08X", + out_buffer_size, wlan_comm_id, id, input_handle, out_buffer_ptr, unk1, unk2); } /** @@ -127,10 +172,10 @@ static void InitializeWithVersion(Interface* self) { u32 sharedmem_size = rp.Pop<u32>(); // Update the node information with the data the game gave us. - rp.PopRaw(node_info); + rp.PopRaw(node_info[0]); + + u16 version = rp.Pop<u16>(); - u16 version; - rp.PopRaw(version); Kernel::Handle sharedmem_handle = rp.PopHandle(); recv_buffer_memory = Kernel::g_handle_table.Get<Kernel::SharedMemory>(sharedmem_handle); @@ -191,10 +236,8 @@ static void Bind(Interface* self) { u32 bind_node_id = rp.Pop<u32>(); u32 recv_buffer_size = rp.Pop<u32>(); - u8 data_channel; - rp.PopRaw(data_channel); - u16 network_node_id; - rp.PopRaw(network_node_id); + u8 data_channel = rp.Pop<u8>(); + u16 network_node_id = rp.Pop<u16>(); // TODO(Subv): Store the data channel and verify it when receiving data frames. @@ -251,13 +294,25 @@ static void BeginHostingNetwork(Interface* self) { ASSERT_MSG(network_info.max_nodes > 1, "Trying to host a network of only one member."); connection_status.status = static_cast<u32>(NetworkStatus::ConnectedAsHost); + + // Ensure the application data size is less than the maximum value. + ASSERT_MSG(network_info.application_data_size <= ApplicationDataSize, "Data size is too big."); + + // Set up basic information for this network. + network_info.oui_value = NintendoOUI; + network_info.oui_type = static_cast<u8>(NintendoTagId::NetworkInfo); + connection_status.max_nodes = network_info.max_nodes; + // Resize the nodes list to hold max_nodes. + node_info.resize(network_info.max_nodes); + // There's currently only one node in the network (the host). connection_status.total_nodes = 1; + network_info.total_nodes = 1; // The host is always the first node connection_status.network_node_id = 1; - node_info.network_node_id = 1; + node_info[0].network_node_id = 1; // Set the bit 0 in the nodes bitmask to indicate that node 1 is already taken. connection_status.node_bitmask |= 1; @@ -325,7 +380,7 @@ static void GetChannel(Interface* self) { u8 channel = is_connected ? network_channel : 0; rb.Push(RESULT_SUCCESS); - rb.PushRaw(channel); + rb.Push(channel); LOG_DEBUG(Service_NWM, "called"); } @@ -373,7 +428,8 @@ static void BeaconBroadcastCallback(u64 userdata, int cycles_late) { if (connection_status.status != static_cast<u32>(NetworkStatus::ConnectedAsHost)) return; - // TODO(Subv): Actually generate the beacon and send it. + // TODO(Subv): Actually send the beacon. + std::vector<u8> frame = GenerateBeaconFrame(network_info, node_info); // Start broadcasting the network, send a beacon frame every 102.4ms. CoreTiming::ScheduleEvent(msToCycles(DefaultBeaconInterval * MillisecondsPerTU) - cycles_late, diff --git a/src/core/hle/service/nwm/nwm_uds.h b/src/core/hle/service/nwm/nwm_uds.h index 65349f9fd..29b146569 100644 --- a/src/core/hle/service/nwm/nwm_uds.h +++ b/src/core/hle/service/nwm/nwm_uds.h @@ -6,6 +6,7 @@ #include <array> #include <cstddef> +#include <vector> #include "common/common_types.h" #include "common/swap.h" #include "core/hle/service/service.h" @@ -33,6 +34,8 @@ struct NodeInfo { static_assert(sizeof(NodeInfo) == 40, "NodeInfo has incorrect size."); +using NodeList = std::vector<NodeInfo>; + enum class NetworkStatus { NotConnected = 3, ConnectedAsHost = 6, @@ -75,6 +78,8 @@ struct NetworkInfo { std::array<u8, ApplicationDataSize> application_data; }; +static_assert(offsetof(NetworkInfo, oui_value) == 0xC, "oui_value is at the wrong offset."); +static_assert(offsetof(NetworkInfo, wlan_comm_id) == 0x10, "wlancommid is at the wrong offset."); static_assert(sizeof(NetworkInfo) == 0x108, "NetworkInfo has incorrect size."); class NWM_UDS final : public Interface { diff --git a/src/core/hle/service/nwm/uds_beacon.cpp b/src/core/hle/service/nwm/uds_beacon.cpp new file mode 100644 index 000000000..c6e5bc5f1 --- /dev/null +++ b/src/core/hle/service/nwm/uds_beacon.cpp @@ -0,0 +1,333 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <cstring> + +#include "core/hle/service/nwm/nwm_uds.h" +#include "core/hle/service/nwm/uds_beacon.h" + +#include <cryptopp/aes.h> +#include <cryptopp/md5.h> +#include <cryptopp/modes.h> +#include <cryptopp/sha.h> + +namespace Service { +namespace NWM { + +// 802.11 broadcast MAC address +constexpr MacAddress BroadcastMac = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + +constexpr u64 DefaultNetworkUptime = 900000000; // 15 minutes in microseconds. + +// Note: These values were taken from a packet capture of an o3DS XL +// broadcasting a Super Smash Bros. 4 lobby. +constexpr u16 DefaultExtraCapabilities = 0x0431; + +// Size of the SSID broadcast by an UDS beacon frame. +constexpr u8 UDSBeaconSSIDSize = 8; + +// The maximum size of the data stored in the EncryptedData0 tag (24). +constexpr u32 EncryptedDataSizeCutoff = 0xFA; + +/** + * NWM Beacon data encryption key, taken from the NWM module code. + * We stub this with an all-zeros key as that is enough for Citra's purpose. + * The real key can be used here to generate beacons that will be accepted by + * a real 3ds. + */ +constexpr std::array<u8, CryptoPP::AES::BLOCKSIZE> nwm_beacon_key = {}; + +/** + * Generates a buffer with the fixed parameters of an 802.11 Beacon frame + * using dummy values. + * @returns A buffer with the fixed parameters of the beacon frame. + */ +std::vector<u8> GenerateFixedParameters() { + std::vector<u8> buffer(sizeof(BeaconFrameHeader)); + + BeaconFrameHeader header{}; + // Use a fixed default time for now. + // TODO(Subv): Perhaps use the difference between now and the time the network was started? + header.timestamp = DefaultNetworkUptime; + header.beacon_interval = DefaultBeaconInterval; + header.capabilities = DefaultExtraCapabilities; + + std::memcpy(buffer.data(), &header, sizeof(header)); + + return buffer; +} + +/** + * Generates an SSID tag of an 802.11 Beacon frame with an 8-byte all-zero SSID value. + * @returns A buffer with the SSID tag. + */ +std::vector<u8> GenerateSSIDTag() { + std::vector<u8> buffer(sizeof(TagHeader) + UDSBeaconSSIDSize); + + TagHeader tag_header{}; + tag_header.tag_id = static_cast<u8>(TagId::SSID); + tag_header.length = UDSBeaconSSIDSize; + + std::memcpy(buffer.data(), &tag_header, sizeof(TagHeader)); + + // The rest of the buffer is already filled with zeros. + + return buffer; +} + +/** + * Generates a buffer with the basic tagged parameters of an 802.11 Beacon frame + * such as SSID, Rate Information, Country Information, etc. + * @returns A buffer with the tagged parameters of the beacon frame. + */ +std::vector<u8> GenerateBasicTaggedParameters() { + // Append the SSID tag + std::vector<u8> buffer = GenerateSSIDTag(); + + // TODO(Subv): Add the SupportedRates tag. + // TODO(Subv): Add the DSParameterSet tag. + // TODO(Subv): Add the TrafficIndicationMap tag. + // TODO(Subv): Add the CountryInformation tag. + // TODO(Subv): Add the ERPInformation tag. + + return buffer; +} + +/** + * Generates a buffer with the Dummy Nintendo tag. + * It is currently unknown what this tag does. + * TODO(Subv): Figure out if this is needed and what it does. + * @returns A buffer with the Nintendo tagged parameters of the beacon frame. + */ +std::vector<u8> GenerateNintendoDummyTag() { + // Note: These values were taken from a packet capture of an o3DS XL + // broadcasting a Super Smash Bros. 4 lobby. + constexpr std::array<u8, 3> dummy_data = {0x0A, 0x00, 0x00}; + + DummyTag tag{}; + tag.header.tag_id = static_cast<u8>(TagId::VendorSpecific); + tag.header.length = sizeof(DummyTag) - sizeof(TagHeader); + tag.oui_type = static_cast<u8>(NintendoTagId::Dummy); + tag.oui = NintendoOUI; + tag.data = dummy_data; + + std::vector<u8> buffer(sizeof(DummyTag)); + std::memcpy(buffer.data(), &tag, sizeof(DummyTag)); + + return buffer; +} + +/** + * Generates a buffer with the Network Info Nintendo tag. + * This tag contains the network information of the network that is being broadcast. + * It also contains the application data provided by the application that opened the network. + * @returns A buffer with the Nintendo network info parameter of the beacon frame. + */ +std::vector<u8> GenerateNintendoNetworkInfoTag(const NetworkInfo& network_info) { + NetworkInfoTag tag{}; + tag.header.tag_id = static_cast<u8>(TagId::VendorSpecific); + tag.header.length = + sizeof(NetworkInfoTag) - sizeof(TagHeader) + network_info.application_data_size; + tag.appdata_size = network_info.application_data_size; + // Set the hash to zero initially, it will be updated once we calculate it. + tag.sha_hash = {}; + + // Ensure the network structure has the correct OUI and OUI type. + ASSERT(network_info.oui_type == static_cast<u8>(NintendoTagId::NetworkInfo)); + ASSERT(network_info.oui_value == NintendoOUI); + + // Ensure the application data size is less than the maximum value. + ASSERT_MSG(network_info.application_data_size <= ApplicationDataSize, "Data size is too big."); + + // This tag contains the network info structure starting at the OUI. + std::memcpy(tag.network_info.data(), &network_info.oui_value, tag.network_info.size()); + + // Copy the tag and the data so we can calculate the SHA1 over it. + std::vector<u8> buffer(sizeof(tag) + network_info.application_data_size); + std::memcpy(buffer.data(), &tag, sizeof(tag)); + std::memcpy(buffer.data() + sizeof(tag), network_info.application_data.data(), + network_info.application_data_size); + + // Calculate the SHA1 of the contents of the tag. + std::array<u8, CryptoPP::SHA1::DIGESTSIZE> hash; + CryptoPP::SHA1().CalculateDigest(hash.data(), + buffer.data() + offsetof(NetworkInfoTag, network_info), + buffer.size() - sizeof(TagHeader)); + + // Copy it directly into the buffer, overwriting the zeros that we had previously placed there. + std::memcpy(buffer.data() + offsetof(NetworkInfoTag, sha_hash), hash.data(), hash.size()); + + return buffer; +} + +/* + * Calculates the CTR used for the AES-CTR encryption of the data stored in the + * EncryptedDataTags. + * @returns The CTR used for beacon crypto. + */ +std::array<u8, CryptoPP::AES::BLOCKSIZE> GetBeaconCryptoCTR(const NetworkInfo& network_info) { + BeaconDataCryptoCTR data{}; + + data.host_mac = network_info.host_mac_address; + data.wlan_comm_id = network_info.wlan_comm_id; + data.id = network_info.id; + data.network_id = network_info.network_id; + + std::array<u8, CryptoPP::AES::BLOCKSIZE> hash; + std::memcpy(hash.data(), &data, sizeof(data)); + + return hash; +} + +/* + * Serializes the node information into the format needed for network transfer, + * and then encrypts it with the NWM key. + * @returns The serialized and encrypted node information. + */ +std::vector<u8> GeneratedEncryptedData(const NetworkInfo& network_info, const NodeList& nodes) { + std::vector<u8> buffer(sizeof(BeaconData)); + + BeaconData data{}; + std::memcpy(buffer.data(), &data, sizeof(BeaconData)); + + for (const NodeInfo& node : nodes) { + // Serialize each node and convert the data from + // host byte-order to Big Endian. + BeaconNodeInfo info{}; + info.friend_code_seed = node.friend_code_seed; + info.network_node_id = node.network_node_id; + for (int i = 0; i < info.username.size(); ++i) + info.username[i] = node.username[i]; + + buffer.insert(buffer.end(), reinterpret_cast<u8*>(&info), + reinterpret_cast<u8*>(&info) + sizeof(info)); + } + + // Calculate the MD5 hash of the data in the buffer, not including the hash field. + std::array<u8, CryptoPP::MD5::DIGESTSIZE> hash; + CryptoPP::MD5().CalculateDigest(hash.data(), buffer.data() + offsetof(BeaconData, bitmask), + buffer.size() - sizeof(data.md5_hash)); + + // Copy the hash into the buffer. + std::memcpy(buffer.data(), hash.data(), hash.size()); + + // Encrypt the data using AES-CTR and the NWM beacon key. + using CryptoPP::AES; + std::array<u8, AES::BLOCKSIZE> counter = GetBeaconCryptoCTR(network_info); + CryptoPP::CTR_Mode<AES>::Encryption aes; + aes.SetKeyWithIV(nwm_beacon_key.data(), AES::BLOCKSIZE, counter.data()); + aes.ProcessData(buffer.data(), buffer.data(), buffer.size()); + + return buffer; +} + +void DecryptBeaconData(const NetworkInfo& network_info, std::vector<u8>& buffer) { + // Decrypt the data using AES-CTR and the NWM beacon key. + using CryptoPP::AES; + std::array<u8, AES::BLOCKSIZE> counter = GetBeaconCryptoCTR(network_info); + CryptoPP::CTR_Mode<AES>::Decryption aes; + aes.SetKeyWithIV(nwm_beacon_key.data(), AES::BLOCKSIZE, counter.data()); + aes.ProcessData(buffer.data(), buffer.data(), buffer.size()); +} + +/** + * Generates a buffer with the Network Info Nintendo tag. + * This tag contains the first portion of the encrypted payload in the 802.11 beacon frame. + * The encrypted payload contains information about the nodes currently connected to the network. + * @returns A buffer with the first Nintendo encrypted data parameters of the beacon frame. + */ +std::vector<u8> GenerateNintendoFirstEncryptedDataTag(const NetworkInfo& network_info, + const NodeList& nodes) { + const size_t payload_size = + std::min<size_t>(EncryptedDataSizeCutoff, nodes.size() * sizeof(NodeInfo)); + + EncryptedDataTag tag{}; + tag.header.tag_id = static_cast<u8>(TagId::VendorSpecific); + tag.header.length = sizeof(tag) - sizeof(TagHeader) + payload_size; + tag.oui_type = static_cast<u8>(NintendoTagId::EncryptedData0); + tag.oui = NintendoOUI; + + std::vector<u8> buffer(sizeof(tag) + payload_size); + std::memcpy(buffer.data(), &tag, sizeof(tag)); + + std::vector<u8> encrypted_data = GeneratedEncryptedData(network_info, nodes); + std::memcpy(buffer.data() + sizeof(tag), encrypted_data.data(), payload_size); + + return buffer; +} + +/** + * Generates a buffer with the Network Info Nintendo tag. + * This tag contains the second portion of the encrypted payload in the 802.11 beacon frame. + * The encrypted payload contains information about the nodes currently connected to the network. + * This tag is only present if the payload size is greater than EncryptedDataSizeCutoff (0xFA) + * bytes. + * @returns A buffer with the second Nintendo encrypted data parameters of the beacon frame. + */ +std::vector<u8> GenerateNintendoSecondEncryptedDataTag(const NetworkInfo& network_info, + const NodeList& nodes) { + // This tag is only present if the payload is larger than EncryptedDataSizeCutoff (0xFA). + if (nodes.size() * sizeof(NodeInfo) <= EncryptedDataSizeCutoff) + return {}; + + const size_t payload_size = nodes.size() * sizeof(NodeInfo) - EncryptedDataSizeCutoff; + + const size_t tag_length = sizeof(EncryptedDataTag) - sizeof(TagHeader) + payload_size; + + // TODO(Subv): What does the 3DS do when a game has too much data to fit into the tag? + ASSERT_MSG(tag_length <= 255, "Data is too big."); + + EncryptedDataTag tag{}; + tag.header.tag_id = static_cast<u8>(TagId::VendorSpecific); + tag.header.length = tag_length; + tag.oui_type = static_cast<u8>(NintendoTagId::EncryptedData1); + tag.oui = NintendoOUI; + + std::vector<u8> buffer(sizeof(tag) + payload_size); + std::memcpy(buffer.data(), &tag, sizeof(tag)); + + std::vector<u8> encrypted_data = GeneratedEncryptedData(network_info, nodes); + std::memcpy(buffer.data() + sizeof(tag), encrypted_data.data() + EncryptedDataSizeCutoff, + payload_size); + + return buffer; +} + +/** + * Generates a buffer with the Nintendo tagged parameters of an 802.11 Beacon frame + * for UDS communication. + * @returns A buffer with the Nintendo tagged parameters of the beacon frame. + */ +std::vector<u8> GenerateNintendoTaggedParameters(const NetworkInfo& network_info, + const NodeList& nodes) { + ASSERT_MSG(network_info.max_nodes == nodes.size(), "Inconsistent network state."); + + std::vector<u8> buffer = GenerateNintendoDummyTag(); + std::vector<u8> network_info_tag = GenerateNintendoNetworkInfoTag(network_info); + std::vector<u8> first_data_tag = GenerateNintendoFirstEncryptedDataTag(network_info, nodes); + std::vector<u8> second_data_tag = GenerateNintendoSecondEncryptedDataTag(network_info, nodes); + + buffer.insert(buffer.end(), network_info_tag.begin(), network_info_tag.end()); + buffer.insert(buffer.end(), first_data_tag.begin(), first_data_tag.end()); + buffer.insert(buffer.end(), second_data_tag.begin(), second_data_tag.end()); + + return buffer; +} + +std::vector<u8> GenerateBeaconFrame(const NetworkInfo& network_info, const NodeList& nodes) { + std::vector<u8> buffer = GenerateFixedParameters(); + std::vector<u8> basic_tags = GenerateBasicTaggedParameters(); + std::vector<u8> nintendo_tags = GenerateNintendoTaggedParameters(network_info, nodes); + + buffer.insert(buffer.end(), basic_tags.begin(), basic_tags.end()); + buffer.insert(buffer.end(), nintendo_tags.begin(), nintendo_tags.end()); + + return buffer; +} + +std::deque<WifiPacket> GetReceivedPackets(WifiPacket::PacketType type, const MacAddress& sender) { + return {}; +} +} // namespace NWM +} // namespace Service diff --git a/src/core/hle/service/nwm/uds_beacon.h b/src/core/hle/service/nwm/uds_beacon.h new file mode 100644 index 000000000..6df4c4f47 --- /dev/null +++ b/src/core/hle/service/nwm/uds_beacon.h @@ -0,0 +1,173 @@ +// Copyright 2017 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <deque> +#include <vector> +#include "common/common_types.h" +#include "common/swap.h" +#include "core/hle/service/service.h" + +namespace Service { +namespace NWM { + +using MacAddress = std::array<u8, 6>; + +/// The maximum number of nodes that can exist in an UDS session. +constexpr u32 UDSMaxNodes = 16; +constexpr std::array<u8, 3> NintendoOUI = {0x00, 0x1F, 0x32}; + +/// Additional block tag ids in the Beacon frames +enum class TagId : u8 { + SSID = 0, + SupportedRates = 1, + DSParameterSet = 2, + TrafficIndicationMap = 5, + CountryInformation = 7, + ERPInformation = 42, + VendorSpecific = 221 +}; + +/** + * Internal vendor-specific tag ids as stored inside + * VendorSpecific blocks in the Beacon frames. + */ +enum class NintendoTagId : u8 { + Dummy = 20, + NetworkInfo = 21, + EncryptedData0 = 24, + EncryptedData1 = 25, +}; + +struct BeaconEntryHeader { + u32_le total_size; + INSERT_PADDING_BYTES(1); + u8 wifi_channel; + INSERT_PADDING_BYTES(2); + MacAddress mac_address; + INSERT_PADDING_BYTES(6); + u32_le unk_size; + u32_le header_size; +}; + +static_assert(sizeof(BeaconEntryHeader) == 0x1C, "BeaconEntryHeader has incorrect size."); + +struct BeaconDataReplyHeader { + u32_le max_output_size; + u32_le total_size; + u32_le total_entries; +}; + +static_assert(sizeof(BeaconDataReplyHeader) == 12, "BeaconDataReplyHeader has incorrect size."); + +#pragma pack(push, 1) +struct BeaconFrameHeader { + // Number of microseconds the AP has been active. + u64_le timestamp; + // Interval between beacon transmissions, expressed in TU. + u16_le beacon_interval; + // Indicates the presence of optional capabilities. + u16_le capabilities; +}; +#pragma pack(pop) + +static_assert(sizeof(BeaconFrameHeader) == 12, "BeaconFrameHeader has incorrect size."); + +struct TagHeader { + u8 tag_id; + u8 length; +}; + +static_assert(sizeof(TagHeader) == 2, "TagHeader has incorrect size."); + +struct DummyTag { + TagHeader header; + std::array<u8, 3> oui; + u8 oui_type; + std::array<u8, 3> data; +}; + +static_assert(sizeof(DummyTag) == 9, "DummyTag has incorrect size."); + +struct NetworkInfoTag { + TagHeader header; + std::array<u8, 0x1F> network_info; + std::array<u8, 0x14> sha_hash; + u8 appdata_size; +}; + +static_assert(sizeof(NetworkInfoTag) == 54, "NetworkInfoTag has incorrect size."); + +struct EncryptedDataTag { + TagHeader header; + std::array<u8, 3> oui; + u8 oui_type; +}; + +static_assert(sizeof(EncryptedDataTag) == 6, "EncryptedDataTag has incorrect size."); + +#pragma pack(push, 1) +// The raw bytes of this structure are the CTR used in the encryption (AES-CTR) +// of the beacon data stored in the EncryptedDataTags. +struct BeaconDataCryptoCTR { + MacAddress host_mac; + u32_le wlan_comm_id; + u8 id; + INSERT_PADDING_BYTES(1); + u32_le network_id; +}; + +static_assert(sizeof(BeaconDataCryptoCTR) == 0x10, "BeaconDataCryptoCTR has incorrect size."); + +struct BeaconNodeInfo { + u64_be friend_code_seed; + std::array<u16_be, 10> username; + u16_be network_node_id; +}; + +static_assert(sizeof(BeaconNodeInfo) == 0x1E, "BeaconNodeInfo has incorrect size."); + +struct BeaconData { + std::array<u8, 0x10> md5_hash; + u16_be bitmask; +}; +#pragma pack(pop) + +static_assert(sizeof(BeaconData) == 0x12, "BeaconData has incorrect size."); + +/// Information about a received WiFi packet. +/// Acts as our own 802.11 header. +struct WifiPacket { + enum class PacketType { Beacon, Data }; + + PacketType type; ///< The type of 802.11 frame, Beacon / Data. + + /// Raw 802.11 frame data, starting at the management frame header for management frames. + std::vector<u8> data; + MacAddress transmitter_address; ///< Mac address of the transmitter. + MacAddress destination_address; ///< Mac address of the receiver. + u8 channel; ///< WiFi channel where this frame was transmitted. +}; + +/** + * Decrypts the beacon data buffer for the network described by `network_info`. + */ +void DecryptBeaconData(const NetworkInfo& network_info, std::vector<u8>& buffer); + +/** + * Generates an 802.11 beacon frame starting at the management frame header. + * This frame contains information about the network and its connected clients. + * @returns The generated frame. + */ +std::vector<u8> GenerateBeaconFrame(const NetworkInfo& network_info, const NodeList& nodes); + +/** + * Returns a list of received 802.11 frames from the specified sender + * matching the type since the last call. + */ +std::deque<WifiPacket> GetReceivedPackets(WifiPacket::PacketType type, const MacAddress& sender); +} // namespace NWM +} // namespace Service diff --git a/src/core/hle/service/ptm/ptm.cpp b/src/core/hle/service/ptm/ptm.cpp index e373ed47a..319e8c946 100644 --- a/src/core/hle/service/ptm/ptm.cpp +++ b/src/core/hle/service/ptm/ptm.cpp @@ -27,67 +27,72 @@ static bool shell_open; static bool battery_is_charging; -void GetAdapterState(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); +static bool pedometer_is_counting; - // TODO(purpasmart96): This function is only a stub, - // it returns a valid result without implementing full functionality. +void GetAdapterState(Service::Interface* self) { + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x5, 0, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = battery_is_charging ? 1 : 0; + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(battery_is_charging); LOG_WARNING(Service_PTM, "(STUBBED) called"); } void GetShellState(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x6, 0, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = shell_open ? 1 : 0; + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(shell_open); } void GetBatteryLevel(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x7, 0, 0); - // TODO(purpasmart96): This function is only a stub, - // it returns a valid result without implementing full functionality. - - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = - static_cast<u32>(ChargeLevels::CompletelyFull); // Set to a completely full battery + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(static_cast<u32>(ChargeLevels::CompletelyFull)); // Set to a completely full battery LOG_WARNING(Service_PTM, "(STUBBED) called"); } void GetBatteryChargeState(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x8, 0, 0); - // TODO(purpasmart96): This function is only a stub, - // it returns a valid result without implementing full functionality. + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(battery_is_charging); - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = battery_is_charging ? 1 : 0; + LOG_WARNING(Service_PTM, "(STUBBED) called"); +} + +void GetPedometerState(Service::Interface* self) { + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x9, 0, 0); + + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(pedometer_is_counting); LOG_WARNING(Service_PTM, "(STUBBED) called"); } void GetTotalStepCount(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0xC, 0, 0); - // TODO: This function is only a stub, - // it returns 0 as the total step count - - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = 0; + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); + rb.Push<u32>(0); LOG_WARNING(Service_PTM, "(STUBBED) called"); } void GetSoftwareClosedFlag(Service::Interface* self) { - u32* cmd_buff = Kernel::GetCommandBuffer(); + IPC::RequestParser rp(Kernel::GetCommandBuffer(), 0x80F, 0, 0); - cmd_buff[1] = RESULT_SUCCESS.raw; - cmd_buff[2] = 0; + IPC::RequestBuilder rb = rp.MakeBuilder(2, 0); + rb.Push(RESULT_SUCCESS); + rb.Push(false); LOG_WARNING(Service_PTM, "(STUBBED) called"); } @@ -121,6 +126,7 @@ void Init() { shell_open = true; battery_is_charging = true; + pedometer_is_counting = false; // Open the SharedExtSaveData archive 0xF000000B and create the gamecoin.dat file if it doesn't // exist diff --git a/src/core/hle/service/ptm/ptm.h b/src/core/hle/service/ptm/ptm.h index 683fb445b..e17e59835 100644 --- a/src/core/hle/service/ptm/ptm.h +++ b/src/core/hle/service/ptm/ptm.h @@ -75,6 +75,14 @@ void GetBatteryLevel(Interface* self); void GetBatteryChargeState(Interface* self); /** + * PTM::GetPedometerState service function + * Outputs: + * 1 : Result of function, 0 on success, otherwise error code + * 2 : Output of function, 0 = not counting steps, 1 = counting steps. + */ +void GetPedometerState(Interface* self); + +/** * PTM::GetTotalStepCount service function * Outputs: * 1 : Result of function, 0 on success, otherwise error code diff --git a/src/core/hle/service/ptm/ptm_u.cpp b/src/core/hle/service/ptm/ptm_u.cpp index e0b65ba89..696a58a36 100644 --- a/src/core/hle/service/ptm/ptm_u.cpp +++ b/src/core/hle/service/ptm/ptm_u.cpp @@ -17,7 +17,7 @@ const Interface::FunctionInfo FunctionTable[] = { {0x00060000, GetShellState, "GetShellState"}, {0x00070000, GetBatteryLevel, "GetBatteryLevel"}, {0x00080000, GetBatteryChargeState, "GetBatteryChargeState"}, - {0x00090000, nullptr, "GetPedometerState"}, + {0x00090000, GetPedometerState, "GetPedometerState"}, {0x000A0042, nullptr, "GetStepHistoryEntry"}, {0x000B00C2, nullptr, "GetStepHistory"}, {0x000C0000, GetTotalStepCount, "GetTotalStepCount"}, diff --git a/src/core/hle/svc.cpp b/src/core/hle/svc.cpp index 2db823c61..8538cfc9d 100644 --- a/src/core/hle/svc.cpp +++ b/src/core/hle/svc.cpp @@ -2,12 +2,12 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <cinttypes> #include <map> #include "common/logging/log.h" #include "common/microprofile.h" #include "common/scope_exit.h" #include "common/string_util.h" -#include "common/symbols.h" #include "core/arm/arm_interface.h" #include "core/core_timing.h" #include "core/hle/function_wrappers.h" @@ -524,13 +524,7 @@ static ResultCode CreateThread(Kernel::Handle* out_handle, s32 priority, u32 ent u32 stack_top, s32 processor_id) { using Kernel::Thread; - std::string name; - if (Symbols::HasSymbol(entry_point)) { - TSymbol symbol = Symbols::GetSymbol(entry_point); - name = symbol.name; - } else { - name = Common::StringFromFormat("unknown-%08x", entry_point); - } + std::string name = Common::StringFromFormat("unknown-%08" PRIX32, entry_point); if (priority > THREADPRIO_LOWEST) { return ResultCode(ErrorDescription::OutOfRange, ErrorModule::OS, diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index 8eb5200ab..cfcde9167 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp @@ -8,7 +8,6 @@ #include "common/common_types.h" #include "common/file_util.h" #include "common/logging/log.h" -#include "common/symbols.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/resource_limit.h" #include "core/loader/elf.h" @@ -210,7 +209,6 @@ public: return (u32)(header->e_flags); } SharedPtr<CodeSet> LoadInto(u32 vaddr); - bool LoadSymbols(); int GetNumSegments() const { return (int)(header->e_phnum); @@ -258,8 +256,6 @@ ElfReader::ElfReader(void* ptr) { sections = (Elf32_Shdr*)(base + header->e_shoff); entryPoint = header->e_entry; - - LoadSymbols(); } const char* ElfReader::GetSectionName(int section) const { @@ -362,34 +358,6 @@ SectionID ElfReader::GetSectionByName(const char* name, int firstSection) const return -1; } -bool ElfReader::LoadSymbols() { - bool hasSymbols = false; - SectionID sec = GetSectionByName(".symtab"); - if (sec != -1) { - int stringSection = sections[sec].sh_link; - const char* stringBase = reinterpret_cast<const char*>(GetSectionDataPtr(stringSection)); - - // We have a symbol table! - const Elf32_Sym* symtab = reinterpret_cast<const Elf32_Sym*>(GetSectionDataPtr(sec)); - unsigned int numSymbols = sections[sec].sh_size / sizeof(Elf32_Sym); - for (unsigned sym = 0; sym < numSymbols; sym++) { - int size = symtab[sym].st_size; - if (size == 0) - continue; - - int type = symtab[sym].st_info & 0xF; - - const char* name = stringBase + symtab[sym].st_name; - - Symbols::Add(symtab[sym].st_value, name, size, type); - - hasSymbols = true; - } - } - - return hasSymbols; -} - //////////////////////////////////////////////////////////////////////////////////////////////////// // Loader namespace diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 65e4bba85..b8438e490 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -672,12 +672,14 @@ PAddr VirtualToPhysicalAddress(const VAddr addr) { return addr - VRAM_VADDR + VRAM_PADDR; } else if (addr >= LINEAR_HEAP_VADDR && addr < LINEAR_HEAP_VADDR_END) { return addr - LINEAR_HEAP_VADDR + FCRAM_PADDR; + } else if (addr >= NEW_LINEAR_HEAP_VADDR && addr < NEW_LINEAR_HEAP_VADDR_END) { + return addr - NEW_LINEAR_HEAP_VADDR + FCRAM_PADDR; } else if (addr >= DSP_RAM_VADDR && addr < DSP_RAM_VADDR_END) { return addr - DSP_RAM_VADDR + DSP_RAM_PADDR; } else if (addr >= IO_AREA_VADDR && addr < IO_AREA_VADDR_END) { return addr - IO_AREA_VADDR + IO_AREA_PADDR; - } else if (addr >= NEW_LINEAR_HEAP_VADDR && addr < NEW_LINEAR_HEAP_VADDR_END) { - return addr - NEW_LINEAR_HEAP_VADDR + FCRAM_PADDR; + } else if (addr >= N3DS_EXTRA_RAM_VADDR && addr < N3DS_EXTRA_RAM_VADDR_END) { + return addr - N3DS_EXTRA_RAM_VADDR + N3DS_EXTRA_RAM_PADDR; } LOG_ERROR(HW_Memory, "Unknown virtual address @ 0x%08X", addr); @@ -696,6 +698,8 @@ VAddr PhysicalToVirtualAddress(const PAddr addr) { return addr - DSP_RAM_PADDR + DSP_RAM_VADDR; } else if (addr >= IO_AREA_PADDR && addr < IO_AREA_PADDR_END) { return addr - IO_AREA_PADDR + IO_AREA_VADDR; + } else if (addr >= N3DS_EXTRA_RAM_PADDR && addr < N3DS_EXTRA_RAM_PADDR_END) { + return addr - N3DS_EXTRA_RAM_PADDR + N3DS_EXTRA_RAM_VADDR; } LOG_ERROR(HW_Memory, "Unknown physical address @ 0x%08X", addr); diff --git a/src/core/memory.h b/src/core/memory.h index 903b58a22..802aa465e 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -37,6 +37,12 @@ enum : PAddr { VRAM_SIZE = 0x00600000, ///< VRAM size (6MB) VRAM_PADDR_END = VRAM_PADDR + VRAM_SIZE, + /// New 3DS additional memory. Supposedly faster than regular FCRAM. Part of it can be used by + /// applications and system modules if mapped via the ExHeader. + N3DS_EXTRA_RAM_PADDR = 0x1F000000, + N3DS_EXTRA_RAM_SIZE = 0x00400000, ///< New 3DS additional memory size (4MB) + N3DS_EXTRA_RAM_PADDR_END = N3DS_EXTRA_RAM_PADDR + N3DS_EXTRA_RAM_SIZE, + /// DSP memory DSP_RAM_PADDR = 0x1FF00000, DSP_RAM_SIZE = 0x00080000, ///< DSP memory size (512KB) @@ -81,6 +87,10 @@ enum : VAddr { LINEAR_HEAP_SIZE = 0x08000000, LINEAR_HEAP_VADDR_END = LINEAR_HEAP_VADDR + LINEAR_HEAP_SIZE, + /// Maps 1:1 to New 3DS additional memory + N3DS_EXTRA_RAM_VADDR = 0x1E800000, + N3DS_EXTRA_RAM_VADDR_END = N3DS_EXTRA_RAM_VADDR + N3DS_EXTRA_RAM_SIZE, + /// Maps 1:1 to the IO register area. IO_AREA_VADDR = 0x1EC00000, IO_AREA_VADDR_END = IO_AREA_VADDR + IO_AREA_SIZE, diff --git a/src/core/settings.cpp b/src/core/settings.cpp index a598f9f2f..d2e7c6b97 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -5,6 +5,7 @@ #include "audio_core/audio_core.h" #include "core/gdbstub/gdbstub.h" #include "core/hle/service/hid/hid.h" +#include "core/hle/service/ir/ir.h" #include "settings.h" #include "video_core/video_core.h" @@ -32,6 +33,7 @@ void Apply() { AudioCore::EnableStretching(values.enable_audio_stretching); Service::HID::ReloadInputDevices(); + Service::IR::ReloadInputDevices(); } } // namespace |