Skip to content

Commit 9ff272e

Browse files
authored
Merge pull request #1149 from zqb-all/read-write-cross-page
riscv: fix read/write virtual memory across page boundaries
2 parents 7b4ad6f + 92d7a57 commit 9ff272e

File tree

2 files changed

+78
-28
lines changed

2 files changed

+78
-28
lines changed

src/target/riscv/riscv.c

Lines changed: 75 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3086,32 +3086,28 @@ static int riscv_virt2phys(struct target *target, target_addr_t virtual, target_
30863086
virtual, physical);
30873087
}
30883088

3089+
static int check_virt_memory_access(struct target *target, target_addr_t address,
3090+
uint32_t size, uint32_t count, bool is_write)
3091+
{
3092+
const bool is_misaligned = address % size != 0;
3093+
// TODO: This assumes that size of each page is 4 KiB, which is not necessarily the case.
3094+
const bool crosses_page_boundary = RISCV_PGBASE(address + size * count - 1) != RISCV_PGBASE(address);
3095+
if (is_misaligned && crosses_page_boundary) {
3096+
LOG_TARGET_ERROR(target, "Mis-aligned memory %s (address=0x%" TARGET_PRIxADDR ", size=%d, count=%d)"
3097+
" would access an element across page boundary. This is not supported.",
3098+
is_write ? "write" : "read", address, size, count);
3099+
return ERROR_FAIL;
3100+
}
3101+
return ERROR_OK;
3102+
}
3103+
30893104
static int riscv_read_phys_memory(struct target *target, target_addr_t phys_address,
30903105
uint32_t size, uint32_t count, uint8_t *buffer)
30913106
{
30923107
RISCV_INFO(r);
30933108
return r->read_memory(target, phys_address, size, count, buffer, size);
30943109
}
30953110

3096-
static int riscv_read_memory(struct target *target, target_addr_t address,
3097-
uint32_t size, uint32_t count, uint8_t *buffer)
3098-
{
3099-
if (count == 0) {
3100-
LOG_TARGET_WARNING(target, "0-length read from 0x%" TARGET_PRIxADDR, address);
3101-
return ERROR_OK;
3102-
}
3103-
3104-
target_addr_t physical_addr;
3105-
int result = target->type->virt2phys(target, address, &physical_addr);
3106-
if (result != ERROR_OK) {
3107-
LOG_TARGET_ERROR(target, "Address translation failed.");
3108-
return result;
3109-
}
3110-
3111-
RISCV_INFO(r);
3112-
return r->read_memory(target, physical_addr, size, count, buffer, size);
3113-
}
3114-
31153111
static int riscv_write_phys_memory(struct target *target, target_addr_t phys_address,
31163112
uint32_t size, uint32_t count, const uint8_t *buffer)
31173113
{
@@ -3121,25 +3117,76 @@ static int riscv_write_phys_memory(struct target *target, target_addr_t phys_add
31213117
return tt->write_memory(target, phys_address, size, count, buffer);
31223118
}
31233119

3124-
static int riscv_write_memory(struct target *target, target_addr_t address,
3125-
uint32_t size, uint32_t count, const uint8_t *buffer)
3120+
static int riscv_rw_memory(struct target *target, target_addr_t address, uint32_t size,
3121+
uint32_t count, uint8_t *read_buffer, const uint8_t *write_buffer)
31263122
{
3123+
/* Exactly one of the buffers must be set, the other must be NULL */
3124+
assert(!!read_buffer != !!write_buffer);
3125+
3126+
const bool is_write = write_buffer ? true : false;
31273127
if (count == 0) {
3128-
LOG_TARGET_WARNING(target, "0-length write to 0x%" TARGET_PRIxADDR, address);
3128+
LOG_TARGET_WARNING(target, "0-length %s 0x%" TARGET_PRIxADDR,
3129+
is_write ? "write to" : "read from", address);
31293130
return ERROR_OK;
31303131
}
31313132

3132-
target_addr_t physical_addr;
3133-
int result = target->type->virt2phys(target, address, &physical_addr);
3134-
if (result != ERROR_OK) {
3135-
LOG_TARGET_ERROR(target, "Address translation failed.");
3133+
int mmu_enabled;
3134+
int result = riscv_mmu(target, &mmu_enabled);
3135+
if (result != ERROR_OK)
31363136
return result;
3137-
}
31383137

3138+
RISCV_INFO(r);
31393139
struct target_type *tt = get_target_type(target);
31403140
if (!tt)
31413141
return ERROR_FAIL;
3142-
return tt->write_memory(target, physical_addr, size, count, buffer);
3142+
3143+
if (!mmu_enabled) {
3144+
if (is_write)
3145+
return tt->write_memory(target, address, size, count, write_buffer);
3146+
else
3147+
return r->read_memory(target, address, size, count, read_buffer, size);
3148+
}
3149+
3150+
result = check_virt_memory_access(target, address, size, count, is_write);
3151+
if (result != ERROR_OK)
3152+
return result;
3153+
3154+
uint32_t current_count = 0;
3155+
while (current_count < count) {
3156+
target_addr_t physical_addr;
3157+
result = target->type->virt2phys(target, address, &physical_addr);
3158+
if (result != ERROR_OK) {
3159+
LOG_TARGET_ERROR(target, "Address translation failed.");
3160+
return result;
3161+
}
3162+
3163+
/* TODO: For simplicity, this algorithm assumes the worst case - the smallest possible page size,
3164+
* which is 4 KiB. The algorithm can be improved to detect the real page size, and allow to use larger
3165+
* memory transfers and avoid extra unnecessary virt2phys address translations. */
3166+
uint32_t chunk_count = MIN(count - current_count, (RISCV_PGSIZE - RISCV_PGOFFSET(address)) / size);
3167+
if (is_write)
3168+
result = tt->write_memory(target, physical_addr, size, chunk_count, write_buffer + current_count * size);
3169+
else
3170+
result = r->read_memory(target, physical_addr, size, chunk_count, read_buffer + current_count * size, size);
3171+
if (result != ERROR_OK)
3172+
return result;
3173+
3174+
current_count += chunk_count;
3175+
address += chunk_count * size;
3176+
}
3177+
return ERROR_OK;
3178+
}
3179+
3180+
static int riscv_read_memory(struct target *target, target_addr_t address,
3181+
uint32_t size, uint32_t count, uint8_t *buffer)
3182+
{
3183+
return riscv_rw_memory(target, address, size, count, buffer, NULL);
3184+
}
3185+
3186+
static int riscv_write_memory(struct target *target, target_addr_t address,
3187+
uint32_t size, uint32_t count, const uint8_t *buffer)
3188+
{
3189+
return riscv_rw_memory(target, address, size, count, NULL, buffer);
31433190
}
31443191

31453192
static const char *riscv_get_gdb_arch(const struct target *target)

src/target/riscv/riscv.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ struct riscv_program;
2929
#define RISCV_HGATP_MODE(xlen) ((xlen) == 32 ? HGATP32_MODE : HGATP64_MODE)
3030
#define RISCV_HGATP_PPN(xlen) ((xlen) == 32 ? HGATP32_PPN : HGATP64_PPN)
3131
#define RISCV_PGSHIFT 12
32+
#define RISCV_PGSIZE BIT(RISCV_PGSHIFT)
33+
#define RISCV_PGBASE(addr) ((addr) & ~(RISCV_PGSIZE - 1))
34+
#define RISCV_PGOFFSET(addr) ((addr) & (RISCV_PGSIZE - 1))
3235

3336
#define PG_MAX_LEVEL 5
3437

0 commit comments

Comments
 (0)