mirror_qemu/semihosting/uaccess.c

92 lines
2.3 KiB
C
Raw Permalink Normal View History

/*
* Helper routines to provide target memory access for semihosting
* syscalls in system emulation mode.
*
* Copyright (c) 2007 CodeSourcery.
*
* This code is licensed under the GPL
*/
#include "qemu/osdep.h"
#include "exec/exec-all.h"
#include "semihosting/uaccess.h"
void *uaccess_lock_user(CPUArchState *env, target_ulong addr,
target_ulong len, bool copy)
{
void *p = malloc(len);
if (p && copy) {
if (cpu_memory_rw_debug(env_cpu(env), addr, p, len, 0)) {
free(p);
p = NULL;
}
}
return p;
}
ssize_t uaccess_strlen_user(CPUArchState *env, target_ulong addr)
{
int mmu_idx = cpu_mmu_index(env_cpu(env), false);
size_t len = 0;
while (1) {
size_t left_in_page;
int flags;
void *h;
/* Find the number of bytes remaining in the page. */
left_in_page = TARGET_PAGE_SIZE - (addr & ~TARGET_PAGE_MASK);
accel/tcg: Add 'size' param to probe_access_flags() probe_access_flags() as it is today uses probe_access_full(), which in turn uses probe_access_internal() with size = 0. probe_access_internal() then uses the size to call the tlb_fill() callback for the given CPU. This size param ('fault_size' as probe_access_internal() calls it) is ignored by most existing .tlb_fill callback implementations, e.g. arm_cpu_tlb_fill(), ppc_cpu_tlb_fill(), x86_cpu_tlb_fill() and mips_cpu_tlb_fill() to name a few. But RISC-V riscv_cpu_tlb_fill() actually uses it. The 'size' parameter is used to check for PMP (Physical Memory Protection) access. This is necessary because PMP does not make any guarantees about all the bytes of the same page having the same permissions, i.e. the same page can have different PMP properties, so we're forced to make sub-page range checks. To allow RISC-V emulation to do a probe_acess_flags() that covers PMP, we need to either add a 'size' param to the existing probe_acess_flags() or create a new interface (e.g. probe_access_range_flags). There are quite a few probe_* APIs already, so let's add a 'size' param to probe_access_flags() and re-use this API. This is done by open coding what probe_access_full() does inside probe_acess_flags() and passing the 'size' param to probe_acess_internal(). Existing probe_access_flags() callers use size = 0 to not change their current API usage. 'size' is asserted to enforce single page access like probe_access() already does. No behavioral changes intended. Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> Message-Id: <20230223234427.521114-2-dbarboza@ventanamicro.com> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
2023-02-24 02:44:24 +03:00
flags = probe_access_flags(env, addr, 0, MMU_DATA_LOAD,
mmu_idx, true, &h, 0);
if (flags & TLB_INVALID_MASK) {
return -1;
}
if (flags & TLB_MMIO) {
do {
uint8_t c;
if (cpu_memory_rw_debug(env_cpu(env), addr, &c, 1, 0)) {
return -1;
}
if (c == 0) {
return len;
}
addr++;
len++;
if (len > INT32_MAX) {
return -1;
}
} while (--left_in_page != 0);
} else {
char *p = memchr(h, 0, left_in_page);
if (p) {
len += p - (char *)h;
return len <= INT32_MAX ? (ssize_t)len : -1;
}
addr += left_in_page;
len += left_in_page;
if (len > INT32_MAX) {
return -1;
}
}
}
}
char *uaccess_lock_user_string(CPUArchState *env, target_ulong addr)
{
ssize_t len = uaccess_strlen_user(env, addr);
if (len < 0) {
return NULL;
}
return uaccess_lock_user(env, addr, len + 1, true);
}
void uaccess_unlock_user(CPUArchState *env, void *p,
target_ulong addr, target_ulong len)
{
if (len) {
cpu_memory_rw_debug(env_cpu(env), addr, p, len, 1);
}
free(p);
}