Files
kernel/arch/amd64/disasm/x86.c
T
2020-10-20 10:42:12 +03:00

179 lines
3.9 KiB
C

#include "arch/amd64/disasm/x86.h"
#include "arch/amd64/disasm/util.h"
#include "sys/assert.h"
#include "sys/debug.h"
//#include <sys/param.h>
//#include <stdlib.h>
//#include <assert.h>
//#include <string.h>
//#include <stdio.h>
static uint32_t processor_mode = MODE_REAL;
static ssize_t x86_read_prefices(const uint8_t *data, uint32_t *prefices) {
uint8_t byte;
uint32_t p = 0;
size_t pos = 0;
while (1) {
int is_prefix = 1;
byte = data[pos];
switch (byte) {
// Grp1
case 0xF0:
p |= PREF_LOCK;
break;
case 0xF2:
break;
case 0xF3:
p |= PREF_REP;
break;
// Grp2
case 0x26:
p |= PREF_SEG_ES;
break;
case 0x2E:
p |= PREF_SEG_CS;
break;
case 0x3E:
p |= PREF_SEG_DS;
break;
case 0x64:
p |= PREF_SEG_FS;
break;
case 0x65:
p |= PREF_SEG_GS;
break;
// Grp3
case 0x66:
p |= PREF_OP_SIZE;
break;
// Grp4
case 0x67:
p |= PREF_AD_SIZE;
break;
default:
is_prefix = 0;
break;
}
if (is_prefix) {
++pos;
} else {
break;
}
}
*prefices = p;
return pos;
}
static void x86_sizes(uint32_t prefices, uint8_t rex, uint32_t insn_flags, size_t *operand_size, size_t *address_size) {
switch (processor_mode) {
case MODE_REAL:
if (prefices & PREF_AD_SIZE) {
*address_size = 32;
} else {
*address_size = 16;
}
if (prefices & PREF_OP_SIZE) {
*operand_size = 32;
} else {
*operand_size = 16;
}
break;
case MODE_LONG:
if (prefices & PREF_AD_SIZE) {
*address_size = 32;
} else {
*address_size = 64;
}
if ((rex & REX_W) || (insn_flags & OP_DEF64)) {
_assert(!(prefices & PREF_OP_SIZE));
*operand_size = 64;
} else {
*operand_size = 32;
if (prefices & PREF_OP_SIZE) {
*operand_size = 16;
}
}
break;
default:
panic("Unhandled CPU mode\n");
}
if (insn_flags & OP_DEF8) {
*operand_size = 8;
}
}
ssize_t read_op(const uint8_t *data, uint64_t base_addr, struct op *op) {
// Read prefixes
size_t pos = 0;
uint8_t byte;
ssize_t op_len;
uint32_t prefices = 0;
uint8_t rex = 0;
int has_modrm = 0;
uint8_t modrm;
struct op_info *info = NULL;
// Read prefices
if ((op_len = x86_read_prefices(data, &prefices)) < 0) {
return -1;
}
pos += op_len;
byte = data[pos];
op->rex = 0;
if (processor_mode != MODE_REAL) {
// Check if it's REX
if ((byte & 0xF0) == 0x40) {
rex = byte;
prefices |= PREF_REX;
++pos;
if ((processor_mode != MODE_LONG) && (rex & REX_W)) {
kerror("REX.W in legacy modes\n");
return -1;
}
}
}
if ((op_len = x86_match_op(data + pos, &info, &modrm)) > 0) {
op->rex = rex;
op->info = info;
op->prefices = prefices;
pos += op_len;
x86_sizes(prefices, rex, info->flags, &op->operand_size, &op->address_size);
// This means x86_match_op has already read ModR/M for us
if (info->flags & OP_RMEXT) {
has_modrm = 1;
}
if ((op_len = x86_read_operands(data + pos, op, has_modrm ? &modrm : NULL)) < 0) {
return -1;
}
pos += op_len;
return pos;
} else {
op->info = NULL;
return pos + 1;
}
}
void x86_set_mode(int mode) {
processor_mode = mode;
}