diff options
| author | Rusty Wagner <rusty.wagner@gmail.com> | 2024-03-05 19:50:13 -0500 |
|---|---|---|
| committer | Rusty Wagner <rusty.wagner@gmail.com> | 2024-03-05 20:34:34 -0500 |
| commit | e093c21ed880ac3eb72119be15093ee04f8ce299 (patch) | |
| tree | 9f720ebdc0ae415734b1199ed341668c69710a94 /arch/arm64/disassembler/pcode.c | |
| parent | 0609276712622908254065546102381466033141 (diff) | |
Move architecture modules into the API repo
Diffstat (limited to 'arch/arm64/disassembler/pcode.c')
| -rw-r--r-- | arch/arm64/disassembler/pcode.c | 755 |
1 files changed, 755 insertions, 0 deletions
diff --git a/arch/arm64/disassembler/pcode.c b/arch/arm64/disassembler/pcode.c new file mode 100644 index 00000000..2daf070b --- /dev/null +++ b/arch/arm64/disassembler/pcode.c @@ -0,0 +1,755 @@ +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +#include "decode.h" +#include "pcode.h" + +int BitCount(uint32_t x) +{ + int result = 0; + while (x) + { + if (x & 1) + result += 1; + x >>= 1; + } + return result; +} + +bool BFXPreferred(uint32_t sf, uint32_t uns, uint32_t imms, uint32_t immr) +{ + // must not match UBFIZ/SBFIX alias + if (imms < immr) + return false; + + // must not match LSR/ASR/LSL alias (imms == 31 or 63) + if (imms == ((sf << 5) | 0x1F)) + return false; + + // must not match UXTx/SXTx alias + if (immr == 0) + { + // must not match 32-bit UXT[BH] or SXT[BH] + if (sf == 0 && (imms == 7 || imms == 15)) + return false; + + // must not match 64-bit SXT[BHW] + if ((sf == 1 && uns == 0) && (imms == 7 || imms == 15 || imms == 31)) + return false; + } + + // must be UBFX/SBFX alias + return true; +} + +uint64_t rotate_right(uint64_t x, unsigned width, unsigned amount) +{ + amount = amount % width; + x = (x >> amount) | (x << (width - amount)); + x = (width < 64) ? x & (((uint64_t)1 << width) - 1) : x; + return x; +} + +DecodeBitMasks_ReturnType DecodeBitMasks( + uint8_t /*bit*/ immN, uint8_t /*bit(6)*/ imms, uint8_t /*bit(6)*/ immr) +{ + int ones_nbits = 0; + if (immN == 1) + ones_nbits = 6; + else if ((imms & 0x3E) == 0x3C) + ones_nbits = 1; + else if ((imms & 0x3C) == 0x38) + ones_nbits = 2; + else if ((imms & 0x38) == 0x30) + ones_nbits = 3; + else if ((imms & 0x30) == 0x20) + ones_nbits = 4; + else if ((imms & 0x20) == 0) + ones_nbits = 5; + // TODO else: return undefined + + /* set 1's in element */ + int ones_n = (imms & ((1 << ones_nbits) - 1)) + 1; + uint64_t result = ((uint64_t)1 << ones_n) - 1; + + /* rotate element */ + int elem_width = 1 << ones_nbits; + result = rotate_right(result, elem_width, immr); + + /* replicate element */ + while (elem_width < 64) + { + result = (result << elem_width) | result; + elem_width *= 2; + } + + DecodeBitMasks_ReturnType dbmrt; + // TODO: do this right + dbmrt.wmask = result; + dbmrt.tmask = result; + return dbmrt; +} + +/* idea to abandon pseudocode and compute+compare actual bitmask + is from NetBSD sys/arch/aarch64/aarch64/disasm.c */ +bool MoveWidePreferred(uint32_t sf, uint32_t immN, uint32_t immS, uint32_t immR) +{ + uint32_t splat = (immN << 6) | immS; + if (sf == 1 && !((splat & 0x40) == 0x40)) + return false; + if (sf == 0 && !((splat & 0x60) == 0x00)) + return false; + + DecodeBitMasks_ReturnType dbmrt = DecodeBitMasks(sf, immS, immR); + uint64_t imm = dbmrt.wmask; + + /* MOVZ check, at most 16 zeroes not across halfword (16-bit) boundary */ + if (sf == 0) + imm &= 0xffffffff; + if (((imm & 0xffffffffffff0000) == 0) || ((imm & 0xffffffff0000ffff) == 0) || + ((imm & 0xffff0000ffffffff) == 0) || ((imm & 0x0000ffffffffffff) == 0)) + return true; + + /* MOVN check, at most 16 ones not across halfword (16-bit) boundary */ + imm = ~imm; + if (sf == 0) + imm &= 0xffffffff; + if (((imm & 0xffffffffffff0000) == 0) || ((imm & 0xffffffff0000ffff) == 0) || + ((imm & 0xffff0000ffffffff) == 0) || ((imm & 0x0000ffffffffffff) == 0)) + return true; + + return false; +} + +int HighestSetBit(uint64_t x) +{ + for (int i = 63; i >= 0 && x; --i) + { + if (x & 0x8000000000000000) + return i; + x <<= 1; + } + + return -1; +} + +int LowestSetBit(uint64_t x) +{ + for (int i = 0; i < 64; ++i) + { + if (x & 1) + return i; + x >>= 1; + } + + return -1; +} + +bool SVEMoveMaskPreferred(uint32_t imm13) +{ + // TODO: populate this + return true; +} + +enum ShiftType DecodeRegExtend(uint8_t op) +{ + switch (op & 7) + { + case 0b000: + return ShiftType_UXTB; + case 0b001: + return ShiftType_UXTH; + case 0b010: + return ShiftType_UXTW; + case 0b011: + return ShiftType_UXTX; + case 0b100: + return ShiftType_SXTB; + case 0b101: + return ShiftType_SXTH; + case 0b110: + return ShiftType_SXTW; + case 0b111: + return ShiftType_SXTX; + default: + return ShiftType_NONE; + } +} + +enum ShiftType DecodeShift(uint8_t op) +{ + switch (op & 3) + { + case 0b00: + return ShiftType_LSL; + case 0b01: + return ShiftType_LSR; + case 0b10: + return ShiftType_ASR; + case 0b11: + return ShiftType_ROR; + default: + return ShiftType_NONE; + } +} + +enum SystemOp SysOp(uint32_t op1, uint32_t CRn, uint32_t CRm, uint32_t op2) +{ + uint32_t tmp = (op1 << 11) | (CRn << 7) | (CRm << 3) | op2; + + switch (tmp) + { + case 0b00001111000000: + return Sys_AT; // S1E1R + case 0b10001111000000: + return Sys_AT; // S1E2R + case 0b11001111000000: + return Sys_AT; // S1E3R + case 0b00001111000001: + return Sys_AT; // S1E1W + case 0b00001111001001: + return Sys_AT; // S1E1WP + case 0b10001111000001: + return Sys_AT; // S1E2W + case 0b11001111000001: + return Sys_AT; // S1E3W + case 0b00001111000010: + return Sys_AT; // S1E0R + case 0b00001111000011: + return Sys_AT; // S1E0W + case 0b10001111000100: + return Sys_AT; // S12E1R + case 0b10001111000101: + return Sys_AT; // S12E1W + case 0b10001111000110: + return Sys_AT; // S12E0R + case 0b10001111000111: + return Sys_AT; // S12E0W + case 0b01101110100001: + return Sys_DC; // ZVA + case 0b00001110110001: + return Sys_DC; // IVAC + case 0b00001110110010: + return Sys_DC; // ISW + case 0b01101111010001: + return Sys_DC; // CVAC + case 0b00001111010010: + return Sys_DC; // CSW + case 0b01101111011001: + return Sys_DC; // CVAU + case 0b01101111110001: + return Sys_DC; // CIVAC + case 0b00001111110010: + return Sys_DC; // CISW + case 0b01101111101001: + return Sys_DC; // CVADP + case 0b00001110001000: + return Sys_IC; // IALLUIS + case 0b00001110101000: + return Sys_IC; // IALLU + case 0b01101110101001: + return Sys_IC; // IVAU + case 0b10010000000001: + return Sys_TLBI; // IPAS2E1IS + case 0b10010000000101: + return Sys_TLBI; // IPAS2LE1IS + case 0b00010000011000: + return Sys_TLBI; // VMALLE1IS + case 0b10010000011000: + return Sys_TLBI; // ALLE2IS + case 0b11010000011000: + return Sys_TLBI; // ALLE3IS + case 0b00010000011001: + return Sys_TLBI; // VAE1IS + case 0b10010000011001: + return Sys_TLBI; // VAE2IS + case 0b11010000011001: + return Sys_TLBI; // VAE3IS + case 0b00010000011010: + return Sys_TLBI; // ASIDE1IS + case 0b00010000011011: + return Sys_TLBI; // VAAE1IS + case 0b10010000011100: + return Sys_TLBI; // ALLE1IS + case 0b00010000011101: + return Sys_TLBI; // VALE1IS + case 0b10010000011101: + return Sys_TLBI; // VALE2IS + case 0b11010000011101: + return Sys_TLBI; // VALE3IS + case 0b10010000011110: + return Sys_TLBI; // VMALLS12E1IS + case 0b00010000011111: + return Sys_TLBI; // VAALE1IS + case 0b10010000100001: + return Sys_TLBI; // IPAS2E1 + case 0b10010000100101: + return Sys_TLBI; // IPAS2LE1 + case 0b00010000111000: + return Sys_TLBI; // VMALLE1 + case 0b10010000111000: + return Sys_TLBI; // ALLE2 + case 0b11010000111000: + return Sys_TLBI; // ALLE3 + case 0b00010000111001: + return Sys_TLBI; // VAE1 + case 0b10010000111001: + return Sys_TLBI; // VAE2 + case 0b11010000111001: + return Sys_TLBI; // VAE3 + case 0b00010000111010: + return Sys_TLBI; // ASIDE1 + case 0b00010000111011: + return Sys_TLBI; // VAAE1 + case 0b10010000111100: + return Sys_TLBI; // ALLE1 + case 0b00010000111101: + return Sys_TLBI; // VALE1 + case 0b10010000111101: + return Sys_TLBI; // VALE2 + case 0b11010000111101: + return Sys_TLBI; // VALE3 + case 0b10010000111110: + return Sys_TLBI; // VMALLS12E1 + case 0b00010000111111: + return Sys_TLBI; // VAALE1 + default: + return Sys_ERROR; + } +} + +uint32_t UInt(uint32_t foo) +{ + return foo; +} + +uint32_t BitSlice(uint64_t foo, int hi, int lo) // including the endpoints +{ + int width = hi - lo + 1; + uint64_t mask = (1 << width) - 1; + return (foo >> lo) & mask; +} + +bool IsZero(uint64_t foo) +{ + return foo == 0; +} + +bool IsOnes(uint64_t foo, int width) +{ + return foo == (1 << width) - 1; +} + +/* Mozilla MPL + TODO: evaluate license, possibly rewrite + https://github.com/Siguza/iometa/blob/master/src/a64.c */ +uint64_t Replicate(uint64_t val, uint8_t times, uint64_t width) +{ + // Fast path + switch (times) + { + case 64: + val |= val << width; + width <<= 1; + case 32: + val |= val << width; + width <<= 1; + case 16: + val |= val << width; + width <<= 1; + case 8: + val |= val << width; + width <<= 1; + case 4: + val |= val << width; + width <<= 1; + case 2: + val |= val << width; + case 1: + return val; + case 0: + return 0; + default: + break; + } + // Slow path + uint64_t orig = val; + for (size_t i = 0; i < times; ++i) + { + val <<= width; + val |= orig; + } + return val; +} + +/* Mozilla MPL + TODO: evaluate license, possibly rewrite + https://github.com/Siguza/iometa/blob/master/src/a64.c */ +uint64_t AdvSIMDExpandImm(uint8_t op, uint8_t cmode, uint64_t imm8) +{ + uint64_t imm64; + switch ((cmode >> 1) & 0b111) + { + case 0b000: + imm64 = Replicate(imm8, 2, 32); + break; + case 0b001: + imm64 = Replicate(imm8 << 8, 2, 32); + break; + case 0b010: + imm64 = Replicate(imm8 << 16, 2, 32); + break; + case 0b011: + imm64 = Replicate(imm8 << 24, 2, 32); + break; + case 0b100: + imm64 = Replicate(imm8, 4, 16); + break; + case 0b101: + imm64 = Replicate(imm8 << 8, 4, 16); + break; + case 0b110: + imm64 = Replicate(imm8 << (8 << (cmode & 0b1)), 2, 32); + break; + case 0b111: + switch (((cmode & 0b1) << 1) | op) + { + case 0b00: + imm64 = Replicate(imm8, 8, 8); + break; + case 0b01: +#if 0 + imm8a = Replicate((imm8 >> 7) & 0b1, 8, 1); + imm8b = Replicate((imm8 >> 6) & 0b1, 8, 1); + imm8c = Replicate((imm8 >> 5) & 0b1, 8, 1); + imm8d = Replicate((imm8 >> 4) & 0b1, 8, 1); + imm8e = Replicate((imm8 >> 3) & 0b1, 8, 1); + imm8f = Replicate((imm8 >> 2) & 0b1, 8, 1); + imm8g = Replicate((imm8 >> 1) & 0b1, 8, 1); + imm8h = Replicate((imm8 ) & 0b1, 8, 1); + imm64 = (imm8a << 0x38) | (imm8b << 0x30) | (imm8c << 0x28) | (imm8d << 0x20) | (imm8e << 0x18) | (imm8f << 0x10) | (imm8g << 0x08) | imm8h; +#else + imm64 = imm8 | (imm8 << (0x08 - 1)) | (imm8 << (0x10 - 2)) | (imm8 << (0x18 - 3)) | + (imm8 << (0x20 - 4)) | (imm8 << (0x28 - 5)) | (imm8 << (0x30 - 6)) | + (imm8 << (0x38 - 7)); + imm64 &= 0x0101010101010101; + imm64 = Replicate(imm64, 8, 1); +#endif + break; + case 0b10: + imm64 = Replicate((((imm8 & 0xc0) ^ 0x80) << 24) | + (Replicate((imm8 >> 6) & 0b1, 5, 1) << 25) | ((imm8 & 0x3f) << 19), + 2, 32); + break; + case 0b11: + imm64 = (((imm8 & 0xc0) ^ 0x80) << 56) | (Replicate((imm8 >> 6) & 0b1, 8, 1) << 54) | + ((imm8 & 0x3f) << 48); + break; + } + break; + } + return imm64; +} + +bool BTypeCompatible_BTI(uint8_t hintcode, uint8_t pstate_btype) +{ + switch (hintcode & 3) + { + case 0b00: + return false; + case 0b01: + return pstate_btype != 0b11; + case 0b10: + return pstate_btype != 0b10; + case 0b11: + return true; + } + + return false; /* impossible, but appease compiler */ +} + +bool BTypeCompatible_PACIXSP() +{ + // TODO: determine if filling this in is necessary + return true; +} + +enum FPRounding FPDecodeRounding(uint8_t RMode) +{ + switch (RMode & 3) + { + case 0b00: + return FPRounding_TIEEVEN; // N + case 0b01: + return FPRounding_POSINF; // P + case 0b10: + return FPRounding_NEGINF; // M + case 0b11: + return FPRounding_ZERO; // Z + } + + return FPRounding_ERROR; +} + +enum FPRounding FPRoundingMode(uint64_t fpcr) +{ + return FPDecodeRounding(FPCR_GET_RMode(fpcr)); +} + +bool HaltingAllowed(void) +{ + // TODO: determine if filling this in is necessary + return true; +} + +// AArch64.SystemAccessTrap +void SystemAccessTrap(uint32_t a, uint32_t b) +{ + // TODO: determine if filling this in is necessary + while (0) + ; +} + +void CheckSystemAccess(uint8_t a, uint8_t b, uint8_t c, uint8_t d, uint8_t e, uint8_t f, uint8_t g) +{ + // TODO: determine if filling this in is necessary + while (0) + ; +} + +// from LLVM +// TODO: check license, determine if rewrite needed +uint64_t VFPExpandImm(unsigned char byte, unsigned N) +{ + // assert(N == 32 || N == 64); + + uint64_t Result; + unsigned bit6 = SLICE(byte, 6, 6); + if (N == 32) + { + Result = SLICE(byte, 7, 7) << 31 | SLICE(byte, 5, 0) << 19; + if (bit6) + Result |= 0x1f << 25; + else + Result |= 0x1 << 30; + } + else + { + Result = (uint64_t)SLICE(byte, 7, 7) << 63 | (uint64_t)SLICE(byte, 5, 0) << 48; + if (bit6) + Result |= 0xffULL << 54; + else + Result |= 0x1ULL << 62; + } + + // return APInt(N, Result); + return Result; +} + +bool EL2Enabled(void) +{ + // TODO: determine if filling this in is necessary + return true; +} + +bool ELUsingAArch32(uint8_t x) +{ + // TODO: determine if filling this in is necessary + return true; +} + +uint64_t FPOne(bool sign, int N) +{ + // width should be 16, 32, 64 + int E, F, exp; + + switch (N) + { + case 16: + E = 5; + case 32: + E = 8; + default: + E = 11; + } + + F = N - (E + 1); + exp = BITMASK(E - 1) << 1; + return (sign << (E - 1 + F)) | (exp << F); +} + +uint64_t FPTwo(bool sign, int N) +{ + // width should be 16, 32, 64 + //int F; + int E, exp; + + switch (N) + { + case 16: + E = 5; + case 32: + E = 8; + default: + E = 11; + } + + //F = N - (E + 1); + exp = 1 << (E - 1); + return (sign << E) | exp; +} + +uint64_t FPPointFive(bool sign, int N) +{ + // width should be 16, 32, 64 + int E, F, exp; + + switch (N) + { + case 16: + E = 5; + case 32: + E = 8; + default: + E = 11; + } + + F = N - (E + 1); + exp = BITMASK(E - 2) << 1; + return (sign << (E - 2 + F)) | (exp << F); +} + +uint64_t SignExtend(uint64_t x, int width) +{ + uint64_t result = -1; + + if (x & ((uint64_t)1 << (width - 1))) + { + result ^= (((uint64_t)1 << width) - 1); + result |= x; + } + else + { + result = x; + } + + return result; +} + +enum Constraint ConstrainUnpredictable(enum Unpredictable u) +{ + switch (u) + { + case Unpredictable_VMSR: + return Constraint_UNDEF; + case Unpredictable_WBOVERLAPLD: + return Constraint_WBSUPPRESS; // return loaded value + case Unpredictable_WBOVERLAPST: + return Constraint_NONE; // store pre-writeback value + case Unpredictable_LDPOVERLAP: + return Constraint_UNDEF; // instruction is UNDEFINED + case Unpredictable_BASEOVERLAP: + return Constraint_NONE; // use original address + case Unpredictable_DATAOVERLAP: + return Constraint_NONE; // store original value + case Unpredictable_DEVPAGE2: + return Constraint_FAULT; // take an alignment fault + case Unpredictable_DEVICETAGSTORE: + return Constraint_NONE; // Do not take a fault + case Unpredictable_INSTRDEVICE: + return Constraint_NONE; // Do not take a fault + case Unpredictable_RESCPACR: + return Constraint_TRUE; // Map to UNKNOWN value + case Unpredictable_RESMAIR: + return Constraint_UNKNOWN; // Map to UNKNOWN value + case Unpredictable_RESTEXCB: + return Constraint_UNKNOWN; // Map to UNKNOWN value + case Unpredictable_RESDACR: + return Constraint_UNKNOWN; // Map to UNKNOWN value + case Unpredictable_RESPRRR: + return Constraint_UNKNOWN; // Map to UNKNOWN value + case Unpredictable_RESVTCRS: + return Constraint_UNKNOWN; // Map to UNKNOWN value + case Unpredictable_RESTnSZ: + return Constraint_FORCE; // Map to the limit value + case Unpredictable_OORTnSZ: + return Constraint_FORCE; // Map to the limit value + case Unpredictable_LARGEIPA: + return Constraint_FORCE; // Restrict the inputsize to the PAMax value + case Unpredictable_ESRCONDPASS: + return Constraint_FALSE; // Report as "AL" + case Unpredictable_ILZEROIT: + return Constraint_FALSE; // Do not zero PSTATE.IT + case Unpredictable_ILZEROT: + return Constraint_FALSE; // Do not zero PSTATE.T + case Unpredictable_BPVECTORCATCHPRI: + return Constraint_TRUE; // Debug Vector Catch: match on 2nd halfword + case Unpredictable_VCMATCHHALF: + return Constraint_FALSE; // No match + case Unpredictable_VCMATCHDAPA: + return Constraint_FALSE; // No match on Data Abort or Prefetch abort + case Unpredictable_WPMASKANDBAS: + return Constraint_FALSE; // Watchpoint disabled + case Unpredictable_WPBASCONTIGUOUS: + return Constraint_FALSE; // Watchpoint disabled + case Unpredictable_RESWPMASK: + return Constraint_DISABLED; // Watchpoint disabled + case Unpredictable_WPMASKEDBITS: + return Constraint_FALSE; // Watchpoint disabled + case Unpredictable_RESBPWPCTRL: + return Constraint_DISABLED; // Breakpoint/watchpoint disabled + case Unpredictable_BPNOTIMPL: + return Constraint_DISABLED; // Breakpoint disabled + case Unpredictable_RESBPTYPE: + return Constraint_DISABLED; // Breakpoint disabled + case Unpredictable_BPNOTCTXCMP: + return Constraint_DISABLED; // Breakpoint disabled + case Unpredictable_BPMATCHHALF: + return Constraint_FALSE; // No match + case Unpredictable_BPMISMATCHHALF: + return Constraint_FALSE; // No match + case Unpredictable_RESTARTALIGNPC: + return Constraint_FALSE; // Do not force alignment + case Unpredictable_RESTARTZEROUPPERPC: + return Constraint_TRUE; // Force zero extension + case Unpredictable_ZEROUPPER: + return Constraint_TRUE; // zero top halves of X registers + case Unpredictable_ERETZEROUPPERPC: + return Constraint_TRUE; // zero top half of PC + case Unpredictable_A32FORCEALIGNPC: + return Constraint_FALSE; // Do not force alignment + case Unpredictable_SMD: + return Constraint_UNDEF; // disabled SMC is Unallocated + case Unpredictable_NONFAULT: + return Constraint_FALSE; // Speculation enabled + case Unpredictable_SVEZEROUPPER: + return Constraint_TRUE; // zero top bits of Z registers + case Unpredictable_SVELDNFDATA: + return Constraint_TRUE; // Load mem data in NF loads + case Unpredictable_SVELDNFZERO: + return Constraint_TRUE; // Write zeros in NF loads + case Unpredictable_CHECKSPNONEACTIVE: + return Constraint_TRUE; // Check SP alignment + case Unpredictable_AFUPDATE: + return Constraint_TRUE; + case Unpredictable_IESBinDebug: + return Constraint_TRUE; + case Unpredictable_BADPMSFCR: + return Constraint_TRUE; + case Unpredictable_ZEROBTYPE: + return Constraint_TRUE; // Save BTYPE in SPSR_ELx/DPSR_EL0 as '00' + case Unpredictable_CLEARERRITEZERO: + return Constraint_FALSE; + case Unpredictable_ALUEXCEPTIONRETURN: + return Constraint_UNDEF; + case Unpredictable_DBGxVR_RESS: + return Constraint_FALSE; + case Unpredictable_WFxTDEBUG: + return Constraint_FALSE; // WFxT in Debug state does not execute as a NOP + case Unpredictable_LS64UNSUPPORTED: + return Constraint_LIMITED_ATOMICITY; // + default: + return Constraint_ERROR; + } +} |
