Support change page size

This commit is contained in:
lazymio 2021-11-04 17:03:30 +01:00
parent 3ead1731fe
commit 3e4b4af7d3
No known key found for this signature in database
GPG Key ID: DFF27E34A47CB873
4 changed files with 155 additions and 16 deletions

View File

@ -267,6 +267,7 @@ struct uc_struct {
unsigned int alloc_hint;
/* qemu/exec-vary.c */
TargetPageBits *init_target_page;
int target_bits; // User defined page bits by uc_ctl
BounceBuffer bounce; // qemu/cpu-exec.c
volatile sig_atomic_t exit_request; // qemu/cpu-exec.c
/* qemu/accel/tcg/cpu-exec-common.c */
@ -339,6 +340,7 @@ struct uc_struct {
struct list saved_contexts; // The contexts saved by this uc_struct.
bool no_exit_request; // Disable check_exit_request temporarily. A
// workaround to treat the IT block as a whole block.
bool init_done; // Whether the initialization is done.
};
// Metadata stub for the variable-size cpu context used with uc_context_*()

View File

@ -35,7 +35,17 @@ bool set_preferred_target_page_bits(struct uc_struct *uc, int bits)
* a particular size.
*/
#ifdef TARGET_PAGE_BITS_VARY
assert(bits >= TARGET_PAGE_BITS_MIN);
//assert(bits >= TARGET_PAGE_BITS_MIN);
if (uc->init_target_page == NULL) {
uc->init_target_page = calloc(1, sizeof(TargetPageBits));
} else {
return false;
}
if (bits < TARGET_PAGE_BITS_MIN) {
return false;
}
if (uc->init_target_page->bits == 0 || uc->init_target_page->bits > bits) {
if (uc->init_target_page->decided) {
return false;
@ -50,10 +60,15 @@ void finalize_target_page_bits(struct uc_struct *uc)
{
#ifdef TARGET_PAGE_BITS_VARY
if (uc->init_target_page == NULL) {
uc->init_target_page = g_new0(TargetPageBits, 1);
uc->init_target_page = calloc(1, sizeof(TargetPageBits));
} else {
return;
}
if (uc->target_bits != 0) {
uc->init_target_page->bits = uc->target_bits;
}
if (uc->init_target_page->bits == 0) {
uc->init_target_page->bits = TARGET_PAGE_BITS_MIN;
}

View File

@ -156,10 +156,29 @@ static void test_uc_ctl_tb_cache()
OK(uc_close(uc));
}
static void test_uc_ctl_change_page_size()
{
uc_engine *uc;
uc_engine *uc2;
int r_pc;
OK(uc_open(UC_ARCH_ARM, UC_MODE_ARM, &uc));
OK(uc_open(UC_ARCH_ARM, UC_MODE_ARM, &uc2));
OK(uc_ctl_set_page_size(uc, 4096));
OK(uc_mem_map(uc2, 1 << 10, 1 << 10, UC_PROT_ALL));
uc_assert_err(UC_ERR_ARG, uc_mem_map(uc, 1 << 10, 1 << 10, UC_PROT_ALL));
OK(uc_close(uc));
OK(uc_close(uc2));
}
TEST_LIST = {{"test_uc_ctl_mode", test_uc_ctl_mode},
{"test_uc_ctl_page_size", test_uc_ctl_page_size},
{"test_uc_ctl_arch", test_uc_ctl_arch},
{"test_uc_ctl_time_out", test_uc_ctl_time_out},
{"test_uc_ctl_exits", test_uc_ctl_exits},
{"test_uc_ctl_tb_cache", test_uc_ctl_tb_cache},
{"test_uc_ctl_change_page_size", test_uc_ctl_change_page_size},
{NULL, NULL}};

131
uc.c
View File

@ -25,6 +25,7 @@
#include "qemu/target/riscv/unicorn.h"
#include "qemu/include/qemu/queue.h"
#include "qemu-common.h"
UNICORN_EXPORT
unsigned int uc_version(unsigned int *major, unsigned int *minor)
@ -136,6 +137,14 @@ bool uc_arch_supported(uc_arch arch)
}
}
#define UC_INIT(uc) \
if (unlikely(!(uc)->init_done)) { \
int __init_ret = uc_init(uc); \
if (unlikely(__init_ret != UC_ERR_OK)) { \
return __init_ret; \
} \
}
static gint uc_exits_cmp(gconstpointer a, gconstpointer b, gpointer user_data)
{
uint64_t lhs = *((uint64_t *)a);
@ -150,6 +159,31 @@ static gint uc_exits_cmp(gconstpointer a, gconstpointer b, gpointer user_data)
}
}
static uc_err uc_init(uc_engine *uc)
{
if (uc->init_done) {
return UC_ERR_HANDLE;
}
uc->exits = g_tree_new_full(uc_exits_cmp, NULL, g_free, NULL);
if (machine_initialize(uc)) {
return UC_ERR_RESOURCE;
}
// init fpu softfloat
uc->softfloat_initialize();
if (uc->reg_reset) {
uc->reg_reset(uc);
}
uc->init_done = true;
return UC_ERR_OK;
}
UNICORN_EXPORT
uc_err uc_open(uc_arch arch, uc_mode mode, uc_engine **result)
{
@ -175,8 +209,6 @@ uc_err uc_open(uc_arch arch, uc_mode mode, uc_engine **result)
QTAILQ_INIT(&uc->address_spaces);
uc->exits = g_tree_new_full(uc_exits_cmp, NULL, g_free, NULL);
switch (arch) {
default:
break;
@ -315,19 +347,10 @@ uc_err uc_open(uc_arch arch, uc_mode mode, uc_engine **result)
return UC_ERR_ARCH;
}
if (machine_initialize(uc)) {
return UC_ERR_RESOURCE;
}
// init fpu softfloat
uc->softfloat_initialize();
uc->init_done = false;
*result = uc;
if (uc->reg_reset) {
uc->reg_reset(uc);
}
return UC_ERR_OK;
} else {
return UC_ERR_ARCH;
@ -342,6 +365,11 @@ uc_err uc_close(uc_engine *uc)
struct hook *hook;
MemoryRegion *mr;
if (!uc->init_done) {
free(uc);
return UC_ERR_OK;
}
// Cleanup internally.
if (uc->release) {
uc->release(uc->tcg_ctx);
@ -425,6 +453,9 @@ UNICORN_EXPORT
uc_err uc_reg_read_batch(uc_engine *uc, int *ids, void **vals, int count)
{
int ret = UC_ERR_OK;
UC_INIT(uc);
if (uc->reg_read) {
ret = uc->reg_read(uc, (unsigned int *)ids, vals, count);
} else {
@ -438,6 +469,9 @@ UNICORN_EXPORT
uc_err uc_reg_write_batch(uc_engine *uc, int *ids, void *const *vals, int count)
{
int ret = UC_ERR_OK;
UC_INIT(uc);
if (uc->reg_write) {
ret = uc->reg_write(uc, (unsigned int *)ids, vals, count);
} else {
@ -450,12 +484,14 @@ uc_err uc_reg_write_batch(uc_engine *uc, int *ids, void *const *vals, int count)
UNICORN_EXPORT
uc_err uc_reg_read(uc_engine *uc, int regid, void *value)
{
UC_INIT(uc);
return uc_reg_read_batch(uc, &regid, &value, 1);
}
UNICORN_EXPORT
uc_err uc_reg_write(uc_engine *uc, int regid, const void *value)
{
UC_INIT(uc);
return uc_reg_write_batch(uc, &regid, (void *const *)&value, 1);
}
@ -485,6 +521,8 @@ uc_err uc_mem_read(uc_engine *uc, uint64_t address, void *_bytes, size_t size)
size_t count = 0, len;
uint8_t *bytes = _bytes;
UC_INIT(uc);
// qemu cpu_physical_memory_rw() size is an int
if (size > INT_MAX)
return UC_ERR_ARG;
@ -528,6 +566,8 @@ uc_err uc_mem_write(uc_engine *uc, uint64_t address, const void *_bytes,
size_t count = 0, len;
const uint8_t *bytes = _bytes;
UC_INIT(uc);
// qemu cpu_physical_memory_rw() size is an int
if (size > INT_MAX)
return UC_ERR_ARG;
@ -657,6 +697,8 @@ uc_err uc_emu_start(uc_engine *uc, uint64_t begin, uint64_t until,
uc->timed_out = false;
uc->first_tb = true;
UC_INIT(uc);
switch (uc->arch) {
default:
break;
@ -777,6 +819,8 @@ uc_err uc_emu_start(uc_engine *uc, uint64_t begin, uint64_t until,
UNICORN_EXPORT
uc_err uc_emu_stop(uc_engine *uc)
{
UC_INIT(uc);
if (uc->emulation_done) {
return UC_ERR_OK;
}
@ -918,6 +962,8 @@ uc_err uc_mem_map(uc_engine *uc, uint64_t address, size_t size, uint32_t perms)
{
uc_err res;
UC_INIT(uc);
if (uc->mem_redirect) {
address = uc->mem_redirect(address);
}
@ -937,6 +983,8 @@ uc_err uc_mem_map_ptr(uc_engine *uc, uint64_t address, size_t size,
{
uc_err res;
UC_INIT(uc);
if (ptr == NULL) {
return UC_ERR_ARG;
}
@ -961,6 +1009,8 @@ uc_err uc_mmio_map(uc_engine *uc, uint64_t address, size_t size,
{
uc_err res;
UC_INIT(uc);
if (uc->mem_redirect) {
address = uc->mem_redirect(address);
}
@ -1167,6 +1217,8 @@ uc_err uc_mem_protect(struct uc_struct *uc, uint64_t address, size_t size,
size_t count, len;
bool remove_exec = false;
UC_INIT(uc);
if (size == 0) {
// trivial case, no change
return UC_ERR_OK;
@ -1237,6 +1289,8 @@ uc_err uc_mem_unmap(struct uc_struct *uc, uint64_t address, size_t size)
uint64_t addr;
size_t count, len;
UC_INIT(uc);
if (size == 0) {
// nothing to unmap
return UC_ERR_OK;
@ -1323,6 +1377,8 @@ uc_err uc_hook_add(uc_engine *uc, uc_hook *hh, int type, void *callback,
int ret = UC_ERR_OK;
int i = 0;
UC_INIT(uc);
struct hook *hook = calloc(1, sizeof(struct hook));
if (hook == NULL) {
return UC_ERR_NOMEM;
@ -1439,6 +1495,8 @@ uc_err uc_hook_del(uc_engine *uc, uc_hook hh)
int i;
struct hook *hook = (struct hook *)hh;
UC_INIT(uc);
// we can't dereference hook->type if hook is invalid
// so for now we need to iterate over all possible types to remove the hook
// which is less efficient
@ -1554,6 +1612,8 @@ uc_err uc_mem_regions(uc_engine *uc, uc_mem_region **regions, uint32_t *count)
uint32_t i;
uc_mem_region *r = NULL;
UC_INIT(uc);
*count = uc->mapped_block_count;
if (*count) {
@ -1578,6 +1638,8 @@ uc_err uc_mem_regions(uc_engine *uc, uc_mem_region **regions, uint32_t *count)
UNICORN_EXPORT
uc_err uc_query(uc_engine *uc, uc_query_type type, size_t *result)
{
UC_INIT(uc);
switch (type) {
default:
return UC_ERR_ARG;
@ -1613,6 +1675,8 @@ uc_err uc_context_alloc(uc_engine *uc, uc_context **context)
struct uc_context **_context = context;
size_t size = uc_context_size(uc);
UC_INIT(uc);
*_context = g_malloc(size);
if (*_context) {
(*_context)->jmp_env_size = sizeof(*uc->cpu->jmp_env);
@ -1640,6 +1704,7 @@ uc_err uc_free(void *mem)
UNICORN_EXPORT
size_t uc_context_size(uc_engine *uc)
{
UC_INIT(uc);
// return the total size of struct uc_context
return sizeof(uc_context) + uc->cpu_context_size +
sizeof(*uc->cpu->jmp_env);
@ -1648,6 +1713,8 @@ size_t uc_context_size(uc_engine *uc)
UNICORN_EXPORT
uc_err uc_context_save(uc_engine *uc, uc_context *context)
{
UC_INIT(uc);
memcpy(context->data, uc->cpu->env_ptr, context->context_size);
memcpy(context->data + context->context_size, uc->cpu->jmp_env,
context->jmp_env_size);
@ -1819,6 +1886,8 @@ uc_err uc_context_reg_read_batch(uc_context *ctx, int *ids, void **vals,
UNICORN_EXPORT
uc_err uc_context_restore(uc_engine *uc, uc_context *context)
{
UC_INIT(uc);
memcpy(uc->cpu->env_ptr, context->data, context->context_size);
if (list_exists(&uc->saved_contexts, context)) {
memcpy(uc->cpu->jmp_env, context->data + context->context_size,
@ -1899,11 +1968,33 @@ uc_err uc_ctl(uc_engine *uc, uc_control_type control, ...)
case UC_CTL_UC_PAGE_SIZE: {
if (rw == UC_CTL_IO_READ) {
UC_INIT(uc);
uint32_t *page_size = va_arg(args, uint32_t *);
*page_size = uc->target_page_size;
} else {
// Not implemented.
err = UC_ERR_ARG;
uint32_t page_size = va_arg(args, uint32_t);
int bits = 0;
if (uc->arch != UC_ARCH_ARM) {
err = UC_ERR_ARG;
break;
}
if ((page_size & (page_size - 1))) {
err = UC_ERR_ARG;
break;
}
while (page_size) {
bits++;
page_size >>= 1;
}
uc->target_bits = bits;
err = UC_ERR_OK;
}
break;
}
@ -1918,6 +2009,9 @@ uc_err uc_ctl(uc_engine *uc, uc_control_type control, ...)
}
case UC_CTL_UC_EXITS_CNT: {
UC_INIT(uc);
if (!uc->use_exits) {
err = UC_ERR_ARG;
} else if (rw == UC_CTL_IO_READ) {
@ -1930,6 +2024,9 @@ uc_err uc_ctl(uc_engine *uc, uc_control_type control, ...)
}
case UC_CTL_UC_EXITS: {
UC_INIT(uc);
if (!uc->use_exits) {
err = UC_ERR_ARG;
} else if (rw == UC_CTL_IO_READ) {
@ -1965,6 +2062,9 @@ uc_err uc_ctl(uc_engine *uc, uc_control_type control, ...)
break;
case UC_CTL_TB_REQUEST_CACHE: {
UC_INIT(uc);
if (rw == UC_CTL_IO_READ_WRITE) {
uint64_t addr = va_arg(args, uint64_t);
uc_tb *tb = va_arg(args, uc_tb *);
@ -1976,6 +2076,9 @@ uc_err uc_ctl(uc_engine *uc, uc_control_type control, ...)
}
case UC_CTL_TB_REMOVE_CACHE: {
UC_INIT(uc);
if (rw == UC_CTL_IO_READ) {
uint64_t addr = va_arg(args, uint64_t);
uc->uc_invalidate_tb(uc, addr, 1);