tcg-i386: Use %gs prefixes for x86_64 GUEST_BASE

When we allocate a reserved_va for the guest, the kernel will likely
choose an address well above 4G.  At which point we must use a pair
of movabsq+addq to form the host address.  If we have OS support,
set up a segment register to point to guest_base instead.

Signed-off-by: Richard Henderson <rth@twiddle.net>
Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
This commit is contained in:
Richard Henderson 2012-10-22 12:11:07 +10:00 committed by Aurelien Jarno
parent d73ee8a2b5
commit 44b37ace06
1 changed files with 95 additions and 54 deletions

View File

@ -232,11 +232,13 @@ static inline int tcg_target_const_match(tcg_target_long val,
# define P_REXW 0x800 /* Set REX.W = 1 */ # define P_REXW 0x800 /* Set REX.W = 1 */
# define P_REXB_R 0x1000 /* REG field as byte register */ # define P_REXB_R 0x1000 /* REG field as byte register */
# define P_REXB_RM 0x2000 /* R/M field as byte register */ # define P_REXB_RM 0x2000 /* R/M field as byte register */
# define P_GS 0x4000 /* gs segment override */
#else #else
# define P_ADDR32 0 # define P_ADDR32 0
# define P_REXW 0 # define P_REXW 0
# define P_REXB_R 0 # define P_REXB_R 0
# define P_REXB_RM 0 # define P_REXB_RM 0
# define P_GS 0
#endif #endif
#define OPC_ARITH_EvIz (0x81) #define OPC_ARITH_EvIz (0x81)
@ -352,6 +354,9 @@ static void tcg_out_opc(TCGContext *s, int opc, int r, int rm, int x)
{ {
int rex; int rex;
if (opc & P_GS) {
tcg_out8(s, 0x65);
}
if (opc & P_DATA16) { if (opc & P_DATA16) {
/* We should never be asking for both 16 and 64-bit operation. */ /* We should never be asking for both 16 and 64-bit operation. */
assert((opc & P_REXW) == 0); assert((opc & P_REXW) == 0);
@ -1076,10 +1081,27 @@ static inline void tcg_out_tlb_load(TCGContext *s, int addrlo_idx,
tcg_out_modrm_offset(s, OPC_ADD_GvEv + P_REXW, r1, r0, tcg_out_modrm_offset(s, OPC_ADD_GvEv + P_REXW, r1, r0,
offsetof(CPUTLBEntry, addend) - which); offsetof(CPUTLBEntry, addend) - which);
} }
#endif #elif defined(__x86_64__) && defined(__linux__)
# include <asm/prctl.h>
# include <sys/prctl.h>
int arch_prctl(int code, unsigned long addr);
static int guest_base_flags;
static inline void setup_guest_base_seg(void)
{
if (arch_prctl(ARCH_SET_GS, GUEST_BASE) == 0) {
guest_base_flags = P_GS;
}
}
#else
# define guest_base_flags 0
static inline void setup_guest_base_seg(void) { }
#endif /* SOFTMMU */
static void tcg_out_qemu_ld_direct(TCGContext *s, int datalo, int datahi, static void tcg_out_qemu_ld_direct(TCGContext *s, int datalo, int datahi,
int base, tcg_target_long ofs, int sizeop) int base, tcg_target_long ofs, int seg,
int sizeop)
{ {
#ifdef TARGET_WORDS_BIGENDIAN #ifdef TARGET_WORDS_BIGENDIAN
const int bswap = 1; const int bswap = 1;
@ -1088,28 +1110,29 @@ static void tcg_out_qemu_ld_direct(TCGContext *s, int datalo, int datahi,
#endif #endif
switch (sizeop) { switch (sizeop) {
case 0: case 0:
tcg_out_modrm_offset(s, OPC_MOVZBL, datalo, base, ofs); tcg_out_modrm_offset(s, OPC_MOVZBL + seg, datalo, base, ofs);
break; break;
case 0 | 4: case 0 | 4:
tcg_out_modrm_offset(s, OPC_MOVSBL + P_REXW, datalo, base, ofs); tcg_out_modrm_offset(s, OPC_MOVSBL + P_REXW + seg, datalo, base, ofs);
break; break;
case 1: case 1:
tcg_out_modrm_offset(s, OPC_MOVZWL, datalo, base, ofs); tcg_out_modrm_offset(s, OPC_MOVZWL + seg, datalo, base, ofs);
if (bswap) { if (bswap) {
tcg_out_rolw_8(s, datalo); tcg_out_rolw_8(s, datalo);
} }
break; break;
case 1 | 4: case 1 | 4:
if (bswap) { if (bswap) {
tcg_out_modrm_offset(s, OPC_MOVZWL, datalo, base, ofs); tcg_out_modrm_offset(s, OPC_MOVZWL + seg, datalo, base, ofs);
tcg_out_rolw_8(s, datalo); tcg_out_rolw_8(s, datalo);
tcg_out_modrm(s, OPC_MOVSWL + P_REXW, datalo, datalo); tcg_out_modrm(s, OPC_MOVSWL + P_REXW, datalo, datalo);
} else { } else {
tcg_out_modrm_offset(s, OPC_MOVSWL + P_REXW, datalo, base, ofs); tcg_out_modrm_offset(s, OPC_MOVSWL + P_REXW + seg,
datalo, base, ofs);
} }
break; break;
case 2: case 2:
tcg_out_ld(s, TCG_TYPE_I32, datalo, base, ofs); tcg_out_modrm_offset(s, OPC_MOVL_GvEv + seg, datalo, base, ofs);
if (bswap) { if (bswap) {
tcg_out_bswap32(s, datalo); tcg_out_bswap32(s, datalo);
} }
@ -1117,17 +1140,18 @@ static void tcg_out_qemu_ld_direct(TCGContext *s, int datalo, int datahi,
#if TCG_TARGET_REG_BITS == 64 #if TCG_TARGET_REG_BITS == 64
case 2 | 4: case 2 | 4:
if (bswap) { if (bswap) {
tcg_out_ld(s, TCG_TYPE_I32, datalo, base, ofs); tcg_out_modrm_offset(s, OPC_MOVL_GvEv + seg, datalo, base, ofs);
tcg_out_bswap32(s, datalo); tcg_out_bswap32(s, datalo);
tcg_out_ext32s(s, datalo, datalo); tcg_out_ext32s(s, datalo, datalo);
} else { } else {
tcg_out_modrm_offset(s, OPC_MOVSLQ, datalo, base, ofs); tcg_out_modrm_offset(s, OPC_MOVSLQ + seg, datalo, base, ofs);
} }
break; break;
#endif #endif
case 3: case 3:
if (TCG_TARGET_REG_BITS == 64) { if (TCG_TARGET_REG_BITS == 64) {
tcg_out_ld(s, TCG_TYPE_I64, datalo, base, ofs); tcg_out_modrm_offset(s, OPC_MOVL_GvEv + P_REXW + seg,
datalo, base, ofs);
if (bswap) { if (bswap) {
tcg_out_bswap64(s, datalo); tcg_out_bswap64(s, datalo);
} }
@ -1138,11 +1162,15 @@ static void tcg_out_qemu_ld_direct(TCGContext *s, int datalo, int datahi,
datahi = t; datahi = t;
} }
if (base != datalo) { if (base != datalo) {
tcg_out_ld(s, TCG_TYPE_I32, datalo, base, ofs); tcg_out_modrm_offset(s, OPC_MOVL_GvEv + seg,
tcg_out_ld(s, TCG_TYPE_I32, datahi, base, ofs + 4); datalo, base, ofs);
tcg_out_modrm_offset(s, OPC_MOVL_GvEv + seg,
datahi, base, ofs + 4);
} else { } else {
tcg_out_ld(s, TCG_TYPE_I32, datahi, base, ofs + 4); tcg_out_modrm_offset(s, OPC_MOVL_GvEv + seg,
tcg_out_ld(s, TCG_TYPE_I32, datalo, base, ofs); datahi, base, ofs + 4);
tcg_out_modrm_offset(s, OPC_MOVL_GvEv + seg,
datalo, base, ofs);
} }
if (bswap) { if (bswap) {
tcg_out_bswap32(s, datalo); tcg_out_bswap32(s, datalo);
@ -1186,7 +1214,7 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args,
label_ptr, offsetof(CPUTLBEntry, addr_read)); label_ptr, offsetof(CPUTLBEntry, addr_read));
/* TLB Hit. */ /* TLB Hit. */
tcg_out_qemu_ld_direct(s, data_reg, data_reg2, TCG_REG_L1, 0, opc); tcg_out_qemu_ld_direct(s, data_reg, data_reg2, TCG_REG_L1, 0, 0, opc);
/* jmp label2 */ /* jmp label2 */
tcg_out8(s, OPC_JMP_short); tcg_out8(s, OPC_JMP_short);
@ -1273,29 +1301,31 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args,
{ {
int32_t offset = GUEST_BASE; int32_t offset = GUEST_BASE;
int base = args[addrlo_idx]; int base = args[addrlo_idx];
int seg = 0;
if (TCG_TARGET_REG_BITS == 64) { /* ??? We assume all operations have left us with register contents
/* ??? We assume all operations have left us with register that are zero extended. So far this appears to be true. If we
contents that are zero extended. So far this appears to want to enforce this, we can either do an explicit zero-extension
be true. If we want to enforce this, we can either do here, or (if GUEST_BASE == 0, or a segment register is in use)
an explicit zero-extension here, or (if GUEST_BASE == 0)
use the ADDR32 prefix. For now, do nothing. */ use the ADDR32 prefix. For now, do nothing. */
if (GUEST_BASE && guest_base_flags) {
if (offset != GUEST_BASE) { seg = guest_base_flags;
offset = 0;
} else if (TCG_TARGET_REG_BITS == 64 && offset != GUEST_BASE) {
tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_L1, GUEST_BASE); tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_L1, GUEST_BASE);
tgen_arithr(s, ARITH_ADD + P_REXW, TCG_REG_L1, base); tgen_arithr(s, ARITH_ADD + P_REXW, TCG_REG_L1, base);
base = TCG_REG_L1; base = TCG_REG_L1;
offset = 0; offset = 0;
} }
}
tcg_out_qemu_ld_direct(s, data_reg, data_reg2, base, offset, opc); tcg_out_qemu_ld_direct(s, data_reg, data_reg2, base, offset, seg, opc);
} }
#endif #endif
} }
static void tcg_out_qemu_st_direct(TCGContext *s, int datalo, int datahi, static void tcg_out_qemu_st_direct(TCGContext *s, int datalo, int datahi,
int base, tcg_target_long ofs, int sizeop) int base, tcg_target_long ofs, int seg,
int sizeop)
{ {
#ifdef TARGET_WORDS_BIGENDIAN #ifdef TARGET_WORDS_BIGENDIAN
const int bswap = 1; const int bswap = 1;
@ -1310,7 +1340,8 @@ static void tcg_out_qemu_st_direct(TCGContext *s, int datalo, int datahi,
switch (sizeop) { switch (sizeop) {
case 0: case 0:
tcg_out_modrm_offset(s, OPC_MOVB_EvGv + P_REXB_R, datalo, base, ofs); tcg_out_modrm_offset(s, OPC_MOVB_EvGv + P_REXB_R + seg,
datalo, base, ofs);
break; break;
case 1: case 1:
if (bswap) { if (bswap) {
@ -1318,7 +1349,8 @@ static void tcg_out_qemu_st_direct(TCGContext *s, int datalo, int datahi,
tcg_out_rolw_8(s, scratch); tcg_out_rolw_8(s, scratch);
datalo = scratch; datalo = scratch;
} }
tcg_out_modrm_offset(s, OPC_MOVL_EvGv + P_DATA16, datalo, base, ofs); tcg_out_modrm_offset(s, OPC_MOVL_EvGv + P_DATA16 + seg,
datalo, base, ofs);
break; break;
case 2: case 2:
if (bswap) { if (bswap) {
@ -1326,7 +1358,7 @@ static void tcg_out_qemu_st_direct(TCGContext *s, int datalo, int datahi,
tcg_out_bswap32(s, scratch); tcg_out_bswap32(s, scratch);
datalo = scratch; datalo = scratch;
} }
tcg_out_st(s, TCG_TYPE_I32, datalo, base, ofs); tcg_out_modrm_offset(s, OPC_MOVL_EvGv + seg, datalo, base, ofs);
break; break;
case 3: case 3:
if (TCG_TARGET_REG_BITS == 64) { if (TCG_TARGET_REG_BITS == 64) {
@ -1335,17 +1367,18 @@ static void tcg_out_qemu_st_direct(TCGContext *s, int datalo, int datahi,
tcg_out_bswap64(s, scratch); tcg_out_bswap64(s, scratch);
datalo = scratch; datalo = scratch;
} }
tcg_out_st(s, TCG_TYPE_I64, datalo, base, ofs); tcg_out_modrm_offset(s, OPC_MOVL_EvGv + P_REXW + seg,
datalo, base, ofs);
} else if (bswap) { } else if (bswap) {
tcg_out_mov(s, TCG_TYPE_I32, scratch, datahi); tcg_out_mov(s, TCG_TYPE_I32, scratch, datahi);
tcg_out_bswap32(s, scratch); tcg_out_bswap32(s, scratch);
tcg_out_st(s, TCG_TYPE_I32, scratch, base, ofs); tcg_out_modrm_offset(s, OPC_MOVL_EvGv + seg, scratch, base, ofs);
tcg_out_mov(s, TCG_TYPE_I32, scratch, datalo); tcg_out_mov(s, TCG_TYPE_I32, scratch, datalo);
tcg_out_bswap32(s, scratch); tcg_out_bswap32(s, scratch);
tcg_out_st(s, TCG_TYPE_I32, scratch, base, ofs + 4); tcg_out_modrm_offset(s, OPC_MOVL_EvGv + seg, scratch, base, ofs+4);
} else { } else {
tcg_out_st(s, TCG_TYPE_I32, datalo, base, ofs); tcg_out_modrm_offset(s, OPC_MOVL_EvGv + seg, datalo, base, ofs);
tcg_out_st(s, TCG_TYPE_I32, datahi, base, ofs + 4); tcg_out_modrm_offset(s, OPC_MOVL_EvGv + seg, datahi, base, ofs+4);
} }
break; break;
default: default:
@ -1379,7 +1412,7 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args,
label_ptr, offsetof(CPUTLBEntry, addr_write)); label_ptr, offsetof(CPUTLBEntry, addr_write));
/* TLB Hit. */ /* TLB Hit. */
tcg_out_qemu_st_direct(s, data_reg, data_reg2, TCG_REG_L1, 0, opc); tcg_out_qemu_st_direct(s, data_reg, data_reg2, TCG_REG_L1, 0, 0, opc);
/* jmp label2 */ /* jmp label2 */
tcg_out8(s, OPC_JMP_short); tcg_out8(s, OPC_JMP_short);
@ -1436,23 +1469,24 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args,
{ {
int32_t offset = GUEST_BASE; int32_t offset = GUEST_BASE;
int base = args[addrlo_idx]; int base = args[addrlo_idx];
int seg = 0;
if (TCG_TARGET_REG_BITS == 64) { /* ??? We assume all operations have left us with register contents
/* ??? We assume all operations have left us with register that are zero extended. So far this appears to be true. If we
contents that are zero extended. So far this appears to want to enforce this, we can either do an explicit zero-extension
be true. If we want to enforce this, we can either do here, or (if GUEST_BASE == 0, or a segment register is in use)
an explicit zero-extension here, or (if GUEST_BASE == 0)
use the ADDR32 prefix. For now, do nothing. */ use the ADDR32 prefix. For now, do nothing. */
if (GUEST_BASE && guest_base_flags) {
if (offset != GUEST_BASE) { seg = guest_base_flags;
offset = 0;
} else if (TCG_TARGET_REG_BITS == 64 && offset != GUEST_BASE) {
tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_L1, GUEST_BASE); tcg_out_movi(s, TCG_TYPE_I64, TCG_REG_L1, GUEST_BASE);
tgen_arithr(s, ARITH_ADD + P_REXW, TCG_REG_L1, base); tgen_arithr(s, ARITH_ADD + P_REXW, TCG_REG_L1, base);
base = TCG_REG_L1; base = TCG_REG_L1;
offset = 0; offset = 0;
} }
}
tcg_out_qemu_st_direct(s, data_reg, data_reg2, base, offset, opc); tcg_out_qemu_st_direct(s, data_reg, data_reg2, base, offset, seg, opc);
} }
#endif #endif
} }
@ -2046,6 +2080,13 @@ static void tcg_target_qemu_prologue(TCGContext *s)
tcg_out_pop(s, tcg_target_callee_save_regs[i]); tcg_out_pop(s, tcg_target_callee_save_regs[i]);
} }
tcg_out_opc(s, OPC_RET, 0, 0, 0); tcg_out_opc(s, OPC_RET, 0, 0, 0);
#if !defined(CONFIG_SOFTMMU)
/* Try to set up a segment register to point to GUEST_BASE. */
if (GUEST_BASE) {
setup_guest_base_seg();
}
#endif
} }
static void tcg_target_init(TCGContext *s) static void tcg_target_init(TCGContext *s)