Skip to content

Commit

Permalink
Merge pull request #1149 from zqb-all/read-write-cross-page
Browse files Browse the repository at this point in the history
riscv: fix read/write virtual memory across page boundaries
  • Loading branch information
en-sc authored Oct 28, 2024
2 parents 7b4ad6f + 92d7a57 commit 9ff272e
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 28 deletions.
103 changes: 75 additions & 28 deletions src/target/riscv/riscv.c
Original file line number Diff line number Diff line change
Expand Up @@ -3086,32 +3086,28 @@ static int riscv_virt2phys(struct target *target, target_addr_t virtual, target_
virtual, physical);
}

static int check_virt_memory_access(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, bool is_write)
{
const bool is_misaligned = address % size != 0;
// TODO: This assumes that size of each page is 4 KiB, which is not necessarily the case.
const bool crosses_page_boundary = RISCV_PGBASE(address + size * count - 1) != RISCV_PGBASE(address);
if (is_misaligned && crosses_page_boundary) {
LOG_TARGET_ERROR(target, "Mis-aligned memory %s (address=0x%" TARGET_PRIxADDR ", size=%d, count=%d)"
" would access an element across page boundary. This is not supported.",
is_write ? "write" : "read", address, size, count);
return ERROR_FAIL;
}
return ERROR_OK;
}

static int riscv_read_phys_memory(struct target *target, target_addr_t phys_address,
uint32_t size, uint32_t count, uint8_t *buffer)
{
RISCV_INFO(r);
return r->read_memory(target, phys_address, size, count, buffer, size);
}

static int riscv_read_memory(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, uint8_t *buffer)
{
if (count == 0) {
LOG_TARGET_WARNING(target, "0-length read from 0x%" TARGET_PRIxADDR, address);
return ERROR_OK;
}

target_addr_t physical_addr;
int result = target->type->virt2phys(target, address, &physical_addr);
if (result != ERROR_OK) {
LOG_TARGET_ERROR(target, "Address translation failed.");
return result;
}

RISCV_INFO(r);
return r->read_memory(target, physical_addr, size, count, buffer, size);
}

static int riscv_write_phys_memory(struct target *target, target_addr_t phys_address,
uint32_t size, uint32_t count, const uint8_t *buffer)
{
Expand All @@ -3121,25 +3117,76 @@ static int riscv_write_phys_memory(struct target *target, target_addr_t phys_add
return tt->write_memory(target, phys_address, size, count, buffer);
}

static int riscv_write_memory(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, const uint8_t *buffer)
static int riscv_rw_memory(struct target *target, target_addr_t address, uint32_t size,
uint32_t count, uint8_t *read_buffer, const uint8_t *write_buffer)
{
/* Exactly one of the buffers must be set, the other must be NULL */
assert(!!read_buffer != !!write_buffer);

const bool is_write = write_buffer ? true : false;
if (count == 0) {
LOG_TARGET_WARNING(target, "0-length write to 0x%" TARGET_PRIxADDR, address);
LOG_TARGET_WARNING(target, "0-length %s 0x%" TARGET_PRIxADDR,
is_write ? "write to" : "read from", address);
return ERROR_OK;
}

target_addr_t physical_addr;
int result = target->type->virt2phys(target, address, &physical_addr);
if (result != ERROR_OK) {
LOG_TARGET_ERROR(target, "Address translation failed.");
int mmu_enabled;
int result = riscv_mmu(target, &mmu_enabled);
if (result != ERROR_OK)
return result;
}

RISCV_INFO(r);
struct target_type *tt = get_target_type(target);
if (!tt)
return ERROR_FAIL;
return tt->write_memory(target, physical_addr, size, count, buffer);

if (!mmu_enabled) {
if (is_write)
return tt->write_memory(target, address, size, count, write_buffer);
else
return r->read_memory(target, address, size, count, read_buffer, size);
}

result = check_virt_memory_access(target, address, size, count, is_write);
if (result != ERROR_OK)
return result;

uint32_t current_count = 0;
while (current_count < count) {
target_addr_t physical_addr;
result = target->type->virt2phys(target, address, &physical_addr);
if (result != ERROR_OK) {
LOG_TARGET_ERROR(target, "Address translation failed.");
return result;
}

/* TODO: For simplicity, this algorithm assumes the worst case - the smallest possible page size,
* which is 4 KiB. The algorithm can be improved to detect the real page size, and allow to use larger
* memory transfers and avoid extra unnecessary virt2phys address translations. */
uint32_t chunk_count = MIN(count - current_count, (RISCV_PGSIZE - RISCV_PGOFFSET(address)) / size);
if (is_write)
result = tt->write_memory(target, physical_addr, size, chunk_count, write_buffer + current_count * size);
else
result = r->read_memory(target, physical_addr, size, chunk_count, read_buffer + current_count * size, size);
if (result != ERROR_OK)
return result;

current_count += chunk_count;
address += chunk_count * size;
}
return ERROR_OK;
}

static int riscv_read_memory(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, uint8_t *buffer)
{
return riscv_rw_memory(target, address, size, count, buffer, NULL);
}

static int riscv_write_memory(struct target *target, target_addr_t address,
uint32_t size, uint32_t count, const uint8_t *buffer)
{
return riscv_rw_memory(target, address, size, count, NULL, buffer);
}

static const char *riscv_get_gdb_arch(const struct target *target)
Expand Down
3 changes: 3 additions & 0 deletions src/target/riscv/riscv.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ struct riscv_program;
#define RISCV_HGATP_MODE(xlen) ((xlen) == 32 ? HGATP32_MODE : HGATP64_MODE)
#define RISCV_HGATP_PPN(xlen) ((xlen) == 32 ? HGATP32_PPN : HGATP64_PPN)
#define RISCV_PGSHIFT 12
#define RISCV_PGSIZE BIT(RISCV_PGSHIFT)
#define RISCV_PGBASE(addr) ((addr) & ~(RISCV_PGSIZE - 1))
#define RISCV_PGOFFSET(addr) ((addr) & (RISCV_PGSIZE - 1))

#define PG_MAX_LEVEL 5

Expand Down

0 comments on commit 9ff272e

Please sign in to comment.