Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

riscv: fix read/write virtual memory across page boundaries #1149

Merged
merged 3 commits into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 75 additions & 28 deletions src/target/riscv/riscv.c
Original file line number Diff line number Diff line change
Expand Up @@ -3061,32 +3061,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 @@ -3096,25 +3092,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);
JanMatCodasip marked this conversation as resolved.
Show resolved Hide resolved
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