Browse Source

Walk page tables to translate addresses.

pull/39/head
Tim Newsome 10 years ago
parent
commit
f9e7a3f1e1
  1. 272
      riscv/gdbserver.cc
  2. 13
      riscv/gdbserver.h
  3. 7
      riscv/mmu.cc

272
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<uint8_t> &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<uint8_t> &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));
}

13
riscv/gdbserver.h

@ -1,6 +1,7 @@
#ifndef _RISCV_GDBSERVER_H
#define _RISCV_GDBSERVER_H
#include <map>
#include <queue>
#include <stdint.h>
@ -142,6 +143,18 @@ public:
reg_t saved_mcause;
reg_t saved_mstatus;
reg_t dcsr;
reg_t sptbr;
bool sptbr_valid;
std::map<reg_t, reg_t> 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;

7
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;
}
}

Loading…
Cancel
Save