Merge remote-tracking branch 'upstream/master' into change-handle-based-api
This commit is contained in:
133
uc.c
133
uc.c
@ -31,11 +31,6 @@
|
||||
|
||||
#include "qemu/include/hw/boards.h"
|
||||
|
||||
// TODO
|
||||
static uint64_t map_begin[32], map_end[32];
|
||||
static int map_count = 0;
|
||||
|
||||
|
||||
UNICORN_EXPORT
|
||||
unsigned int uc_version(unsigned int *major, unsigned int *minor)
|
||||
{
|
||||
@ -85,6 +80,10 @@ const char *uc_strerror(uc_err code)
|
||||
return "Invalid hook type (UC_ERR_HOOK)";
|
||||
case UC_ERR_MAP:
|
||||
return "Invalid memory mapping (UC_ERR_MAP)";
|
||||
case UC_ERR_MEM_WRITE_NW:
|
||||
return "Write to non-writable (UC_ERR_MEM_WRITE_NW)";
|
||||
case UC_ERR_MEM_READ_NR:
|
||||
return "Read from non-readable (UC_ERR_MEM_READ_NR)";
|
||||
}
|
||||
}
|
||||
|
||||
@ -269,6 +268,8 @@ uc_err uc_close(struct uc_struct *uc)
|
||||
uc->root->free(uc->root);
|
||||
|
||||
free(uc->hook_callbacks);
|
||||
|
||||
free(uc->mapped_blocks);
|
||||
|
||||
// finally, free uc itself.
|
||||
memset(uc, 0, sizeof(*uc));
|
||||
@ -302,23 +303,90 @@ uc_err uc_reg_write(struct uc_struct *uc, int regid, const void *value)
|
||||
}
|
||||
|
||||
|
||||
UNICORN_EXPORT
|
||||
uc_err uc_mem_read(struct uc_struct *uc, uint64_t address, uint8_t *bytes, size_t size)
|
||||
// check if a memory area is mapped
|
||||
// this is complicated because an area can overlap adjacent blocks
|
||||
static bool check_mem_area(struct uc_struct *uc, uint64_t address, size_t size)
|
||||
{
|
||||
if (uc->read_mem(&uc->as, address, bytes, size) == false)
|
||||
return UC_ERR_MEM_READ;
|
||||
size_t count = 0, len;
|
||||
|
||||
return UC_ERR_OK;
|
||||
while(count < size) {
|
||||
MemoryRegion *mr = memory_mapping(uc, address);
|
||||
if (mr) {
|
||||
len = MIN(size - count, mr->end - address);
|
||||
count += len;
|
||||
address += len;
|
||||
} else // this address is not mapped in yet
|
||||
break;
|
||||
}
|
||||
|
||||
return (count == size);
|
||||
}
|
||||
|
||||
|
||||
UNICORN_EXPORT
|
||||
uc_err uc_mem_read(struct uc_struct *uc, uint64_t address, uint8_t *bytes, size_t size)
|
||||
{
|
||||
if (!check_mem_area(uc, address, size))
|
||||
return UC_ERR_MEM_READ;
|
||||
|
||||
size_t count = 0, len;
|
||||
|
||||
// memory area can overlap adjacent memory blocks
|
||||
while(count < size) {
|
||||
MemoryRegion *mr = memory_mapping(uc, address);
|
||||
if (mr) {
|
||||
len = MIN(size - count, mr->end - address);
|
||||
if (uc->read_mem(&uc->as, address, bytes, len) == false)
|
||||
break;
|
||||
count += len;
|
||||
address += len;
|
||||
bytes += len;
|
||||
} else // this address is not mapped in yet
|
||||
break;
|
||||
}
|
||||
|
||||
if (count == size)
|
||||
return UC_ERR_OK;
|
||||
else
|
||||
return UC_ERR_MEM_READ;
|
||||
}
|
||||
|
||||
UNICORN_EXPORT
|
||||
uc_err uc_mem_write(struct uc_struct *uc, uint64_t address, const uint8_t *bytes, size_t size)
|
||||
{
|
||||
if (uc->write_mem(&uc->as, address, bytes, size) == false)
|
||||
if (!check_mem_area(uc, address, size))
|
||||
return UC_ERR_MEM_WRITE;
|
||||
|
||||
return UC_ERR_OK;
|
||||
size_t count = 0, len;
|
||||
|
||||
// memory area can overlap adjacent memory blocks
|
||||
while(count < size) {
|
||||
MemoryRegion *mr = memory_mapping(uc, address);
|
||||
if (mr) {
|
||||
uint32_t operms = mr->perms;
|
||||
if (!(operms & UC_PROT_WRITE)) // write protected
|
||||
// but this is not the program accessing memory, so temporarily mark writable
|
||||
uc->readonly_mem(mr, false);
|
||||
|
||||
len = MIN(size - count, mr->end - address);
|
||||
if (uc->write_mem(&uc->as, address, bytes, len) == false)
|
||||
break;
|
||||
|
||||
if (!(operms & UC_PROT_WRITE)) // write protected
|
||||
// now write protect it again
|
||||
uc->readonly_mem(mr, true);
|
||||
|
||||
count += len;
|
||||
address += len;
|
||||
bytes += len;
|
||||
} else // this address is not mapped in yet
|
||||
break;
|
||||
}
|
||||
|
||||
if (count == size)
|
||||
return UC_ERR_OK;
|
||||
else
|
||||
return UC_ERR_MEM_WRITE;
|
||||
}
|
||||
|
||||
#define TIMEOUT_STEP 2 // microseconds
|
||||
@ -358,6 +426,7 @@ uc_err uc_emu_start(struct uc_struct* uc, uint64_t begin, uint64_t until, uint64
|
||||
uc->stop_request = false;
|
||||
uc->invalid_error = UC_ERR_OK;
|
||||
uc->block_full = false;
|
||||
uc->emulation_done = false;
|
||||
|
||||
switch(uc->arch) {
|
||||
default:
|
||||
@ -423,6 +492,11 @@ uc_err uc_emu_start(struct uc_struct* uc, uint64_t begin, uint64_t until, uint64
|
||||
// emulation is done
|
||||
uc->emulation_done = true;
|
||||
|
||||
if (timeout) {
|
||||
// wait for the timer to finish
|
||||
qemu_thread_join(&uc->timer);
|
||||
}
|
||||
|
||||
return uc->invalid_error;
|
||||
}
|
||||
|
||||
@ -430,6 +504,9 @@ uc_err uc_emu_start(struct uc_struct* uc, uint64_t begin, uint64_t until, uint64
|
||||
UNICORN_EXPORT
|
||||
uc_err uc_emu_stop(struct uc_struct *uc)
|
||||
{
|
||||
if (uc->emulation_done)
|
||||
return UC_ERR_OK;
|
||||
|
||||
uc->stop_request = true;
|
||||
// exit the current TB
|
||||
cpu_exit(uc->current_cpu);
|
||||
@ -469,8 +546,10 @@ static uc_err _hook_mem_access(struct uc_struct *uc, uc_mem_type type,
|
||||
}
|
||||
|
||||
UNICORN_EXPORT
|
||||
uc_err uc_mem_map(struct uc_struct *uc, uint64_t address, size_t size)
|
||||
uc_err uc_mem_map(struct uc_struct *uc, uint64_t address, size_t size, uint32_t perms)
|
||||
{
|
||||
MemoryRegion **regions;
|
||||
|
||||
if (size == 0)
|
||||
// invalid memory mapping
|
||||
return UC_ERR_MAP;
|
||||
@ -483,25 +562,34 @@ uc_err uc_mem_map(struct uc_struct *uc, uint64_t address, size_t size)
|
||||
if ((size & (4*1024 - 1)) != 0)
|
||||
return UC_ERR_MAP;
|
||||
|
||||
map_begin[map_count] = address;
|
||||
map_end[map_count] = size + map_begin[map_count];
|
||||
uc->memory_map(uc, map_begin[map_count], size);
|
||||
map_count++;
|
||||
// check for only valid permissions
|
||||
if ((perms & ~(UC_PROT_READ | UC_PROT_WRITE)) != 0)
|
||||
return UC_ERR_MAP;
|
||||
|
||||
if ((uc->mapped_block_count & (MEM_BLOCK_INCR - 1)) == 0) { //time to grow
|
||||
regions = (MemoryRegion**)realloc(uc->mapped_blocks, sizeof(MemoryRegion*) * (uc->mapped_block_count + MEM_BLOCK_INCR));
|
||||
if (regions == NULL) {
|
||||
return UC_ERR_OOM;
|
||||
}
|
||||
uc->mapped_blocks = regions;
|
||||
}
|
||||
uc->mapped_blocks[uc->mapped_block_count] = uc->memory_map(uc, address, size, perms);
|
||||
uc->mapped_block_count++;
|
||||
|
||||
return UC_ERR_OK;
|
||||
}
|
||||
|
||||
bool memory_mapping(uint64_t address)
|
||||
MemoryRegion *memory_mapping(struct uc_struct* uc, uint64_t address)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for(i = 0; i < map_count; i++) {
|
||||
if (address >= map_begin[i] && address <= map_end[i])
|
||||
return true;
|
||||
for(i = 0; i < uc->mapped_block_count; i++) {
|
||||
if (address >= uc->mapped_blocks[i]->addr && address < uc->mapped_blocks[i]->end)
|
||||
return uc->mapped_blocks[i];
|
||||
}
|
||||
|
||||
// not found
|
||||
return false;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static uc_err _hook_mem_invalid(struct uc_struct* uc, uc_cb_eventmem_t callback,
|
||||
@ -653,4 +741,3 @@ uc_err uc_hook_del(struct uc_struct *uc, uc_hook_h hh)
|
||||
{
|
||||
return hook_del(uc, hh);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user