Commit 33417e70 authored by bellard's avatar bellard
Browse files

soft mmu support - Memory I/O API - synthetize string instructions


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@354 c046a42c-6fe2-441c-8c8c-71466251a162
parent 4021dab0
......@@ -140,6 +140,7 @@ static inline void stfl(void *ptr, float v)
stl(ptr, u.i);
}
#if defined(__arm__) && !defined(WORDS_BIGENDIAN)
/* NOTE: arm is horrible as double 32 bit words are stored in big endian ! */
......@@ -317,6 +318,17 @@ int cpu_breakpoint_insert(CPUState *env, uint32_t pc);
int cpu_breakpoint_remove(CPUState *env, uint32_t pc);
void cpu_single_step(CPUState *env, int enabled);
/* memory API */
typedef void CPUWriteMemoryFunc(uint32_t addr, uint32_t value);
typedef uint32_t CPUReadMemoryFunc(uint32_t addr);
void cpu_register_physical_memory(unsigned long start_addr, unsigned long size,
long phys_offset);
int cpu_register_io_memory(int io_index,
CPUReadMemoryFunc **mem_read,
CPUWriteMemoryFunc **mem_write);
/* gdb stub API */
extern int gdbstub_fd;
CPUState *cpu_gdbstub_get_env(void *opaque);
......
......@@ -20,12 +20,10 @@
#ifndef CPU_ARM_H
#define CPU_ARM_H
#include "config.h"
#include <setjmp.h>
#include "cpu-defs.h"
#define EXCP_UDEF 1 /* undefined instruction */
#define EXCP_SWI 2 /* software interrupt */
#define EXCP_INTERRUPT 256 /* async interruption */
typedef struct CPUARMState {
uint32_t regs[16];
......
......@@ -20,8 +20,7 @@
#ifndef CPU_I386_H
#define CPU_I386_H
#include "config.h"
#include <setjmp.h>
#include "cpu-defs.h"
#define R_EAX 0
#define R_ECX 1
......@@ -153,12 +152,6 @@
#define EXCP11_ALGN 17
#define EXCP12_MCHK 18
#define EXCP_INTERRUPT 256 /* async interruption */
#define EXCP_HLT 257 /* hlt instruction reached */
#define EXCP_DEBUG 258 /* cpu stopped after a breakpoint or singlestep */
#define MAX_BREAKPOINTS 32
enum {
CC_OP_DYNAMIC, /* must use dynamic code to get cc_op */
CC_OP_EFLAGS, /* all cc are explicitely computed, CC_SRC = flags */
......@@ -257,7 +250,8 @@ typedef struct CPUX86State {
SegmentCache gdt; /* only base and limit are used */
SegmentCache idt; /* only base and limit are used */
int cpl; /* current cpl */
int soft_mmu; /* TRUE if soft mmu is being used */
/* sysenter registers */
uint32_t sysenter_cs;
uint32_t sysenter_esp;
......@@ -275,10 +269,16 @@ typedef struct CPUX86State {
int interrupt_request;
int user_mode_only; /* user mode only simulation */
/* soft mmu support */
/* 0 = kernel, 1 = user */
CPUTLBEntry tlb_read[2][CPU_TLB_SIZE];
CPUTLBEntry tlb_write[2][CPU_TLB_SIZE];
/* ice debug support */
uint32_t breakpoints[MAX_BREAKPOINTS];
int nb_breakpoints;
int singlestep_enabled;
/* user data */
void *opaque;
} CPUX86State;
......
......@@ -138,6 +138,7 @@ void cpu_x86_update_cr0(CPUX86State *env);
void cpu_x86_update_cr3(CPUX86State *env);
void cpu_x86_flush_tlb(CPUX86State *env, uint32_t addr);
int cpu_x86_handle_mmu_fault(CPUX86State *env, uint32_t addr, int is_write);
void tlb_fill(unsigned long addr, int is_write, void *retaddr);
void __hidden cpu_lock(void);
void __hidden cpu_unlock(void);
void do_interrupt(int intno, int is_int, int error_code,
......@@ -364,3 +365,52 @@ static inline void load_eflags(int eflags, int update_mask)
env->eflags = (env->eflags & ~update_mask) |
(eflags & update_mask);
}
/* memory access macros */
#define ldul ldl
#define lduq ldq
#define ldul_user ldl_user
#define ldul_kernel ldl_kernel
#define ldub_raw ldub
#define ldsb_raw ldsb
#define lduw_raw lduw
#define ldsw_raw ldsw
#define ldl_raw ldl
#define ldq_raw ldq
#define stb_raw stb
#define stw_raw stw
#define stl_raw stl
#define stq_raw stq
#define MEMUSER 0
#define DATA_SIZE 1
#include "softmmu_header.h"
#define DATA_SIZE 2
#include "softmmu_header.h"
#define DATA_SIZE 4
#include "softmmu_header.h"
#define DATA_SIZE 8
#include "softmmu_header.h"
#undef MEMUSER
#define MEMUSER 1
#define DATA_SIZE 1
#include "softmmu_header.h"
#define DATA_SIZE 2
#include "softmmu_header.h"
#define DATA_SIZE 4
#include "softmmu_header.h"
#define DATA_SIZE 8
#include "softmmu_header.h"
#undef MEMUSER
......@@ -68,6 +68,7 @@ typedef struct PageDesc {
#define L2_SIZE (1 << L2_BITS)
static void tb_invalidate_page(unsigned long address);
static void io_mem_init(void);
unsigned long real_host_page_size;
unsigned long host_page_bits;
......@@ -76,6 +77,12 @@ unsigned long host_page_mask;
static PageDesc *l1_map[L1_SIZE];
/* io memory support */
static unsigned long *l1_physmap[L1_SIZE];
CPUWriteMemoryFunc *io_mem_write[IO_MEM_NB_ENTRIES][4];
CPUReadMemoryFunc *io_mem_read[IO_MEM_NB_ENTRIES][4];
static int io_mem_nb;
static void page_init(void)
{
/* NOTE: we can always suppose that host_page_size >=
......@@ -201,6 +208,7 @@ void cpu_exec_init(void)
if (!code_gen_ptr) {
code_gen_ptr = code_gen_buffer;
page_init();
io_mem_init();
}
}
......@@ -744,3 +752,133 @@ void page_unmap(void)
tb_flush();
}
#endif
void tlb_flush(CPUState *env)
{
#if defined(TARGET_I386)
int i;
for(i = 0; i < CPU_TLB_SIZE; i++) {
env->tlb_read[0][i].address = -1;
env->tlb_write[0][i].address = -1;
env->tlb_read[1][i].address = -1;
env->tlb_write[1][i].address = -1;
}
#endif
}
void tlb_flush_page(CPUState *env, uint32_t addr)
{
#if defined(TARGET_I386)
int i;
i = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1);
env->tlb_read[0][i].address = -1;
env->tlb_write[0][i].address = -1;
env->tlb_read[1][i].address = -1;
env->tlb_write[1][i].address = -1;
#endif
}
static inline unsigned long *physpage_find_alloc(unsigned int page)
{
unsigned long **lp, *p;
unsigned int index, i;
index = page >> TARGET_PAGE_BITS;
lp = &l1_physmap[index >> L2_BITS];
p = *lp;
if (!p) {
/* allocate if not found */
p = malloc(sizeof(unsigned long) * L2_SIZE);
for(i = 0; i < L2_SIZE; i++)
p[i] = IO_MEM_UNASSIGNED;
*lp = p;
}
return p + (index & (L2_SIZE - 1));
}
/* return NULL if no page defined (unused memory) */
unsigned long physpage_find(unsigned long page)
{
unsigned long *p;
unsigned int index;
index = page >> TARGET_PAGE_BITS;
p = l1_physmap[index >> L2_BITS];
if (!p)
return IO_MEM_UNASSIGNED;
return p[index & (L2_SIZE - 1)];
}
/* register physical memory. 'size' must be a multiple of the target
page size. If (phys_offset & ~TARGET_PAGE_MASK) != 0, then it is an
io memory page */
void cpu_register_physical_memory(unsigned long start_addr, unsigned long size,
long phys_offset)
{
unsigned long addr, end_addr;
unsigned long *p;
end_addr = start_addr + size;
for(addr = start_addr; addr < end_addr; addr += TARGET_PAGE_SIZE) {
p = physpage_find_alloc(addr);
*p = phys_offset;
if ((phys_offset & ~TARGET_PAGE_MASK) == 0)
phys_offset += TARGET_PAGE_SIZE;
}
}
static uint32_t unassigned_mem_readb(uint32_t addr)
{
return 0;
}
static void unassigned_mem_writeb(uint32_t addr, uint32_t val)
{
}
static CPUReadMemoryFunc *unassigned_mem_read[3] = {
unassigned_mem_readb,
unassigned_mem_readb,
unassigned_mem_readb,
};
static CPUWriteMemoryFunc *unassigned_mem_write[3] = {
unassigned_mem_writeb,
unassigned_mem_writeb,
unassigned_mem_writeb,
};
static void io_mem_init(void)
{
io_mem_nb = 1;
cpu_register_io_memory(0, unassigned_mem_read, unassigned_mem_write);
}
/* mem_read and mem_write are arrays of functions containing the
function to access byte (index 0), word (index 1) and dword (index
2). All functions must be supplied. If io_index is non zero, the
corresponding io zone is modified. If it is zero, a new io zone is
allocated. The return value can be used with
cpu_register_physical_memory(). (-1) is returned if error. */
int cpu_register_io_memory(int io_index,
CPUReadMemoryFunc **mem_read,
CPUWriteMemoryFunc **mem_write)
{
int i;
if (io_index <= 0) {
if (io_index >= IO_MEM_NB_ENTRIES)
return -1;
io_index = io_mem_nb++;
} else {
if (io_index >= IO_MEM_NB_ENTRIES)
return -1;
}
for(i = 0;i < 3; i++) {
io_mem_read[io_index][i] = mem_read[i];
io_mem_write[io_index][i] = mem_write[i];
}
return io_index << IO_MEM_SHIFT;
}
......@@ -21,6 +21,17 @@
/* allow to see translation results - the slowdown should be negligible, so we leave it */
#define DEBUG_DISAS
#ifndef glue
#define xglue(x, y) x ## y
#define glue(x, y) xglue(x, y)
#define stringify(s) tostring(s)
#define tostring(s) #s
#endif
#if GCC_MAJOR < 3
#define __builtin_expect(x, n) (x)
#endif
/* is_jmp field values */
#define DISAS_NEXT 0 /* next instruction can be analyzed */
#define DISAS_JUMP 1 /* only pc was modified dynamically */
......@@ -44,14 +55,17 @@ extern uint8_t gen_opc_instr_start[OPC_BUF_SIZE];
#if defined(TARGET_I386)
#define GEN_FLAG_CODE32_SHIFT 0
#define GEN_FLAG_ADDSEG_SHIFT 1
#define GEN_FLAG_SS32_SHIFT 2
#define GEN_FLAG_VM_SHIFT 3
#define GEN_FLAG_ST_SHIFT 4
#define GEN_FLAG_TF_SHIFT 8 /* same position as eflags */
#define GEN_FLAG_CPL_SHIFT 9
#define GEN_FLAG_IOPL_SHIFT 12 /* same position as eflags */
#define GEN_FLAG_CODE32_SHIFT 0
#define GEN_FLAG_ADDSEG_SHIFT 1
#define GEN_FLAG_SS32_SHIFT 2
#define GEN_FLAG_VM_SHIFT 3
#define GEN_FLAG_ST_SHIFT 4
#define GEN_FLAG_TF_SHIFT 8 /* same position as eflags */
#define GEN_FLAG_CPL_SHIFT 9
#define GEN_FLAG_SOFT_MMU_SHIFT 11
#define GEN_FLAG_IOPL_SHIFT 12 /* same position as eflags */
void optimize_flags_init(void);
#endif
......@@ -68,6 +82,8 @@ int cpu_restore_state(struct TranslationBlock *tb,
void cpu_exec_init(void);
int page_unprotect(unsigned long address);
void page_unmap(void);
void tlb_flush_page(CPUState *env, uint32_t addr);
void tlb_flush(CPUState *env);
#define CODE_GEN_MAX_SIZE 65536
#define CODE_GEN_ALIGN 16 /* must be >= of the size of a icache line */
......@@ -230,6 +246,17 @@ dummy_label ## n:\
#endif
/* physical memory access */
#define IO_MEM_NB_ENTRIES 256
#define TLB_INVALID_MASK (1 << 3)
#define IO_MEM_SHIFT 4
#define IO_MEM_UNASSIGNED (1 << IO_MEM_SHIFT)
unsigned long physpage_find(unsigned long page);
extern CPUWriteMemoryFunc *io_mem_write[IO_MEM_NB_ENTRIES][4];
extern CPUReadMemoryFunc *io_mem_read[IO_MEM_NB_ENTRIES][4];
#ifdef __powerpc__
static inline int testandset (int *p)
{
......
......@@ -781,7 +781,7 @@ void helper_lcall_real_T0_T1(int shift, int next_eip)
int new_cs, new_eip;
uint32_t esp, esp_mask;
uint8_t *ssp;
new_cs = T0;
new_eip = T1;
esp = env->regs[R_ESP];
......@@ -1741,3 +1741,34 @@ void helper_frstor(uint8_t *ptr, int data32)
}
}
#define SHIFT 0
#include "softmmu_template.h"
#define SHIFT 1
#include "softmmu_template.h"
#define SHIFT 2
#include "softmmu_template.h"
#define SHIFT 3
#include "softmmu_template.h"
/* try to fill the TLB and return an exception if error */
void tlb_fill(unsigned long addr, int is_write, void *retaddr)
{
TranslationBlock *tb;
int ret;
unsigned long pc;
ret = cpu_x86_handle_mmu_fault(env, addr, is_write);
if (ret) {
/* now we have a real cpu fault */
pc = (unsigned long)retaddr;
tb = tb_find_pc(pc);
if (tb) {
/* the PC is inside the translated code. It means that we have
a virtual CPU fault */
cpu_restore_state(tb, env, pc);
}
raise_exception_err(EXCP0E_PAGE, env->error_code);
}
}
......@@ -376,70 +376,14 @@ void OPPROTO op_andl_A0_ffff(void)
/* memory access */
void OPPROTO op_ldub_T0_A0(void)
{
T0 = ldub((uint8_t *)A0);
}
void OPPROTO op_ldsb_T0_A0(void)
{
T0 = ldsb((int8_t *)A0);
}
void OPPROTO op_lduw_T0_A0(void)
{
T0 = lduw((uint8_t *)A0);
}
void OPPROTO op_ldsw_T0_A0(void)
{
T0 = ldsw((int8_t *)A0);
}
void OPPROTO op_ldl_T0_A0(void)
{
T0 = ldl((uint8_t *)A0);
}
#define MEMSUFFIX
#include "ops_mem.h"
void OPPROTO op_ldub_T1_A0(void)
{
T1 = ldub((uint8_t *)A0);
}
void OPPROTO op_ldsb_T1_A0(void)
{
T1 = ldsb((int8_t *)A0);
}
void OPPROTO op_lduw_T1_A0(void)
{
T1 = lduw((uint8_t *)A0);
}
#define MEMSUFFIX _user
#include "ops_mem.h"
void OPPROTO op_ldsw_T1_A0(void)
{
T1 = ldsw((int8_t *)A0);
}
void OPPROTO op_ldl_T1_A0(void)
{
T1 = ldl((uint8_t *)A0);
}
void OPPROTO op_stb_T0_A0(void)
{
stb((uint8_t *)A0, T0);
}
void OPPROTO op_stw_T0_A0(void)
{
stw((uint8_t *)A0, T0);
}
void OPPROTO op_stl_T0_A0(void)
{
stl((uint8_t *)A0, T0);
}
#define MEMSUFFIX _kernel
#include "ops_mem.h"
/* used for bit operations */
......@@ -635,6 +579,38 @@ void OPPROTO op_movswl_DX_AX(void)
EDX = (EDX & 0xffff0000) | (((int16_t)EAX >> 15) & 0xffff);
}
/* string ops helpers */
void OPPROTO op_addl_ESI_T0(void)
{
ESI += T0;
}
void OPPROTO op_addw_ESI_T0(void)
{
ESI = (ESI & ~0xffff) | ((ESI + T0) & 0xffff);
}
void OPPROTO op_addl_EDI_T0(void)
{
EDI += T0;
}
void OPPROTO op_addw_EDI_T0(void)
{
EDI = (EDI & ~0xffff) | ((EDI + T0) & 0xffff);
}
void OPPROTO op_decl_ECX(void)
{
ECX--;
}
void OPPROTO op_decw_ECX(void)
{
ECX = (ECX & ~0xffff) | ((ECX - 1) & 0xffff);
}
/* push/pop */
void op_pushl_T0(void)
......
void OPPROTO glue(glue(op_movs, SUFFIX), STRING_SUFFIX)(void)
{
int v, inc;
v = glue(ldu, SUFFIX)(SI_ADDR);
glue(st, SUFFIX)(DI_ADDR, v);
inc = (DF << SHIFT);
INC_SI();
INC_DI();
}
void OPPROTO glue(glue(op_rep_movs, SUFFIX), STRING_SUFFIX)(void)
{
int v, inc;
inc = (DF << SHIFT);
while (CX != 0) {
v = glue(ldu, SUFFIX)(SI_ADDR);
glue(st, SUFFIX)(DI_ADDR, v);
INC_SI();
INC_DI();
DEC_CX();
}
FORCE_RET();
}
void OPPROTO glue(glue(op_stos, SUFFIX), STRING_SUFFIX)(void)
{
int inc;
glue(st, SUFFIX)(DI_ADDR, EAX);
inc = (DF << SHIFT);
INC_DI();
}
void OPPROTO glue(glue(op_rep_stos, SUFFIX), STRING_SUFFIX)(void)
{
int inc;
inc = (DF << SHIFT);
while (CX != 0) {
glue(st, SUFFIX)(DI_ADDR, EAX);
INC_DI();
DEC_CX();
}
FORCE_RET();
}
void OPPROTO glue(glue(op_lods, SUFFIX), STRING_SUFFIX)(void)
{
int v, inc;
v = glue(ldu, SUFFIX)(SI_ADDR);
#if SHIFT == 0
EAX = (EAX & ~0xff) | v;
#elif SHIFT == 1
EAX = (EAX & ~0xffff) | v;
#else
EAX = v;
#endif
inc = (DF << SHIFT);
INC_SI();
}
/* don't know if it is used */
void OPPROTO glue(glue(op_rep_lods, SUFFIX), STRING_SUFFIX)(void)
{
int v, inc;
inc = (DF << SHIFT);
while (CX != 0) {
v = glue(ldu, SUFFIX)(SI_ADDR);
#if SHIFT == 0
EAX = (EAX & ~0xff) | v;
#elif SHIFT == 1
EAX = (EAX & ~0xffff) | v;
#else
EAX = v;
#endif
INC_SI();
DEC_CX();
}
FORCE_RET();
}
void OPPROTO glue(glue(op_scas, SUFFIX), STRING_SUFFIX)(void)
{
int v, inc;
v = glue(ldu, SUFFIX)(DI_ADDR);
inc = (DF << SHIFT);
INC_DI();
CC_SRC = v;
CC_DST = EAX - v;
}
void OPPROTO glue(glue(op_repz_scas, SUFFIX), STRING_SUFFIX)(void)
{
int v1, v2, inc;
......@@ -133,18 +43,6 @@ void OPPROTO glue(glue(op_repnz_scas, SUFFIX), STRING_SUFFIX)(void)
FORCE_RET();
}
void OPPROTO glue(glue(op_cmps, SUFFIX), STRING_SUFFIX)(void)
{
int v1, v2, inc;
v1 = glue(ldu, SUFFIX)(SI_ADDR);