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@991: #include "xlat/x86/x86op.h" nkeynes@939: nkeynes@939: #if SIZEOF_VOID_P == 8 nkeynes@991: #define XLAT(addr_space, reg) \ nkeynes@991: MOVQ_imm64_r64( (uintptr_t)addr_space, REG_RAX ); \ nkeynes@991: MOVP_sib_rptr( 3, reg, REG_RAX, 0, reg ); nkeynes@991: #define ADDP_imms_ptr(imm,p) \ nkeynes@991: MOVQ_imm64_r64((uintptr_t)p, REG_EAX ); \ nkeynes@991: ADDL_imms_r32disp(imm, REG_EAX, 0); nkeynes@939: #else nkeynes@991: #define XLAT(addr_space, reg) \ nkeynes@991: MOVP_sib_rptr( 2, reg, -1, (uintptr_t)addr_space, reg ); nkeynes@991: #define ADDP_imms_ptr(imm,p) \ nkeynes@991: ADDL_imms_r32disp(imm, -1, (uintptr_t)p); 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@942: uint32_t vpn = ent->vpn & mask; nkeynes@939: uint32_t ppn = ent->ppn & mask; nkeynes@972: struct mem_region_fn **addr_space; nkeynes@972: uint8_t **out = (uint8_t **)&page->fn; nkeynes@972: uint8_t **fn; nkeynes@939: int inc = writable ? 1 : 2; nkeynes@939: int i; nkeynes@939: nkeynes@939: xlat_output = page->code; nkeynes@972: if( (ppn & 0x1FFFFFFF) >= 0x1C000000 ) { nkeynes@972: /* SH4 control region */ nkeynes@972: ppn |= 0xE0000000; nkeynes@972: addr_space = sh4_address_space; nkeynes@972: } else { nkeynes@972: addr_space = ext_address_space; nkeynes@972: } nkeynes@972: fn = (uint8_t **)addr_space[ppn>>12]; nkeynes@939: nkeynes@975: for( i=0; i<10; i+= inc, fn += inc, out += inc ) { nkeynes@939: *out = xlat_output; nkeynes@975: if( i != 9 ) { /* read_byte_for_write doesn't increment mmu_urc, everything else does */ nkeynes@991: ADDP_imms_ptr(1, &mmu_urc); nkeynes@975: } nkeynes@995: ADDL_imms_r32( ppn-vpn, REG_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@991: JMP_prerel( rel ); // 5 nkeynes@939: } else { nkeynes@995: MOVL_r32_r32( REG_ARG1, REG_ECX ); // 2 nkeynes@991: SHRL_imm_r32( 12, REG_ECX ); // 3 nkeynes@991: XLAT(addr_space, REG_ECX); // 14 nkeynes@991: JMP_r32disp(REG_ECX, (((uintptr_t)out) - ((uintptr_t)&page->fn)) ); // 3 nkeynes@939: } nkeynes@939: } nkeynes@946: nkeynes@946: page->fn.prefetch = unmapped_prefetch; // FIXME nkeynes@946: } nkeynes@946: nkeynes@946: void mmu_utlb_init_storequeue_vtable( struct utlb_entry *ent, struct utlb_page_entry *page ) nkeynes@946: { nkeynes@946: uint32_t mask = ent->mask; nkeynes@946: uint32_t vpn = ent->vpn & mask; nkeynes@946: uint32_t ppn = ent->ppn & mask; nkeynes@946: nkeynes@946: xlat_output = page->code; nkeynes@946: nkeynes@946: memcpy( page, &p4_region_storequeue, sizeof(struct mem_region_fn) ); nkeynes@946: nkeynes@946: page->fn.prefetch = (mem_prefetch_fn_t)xlat_output; nkeynes@991: ADDP_imms_ptr(1, &mmu_urc); nkeynes@995: ADDL_imms_r32( ppn-vpn, REG_ARG1 ); nkeynes@946: int rel = ((uint8_t *)ccn_storequeue_prefetch_tlb) - xlat_output; nkeynes@991: JMP_prerel( rel ); 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@946: for( i=0; i<9; i++, out++ ) { nkeynes@939: *out = xlat_output; nkeynes@995: MOVL_r32_r32( REG_ARG1, REG_ECX ); nkeynes@991: SHRL_imm_r32( 10, REG_ECX ); nkeynes@991: ANDL_imms_r32( 0x3, REG_ECX ); nkeynes@991: XLAT( (uintptr_t)&entry->subpages[0], REG_ECX ); nkeynes@991: JMP_r32disp(REG_ECX, (((uintptr_t)out) - ((uintptr_t)&entry->fn)) ); // 3 nkeynes@939: } nkeynes@939: nkeynes@939: out = (uint8_t **)&entry->user_fn; nkeynes@946: for( i=0; i<9; i++, out++ ) { nkeynes@939: *out = xlat_output; nkeynes@995: MOVL_r32_r32( REG_ARG1, REG_ECX ); nkeynes@991: SHRL_imm_r32( 10, REG_ECX ); nkeynes@991: ANDL_imms_r32( 0x3, REG_ECX ); nkeynes@991: XLAT( (uintptr_t)&entry->user_subpages[0], REG_ECX ); nkeynes@991: JMP_r32disp(REG_ECX, (((uintptr_t)out) - ((uintptr_t)&entry->user_fn)) ); // 3 nkeynes@939: } nkeynes@939: nkeynes@939: }