From: Tim Newsome Date: Wed, 4 May 2016 01:53:16 +0000 (-0700) Subject: Walk page tables to translate addresses. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=f9e7a3f1e1058b8d54d15d9ebcecd7e978d5deb9;p=riscv-isa-sim.git Walk page tables to translate addresses. --- diff --git a/riscv/gdbserver.cc b/riscv/gdbserver.cc index fb99c11..d7cf162 100644 --- a/riscv/gdbserver.cc +++ b/riscv/gdbserver.cc @@ -264,6 +264,7 @@ class halt_op_t : public operation_t // trying to keep The patterns here usable for 32-bit ISAs as well. (On a // 32-bit ISA 8 words are required, while the minimum Debug RAM size is 7 // words.) + return false; case 1: gs.saved_dpc = ((uint64_t) gs.read_debug_ram(1) << 32) | gs.read_debug_ram(0); @@ -277,14 +278,17 @@ class halt_op_t : public operation_t gs.write_debug_ram(5, sd(S0, 0, (uint16_t) DEBUG_RAM_START + 16)); gs.write_debug_ram(6, jal(0, (uint32_t) (DEBUG_ROM_RESUME - (DEBUG_RAM_START + 4*6)))); gs.set_interrupt(0); + return false; case 2: gs.saved_mcause = ((uint64_t) gs.read_debug_ram(1) << 32) | gs.read_debug_ram(0); gs.saved_mstatus = ((uint64_t) gs.read_debug_ram(3) << 32) | gs.read_debug_ram(2); gs.dcsr = ((uint64_t) gs.read_debug_ram(5) << 32) | gs.read_debug_ram(4); + + gs.sptbr_valid = false; + gs.pte_cache.clear(); return true; } - return false; } }; @@ -303,6 +307,7 @@ class continue_op_t : public operation_t gs.write_debug_ram(4, gs.saved_dpc); gs.write_debug_ram(5, gs.saved_dpc >> 32); gs.set_interrupt(0); + return false; case 1: gs.write_debug_ram(0, ld(S0, 0, (uint16_t) DEBUG_RAM_START+16)); @@ -311,6 +316,7 @@ class continue_op_t : public operation_t gs.write_debug_ram(4, gs.saved_mbadaddr); gs.write_debug_ram(5, gs.saved_mbadaddr >> 32); gs.set_interrupt(0); + return false; case 2: gs.write_debug_ram(0, ld(S0, 0, (uint16_t) DEBUG_RAM_START+16)); @@ -319,6 +325,7 @@ class continue_op_t : public operation_t gs.write_debug_ram(4, gs.saved_mstatus); gs.write_debug_ram(5, gs.saved_mstatus >> 32); gs.set_interrupt(0); + return false; case 3: gs.write_debug_ram(0, ld(S0, 0, (uint16_t) DEBUG_RAM_START+16)); @@ -454,14 +461,15 @@ class register_read_op_t : public operation_t class memory_read_op_t : public operation_t { public: - memory_read_op_t(gdbserver_t& gdbserver, reg_t addr, unsigned int length) : - operation_t(gdbserver), addr(addr), length(length) {}; + memory_read_op_t(gdbserver_t& gdbserver, reg_t vaddr, unsigned int length) : + operation_t(gdbserver), vaddr(vaddr), length(length) {}; bool perform_step(unsigned int step) { if (step == 0) { // address goes in S0 - access_size = (addr % length); + paddr = gs.translate(vaddr); + access_size = (paddr % length); if (access_size == 0) access_size = length; @@ -485,8 +493,8 @@ class memory_read_op_t : public operation_t } gs.write_debug_ram(2, sd(S1, 0, (uint16_t) DEBUG_RAM_START + 24)); gs.write_debug_ram(3, jal(0, (uint32_t) (DEBUG_ROM_RESUME - (DEBUG_RAM_START + 4*3)))); - gs.write_debug_ram(4, addr); - gs.write_debug_ram(5, addr >> 32); + gs.write_debug_ram(4, paddr); + gs.write_debug_ram(5, paddr >> 32); gs.set_interrupt(0); gs.start_packet(); @@ -501,21 +509,21 @@ class memory_read_op_t : public operation_t value >>= 8; } length -= access_size; - addr += access_size; + paddr += access_size; if (length == 0) { gs.end_packet(); return true; } else { - gs.write_debug_ram(4, addr); - gs.write_debug_ram(5, addr >> 32); + gs.write_debug_ram(4, paddr); + gs.write_debug_ram(5, paddr >> 32); gs.set_interrupt(0); return false; } } private: - reg_t addr; + reg_t vaddr, paddr; unsigned int length; unsigned int access_size; }; @@ -523,9 +531,9 @@ class memory_read_op_t : public operation_t class memory_write_op_t : public operation_t { public: - memory_write_op_t(gdbserver_t& gdbserver, reg_t addr, unsigned int length, + memory_write_op_t(gdbserver_t& gdbserver, reg_t vaddr, unsigned int length, unsigned char *data) : - operation_t(gdbserver), addr(addr), offset(0), length(length), data(data) {}; + operation_t(gdbserver), vaddr(vaddr), offset(0), length(length), data(data) {}; ~memory_write_op_t() { delete[] data; @@ -533,9 +541,10 @@ class memory_write_op_t : public operation_t bool perform_step(unsigned int step) { + reg_t paddr = gs.translate(vaddr); if (step == 0) { // address goes in S0 - access_size = (addr % length); + access_size = (paddr % length); if (access_size == 0) access_size = length; @@ -570,8 +579,8 @@ class memory_write_op_t : public operation_t return true; } gs.write_debug_ram(3, jal(0, (uint32_t) (DEBUG_ROM_RESUME - (DEBUG_RAM_START + 4*3)))); - gs.write_debug_ram(4, addr); - gs.write_debug_ram(5, addr >> 32); + gs.write_debug_ram(4, paddr); + gs.write_debug_ram(5, paddr >> 32); gs.set_interrupt(0); return false; @@ -582,37 +591,37 @@ class memory_write_op_t : public operation_t gs.send_packet("OK"); return true; } else { - const unsigned char *d = data + offset; - switch (access_size) { - case 1: - gs.write_debug_ram(6, d[0]); - break; - case 2: - gs.write_debug_ram(6, d[0] | (d[1] << 8)); - break; - case 4: - gs.write_debug_ram(6, d[0] | (d[1] << 8) | - (d[2] << 16) | (d[3] << 24)); - break; - case 8: - gs.write_debug_ram(6, d[0] | (d[1] << 8) | - (d[2] << 16) | (d[3] << 24)); - gs.write_debug_ram(7, d[4] | (d[5] << 8) | - (d[6] << 16) | (d[7] << 24)); - break; - default: - gs.send_packet("E12"); - return true; - } - gs.write_debug_ram(4, addr + offset); - gs.write_debug_ram(5, (addr + offset) >> 32); + const unsigned char *d = data + offset; + switch (access_size) { + case 1: + gs.write_debug_ram(6, d[0]); + break; + case 2: + gs.write_debug_ram(6, d[0] | (d[1] << 8)); + break; + case 4: + gs.write_debug_ram(6, d[0] | (d[1] << 8) | + (d[2] << 16) | (d[3] << 24)); + break; + case 8: + gs.write_debug_ram(6, d[0] | (d[1] << 8) | + (d[2] << 16) | (d[3] << 24)); + gs.write_debug_ram(7, d[4] | (d[5] << 8) | + (d[6] << 16) | (d[7] << 24)); + break; + default: + gs.send_packet("E12"); + return true; + } + gs.write_debug_ram(4, paddr + offset); + gs.write_debug_ram(5, (paddr + offset) >> 32); gs.set_interrupt(0); return false; } } private: - reg_t addr; + reg_t vaddr; unsigned int offset; unsigned int length; unsigned int access_size; @@ -626,11 +635,11 @@ class collect_translation_info_op_t : public operation_t // that it's possible to translate vaddr, vaddr+length, and all addresses // in between to physical addresses. collect_translation_info_op_t(gdbserver_t& gdbserver, reg_t vaddr, size_t length) : - operation_t(gdbserver), vaddr(vaddr), length(length) {}; + operation_t(gdbserver), state(STATE_START), vaddr(vaddr), length(length) {}; bool perform_step(unsigned int step) { - unsigned int vm = get_field(gs.saved_mstatus, MSTATUS_VM); + unsigned int vm = gs.virtual_memory(); if (step == 0) { switch (vm) { @@ -638,6 +647,22 @@ class collect_translation_info_op_t : public operation_t // Nothing to be done. return true; + case VM_SV32: + levels = 2; + ptidxbits = 10; + ptesize = 4; + break; + case VM_SV39: + levels = 3; + ptidxbits = 9; + ptesize = 8; + break; + case VM_SV48: + levels = 4; + ptidxbits = 9; + ptesize = 8; + break; + default: { char buf[100]; @@ -647,12 +672,87 @@ class collect_translation_info_op_t : public operation_t } } } + + // Perform any reads from the just-completed action. + switch (state) { + case STATE_START: + break; + case STATE_READ_SPTBR: + gs.sptbr = ((uint64_t) gs.read_debug_ram(5) << 32) | gs.read_debug_ram(4); + gs.sptbr_valid = true; + break; + case STATE_READ_PTE: + gs.pte_cache[pte_addr] = ((uint64_t) gs.read_debug_ram(5) << 32) | + gs.read_debug_ram(4); + fprintf(stderr, "pte_cache[0x%lx] = 0x%lx\n", pte_addr, gs.pte_cache[pte_addr]); + break; + } + + // Set up the next action. + // We only get here for VM_SV32/39/38. + + if (!gs.sptbr_valid) { + state = STATE_READ_SPTBR; + gs.write_debug_ram(0, csrr(S0, CSR_SPTBR)); + gs.write_debug_ram(1, sd(S0, 0, (uint16_t) DEBUG_RAM_START + 16)); + gs.write_debug_ram(2, jal(0, (uint32_t) (DEBUG_ROM_RESUME - (DEBUG_RAM_START + 4*2)))); + gs.set_interrupt(0); + return false; + } + + reg_t base = gs.sptbr << PGSHIFT; + int ptshift = (levels - 1) * ptidxbits; + for (unsigned int i = 0; i < levels; i++, ptshift -= ptidxbits) { + reg_t idx = (vaddr >> (PGSHIFT + ptshift)) & ((1 << ptidxbits) - 1); + + pte_addr = base + idx * ptesize; + auto it = gs.pte_cache.find(pte_addr); + if (it == gs.pte_cache.end()) { + state = STATE_READ_PTE; + if (ptesize == 4) { + gs.write_debug_ram(0, lw(S0, 0, (uint16_t) DEBUG_RAM_START + 16)); + gs.write_debug_ram(1, lw(S1, S0, 0)); + gs.write_debug_ram(2, sd(S1, 0, (uint16_t) DEBUG_RAM_START + 16)); + } else { + gs.write_debug_ram(0, ld(S0, 0, (uint16_t) DEBUG_RAM_START + 16)); + gs.write_debug_ram(1, ld(S1, S0, 0)); + gs.write_debug_ram(2, sd(S1, 0, (uint16_t) DEBUG_RAM_START + 16)); + } + gs.write_debug_ram(3, jal(0, (uint32_t) (DEBUG_ROM_RESUME - (DEBUG_RAM_START + 4*3)))); + gs.write_debug_ram(4, pte_addr); + gs.write_debug_ram(5, pte_addr >> 32); + gs.set_interrupt(0); + return false; + } + + reg_t pte = gs.pte_cache[pte_addr]; + reg_t ppn = pte >> PTE_PPN_SHIFT; + + if (PTE_TABLE(pte)) { // next level of page table + base = ppn << PGSHIFT; + } else { + // We've collected all the data required for the translation. + return true; + } + } + fprintf(stderr, + "ERROR: gdbserver couldn't find appropriate PTEs to translate 0x%lx\n", + vaddr); return true; } private: + enum { + STATE_START, + STATE_READ_SPTBR, + STATE_READ_PTE + } state; reg_t vaddr; size_t length; + unsigned int levels; + unsigned int ptidxbits; + unsigned int ptesize; + reg_t pte_addr; }; ////////////////////////////// gdbserver itself @@ -693,6 +793,92 @@ gdbserver_t::gdbserver_t(uint16_t port, sim_t *sim) : } } +reg_t gdbserver_t::translate(reg_t vaddr) +{ + unsigned int vm = virtual_memory(); + unsigned int levels, ptidxbits, ptesize; + + switch (vm) { + case VM_MBARE: + return vaddr; + + case VM_SV32: + levels = 2; + ptidxbits = 10; + ptesize = 4; + break; + case VM_SV39: + levels = 3; + ptidxbits = 9; + ptesize = 8; + break; + case VM_SV48: + levels = 4; + ptidxbits = 9; + ptesize = 8; + break; + + default: + { + char buf[100]; + sprintf(buf, "VM mode %d is not supported by gdbserver.cc.", vm); + die(buf); + return true; // die doesn't return, but gcc doesn't know that. + } + } + + // Handle page tables here. There's a bunch of duplicated code with + // collect_translation_info_op_t. :-( + reg_t base = sptbr << PGSHIFT; + int ptshift = (levels - 1) * ptidxbits; + for (unsigned int i = 0; i < levels; i++, ptshift -= ptidxbits) { + reg_t idx = (vaddr >> (PGSHIFT + ptshift)) & ((1 << ptidxbits) - 1); + + reg_t pte_addr = base + idx * ptesize; + auto it = pte_cache.find(pte_addr); + if (it == pte_cache.end()) { + fprintf(stderr, "ERROR: gdbserver tried to translate 0x%lx without first " + "collecting the relevant PTEs.\n", vaddr); + die("gdbserver_t::translate()"); + } + + reg_t pte = pte_cache[pte_addr]; + reg_t ppn = pte >> PTE_PPN_SHIFT; + + if (PTE_TABLE(pte)) { // next level of page table + base = ppn << PGSHIFT; + } else { + // We've collected all the data required for the translation. + reg_t vpn = vaddr >> PGSHIFT; + reg_t paddr = (ppn | (vpn & ((reg_t(1) << ptshift) - 1))) << PGSHIFT; + paddr += vaddr & (PGSIZE-1); + fprintf(stderr, "gdbserver translate 0x%lx -> 0x%lx\n", vaddr, paddr); + return paddr; + } + } + + fprintf(stderr, "ERROR: gdbserver tried to translate 0x%lx but the relevant " + "PTEs are invalid.\n", vaddr); + // TODO: Is it better to throw an exception here? + return -1; +} + +unsigned int gdbserver_t::privilege_mode() +{ + unsigned int mode = get_field(dcsr, DCSR_PRV); + if (get_field(saved_mstatus, MSTATUS_MPRV)) + mode = get_field(saved_mstatus, MSTATUS_MPP); + return mode; +} + +unsigned int gdbserver_t::virtual_memory() +{ + unsigned int mode = privilege_mode(); + if (mode == PRV_M) + return VM_MBARE; + return get_field(saved_mstatus, MSTATUS_VM); +} + void gdbserver_t::write_debug_ram(unsigned int index, uint32_t value) { sim->debug_module.ram_write32(index, value); @@ -998,6 +1184,7 @@ void gdbserver_t::handle_memory_read(const std::vector &packet) if (*iter != '#') return send_packet("E11"); + add_operation(new collect_translation_info_op_t(*this, address, length)); add_operation(new memory_read_op_t(*this, address, length)); } @@ -1029,6 +1216,7 @@ void gdbserver_t::handle_memory_binary_write(const std::vector &packet) if (*iter != '#') return send_packet("E4b"); // EOVERFLOW + add_operation(new collect_translation_info_op_t(*this, address, length)); add_operation(new memory_write_op_t(*this, address, length, data)); } diff --git a/riscv/gdbserver.h b/riscv/gdbserver.h index 41d1c93..9b39450 100644 --- a/riscv/gdbserver.h +++ b/riscv/gdbserver.h @@ -1,6 +1,7 @@ #ifndef _RISCV_GDBSERVER_H #define _RISCV_GDBSERVER_H +#include #include #include @@ -142,6 +143,18 @@ public: reg_t saved_mcause; reg_t saved_mstatus; reg_t dcsr; + reg_t sptbr; + bool sptbr_valid; + + std::map pte_cache; + + reg_t translate(reg_t vaddr); + // Return the PRV_x that is used when the code under debug performs a memory + // access. + unsigned int privilege_mode(); + // Return the VM_x that is used when the code under debug performs a memory + // access. + unsigned int virtual_memory(); private: sim_t *sim; diff --git a/riscv/mmu.cc b/riscv/mmu.cc index df6770f..44ff97a 100644 --- a/riscv/mmu.cc +++ b/riscv/mmu.cc @@ -44,8 +44,6 @@ reg_t mmu_t::translate(reg_t addr, access_type type) if (get_field(proc->state.mstatus, MSTATUS_VM) == VM_MBARE) mode = PRV_M; - fprintf(stderr, "translate(0x%lx, %d), mstatus=0x%lx, prv=%ld, mode=%ld, pum=%d\n", - addr, type, proc->state.mstatus, proc->state.prv, mode, pum); if (mode == PRV_M) { reg_t msb_mask = (reg_t(2) << (proc->xlen-1))-1; // zero-extend from xlen return addr & msb_mask; @@ -149,6 +147,7 @@ reg_t mmu_t::walk(reg_t addr, access_type type, bool supervisor, bool pum) void* ppte = sim->addr_to_mem(pte_addr); reg_t pte = ptesize == 4 ? *(uint32_t*)ppte : *(uint64_t*)ppte; + fprintf(stderr, "walk pte entry 0x%lx = 0x%lx\n", pte_addr, pte); reg_t ppn = pte >> PTE_PPN_SHIFT; if (PTE_TABLE(pte)) { // next level of page table @@ -162,7 +161,9 @@ reg_t mmu_t::walk(reg_t addr, access_type type, bool supervisor, bool pum) *(uint32_t*)ppte |= PTE_R | ((type == STORE) * PTE_D); // for superpage mappings, make a fake leaf PTE for the TLB's benefit. reg_t vpn = addr >> PGSHIFT; - return (ppn | (vpn & ((reg_t(1) << ptshift) - 1))) << PGSHIFT; + reg_t value = (ppn | (vpn & ((reg_t(1) << ptshift) - 1))) << PGSHIFT; + fprintf(stderr, "walk 0x%lx -> 0x%lx\n", addr, value); + return value; } }