nkeynes@939: /** nkeynes@940: * $Id$ nkeynes@939: * nkeynes@939: * x86-specific MMU code - this emits simple TLB stubs for TLB indirection. nkeynes@939: * nkeynes@939: * Copyright (c) 2008 Nathan Keynes. nkeynes@939: * nkeynes@939: * This program is free software; you can redistribute it and/or modify nkeynes@939: * it under the terms of the GNU General Public License as published by nkeynes@939: * the Free Software Foundation; either version 2 of the License, or nkeynes@939: * (at your option) any later version. nkeynes@939: * nkeynes@939: * This program is distributed in the hope that it will be useful, nkeynes@939: * but WITHOUT ANY WARRANTY; without even the implied warranty of nkeynes@939: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the nkeynes@939: * GNU General Public License for more details. nkeynes@939: */ nkeynes@939: nkeynes@939: #include "lxdream.h" nkeynes@939: #include "mem.h" nkeynes@939: #include "sh4/sh4core.h" nkeynes@939: #include "sh4/sh4mmio.h" nkeynes@939: #include "sh4/sh4trans.h" nkeynes@939: #include "sh4/mmu.h" nkeynes@939: #include "sh4/x86op.h" nkeynes@939: nkeynes@939: #if SIZEOF_VOID_P == 8 nkeynes@939: #define ARG1 R_EDI nkeynes@939: #define ARG2 R_ESI nkeynes@939: #define DECODE() \ nkeynes@939: MOV_imm64_r32((uintptr_t)ext_address_space, R_EAX); /* movq ptr, %rax */ \ nkeynes@939: REXW(); OP(0x8B); OP(0x0C); OP(0xC8) /* movq [%rax + %rcx*8], %rcx */ nkeynes@939: #else nkeynes@939: #define ARG1 R_EAX nkeynes@939: #define ARG2 R_EDX nkeynes@939: #define DECODE() \ nkeynes@939: MOV_r32disp32x4_r32( R_ECX, (uintptr_t)ext_address_space, R_ECX ); nkeynes@939: #endif nkeynes@939: nkeynes@939: void mmu_utlb_init_vtable( struct utlb_entry *ent, struct utlb_page_entry *page, gboolean writable ) nkeynes@939: { nkeynes@939: uint32_t mask = ent->mask; nkeynes@939: uint32_t ppn = ent->ppn & mask; nkeynes@939: int inc = writable ? 1 : 2; nkeynes@939: int i; nkeynes@939: nkeynes@939: xlat_output = page->code; nkeynes@939: uint8_t **fn = (uint8_t **)ext_address_space[ppn>>12]; nkeynes@939: uint8_t **out = (uint8_t **)&page->fn; nkeynes@939: nkeynes@939: for( i=0; i<8; i+= inc, fn += inc, out += inc ) { nkeynes@939: *out = xlat_output; nkeynes@939: #if SIZEOF_VOID_P == 8 nkeynes@939: MOV_imm64_r32((uintptr_t)&mmu_urc, R_EAX ); nkeynes@939: OP(0x83); OP(0x00); OP(0x01); // ADD #1, [RAX] nkeynes@939: #else nkeynes@939: OP(0x83); MODRM_r32_disp32(0, (uintptr_t)&mmu_urc); OP(0x01); // ADD #1, mmu_urc nkeynes@939: #endif nkeynes@939: AND_imm32_r32( ~mask, ARG1 ); // 6 nkeynes@939: OR_imm32_r32( ppn, ARG1 ); // 6 nkeynes@939: if( ent->mask >= 0xFFFFF000 ) { nkeynes@939: // Maps to a single page, so jump directly there nkeynes@939: int rel = (*fn - xlat_output); nkeynes@939: JMP_rel( rel ); // 5 nkeynes@939: } else { nkeynes@939: MOV_r32_r32( ARG1, R_ECX ); // 2 nkeynes@939: SHR_imm8_r32( 12, R_ECX ); // 3 nkeynes@939: DECODE(); // 14 nkeynes@939: JMP_r32disp8(R_ECX, (((uintptr_t)out) - ((uintptr_t)&page->fn)) ); // 3 nkeynes@939: } nkeynes@939: } nkeynes@939: } nkeynes@939: nkeynes@939: void mmu_utlb_1k_init_vtable( struct utlb_1k_entry *entry ) nkeynes@939: { nkeynes@939: xlat_output = entry->code; nkeynes@939: int i; nkeynes@939: uint8_t **out = (uint8_t **)&entry->fn; nkeynes@939: nkeynes@939: for( i=0; i<8; i++, out++ ) { nkeynes@939: *out = xlat_output; nkeynes@939: MOV_r32_r32( ARG1, R_ECX ); nkeynes@939: SHR_imm8_r32( 10, R_ECX ); nkeynes@939: AND_imm8s_r32( 0x3, R_ECX ); nkeynes@939: #if SIZEOF_VOID_P == 8 nkeynes@939: MOV_imm64_r32( (uintptr_t)&entry->subpages[0], R_EAX ); nkeynes@939: REXW(); OP(0x8B); OP(0x0C); OP(0xC8); /* movq [%rax + %rcx*8], %rcx */ nkeynes@939: #else nkeynes@939: MOV_r32disp32x4_r32( R_ECX, ((uintptr_t)&entry->subpages[0]), R_ECX ); nkeynes@939: #endif nkeynes@939: JMP_r32disp8(R_ECX, (((uintptr_t)out) - ((uintptr_t)&entry->fn)) ); // 3 nkeynes@939: } nkeynes@939: nkeynes@939: out = (uint8_t **)&entry->user_fn; nkeynes@939: for( i=0; i<8; i++, out++ ) { nkeynes@939: *out = xlat_output; nkeynes@939: MOV_r32_r32( ARG1, R_ECX ); nkeynes@939: SHR_imm8_r32( 10, R_ECX ); nkeynes@939: AND_imm8s_r32( 0x3, R_ECX ); nkeynes@939: #if SIZEOF_VOID_P == 8 nkeynes@939: MOV_imm64_r32( (uintptr_t)&entry->user_subpages[0], R_EAX ); nkeynes@939: REXW(); OP(0x8B); OP(0x0C); OP(0xC8); /* movq [%rax + %rcx*8], %rcx */ nkeynes@939: #else nkeynes@939: MOV_r32disp32x4_r32( R_ECX, ((uintptr_t)&entry->user_subpages[0]), R_ECX ); nkeynes@939: #endif nkeynes@939: JMP_r32disp8(R_ECX, (((uintptr_t)out) - ((uintptr_t)&entry->user_fn)) ); // 3 nkeynes@939: } nkeynes@939: nkeynes@939: }