Skip to content

Commit

Permalink
Extended mem_init_region_io API
Browse files Browse the repository at this point in the history
It's now possible to update the io handlers of an existing region after it's been created
  • Loading branch information
ergo720 committed Mar 15, 2024
1 parent 648bdfa commit c579548
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 46 deletions.
2 changes: 1 addition & 1 deletion include/lib86cpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ API_FUNC void write_fstatus(cpu_t *cpu, uint16_t value);
API_FUNC uint8_t *get_ram_ptr(cpu_t *cpu);
API_FUNC uint8_t* get_host_ptr(cpu_t *cpu, addr_t addr);
API_FUNC lc86_status mem_init_region_ram(cpu_t *cpu, addr_t start, uint32_t size, bool should_int = false);
API_FUNC lc86_status mem_init_region_io(cpu_t *cpu, addr_t start, uint32_t size, bool io_space, io_handlers_t handlers, void *opaque, bool should_int = false);
API_FUNC lc86_status mem_init_region_io(cpu_t *cpu, addr_t start, uint32_t size, bool io_space, io_handlers_t handlers, void *opaque, bool should_int = false, int update = 0);
API_FUNC lc86_status mem_init_region_alias(cpu_t *cpu, addr_t alias_start, addr_t ori_start, uint32_t ori_size, bool should_int = false);
API_FUNC lc86_status mem_init_region_rom(cpu_t *cpu, addr_t start, uint32_t size, uint8_t *buffer, bool should_int = false);
API_FUNC lc86_status mem_destroy_region(cpu_t *cpu, addr_t start, uint32_t size, bool io_space, bool should_int = false);
Expand Down
3 changes: 2 additions & 1 deletion lib86cpu/core/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ void JIT_API tlb_invalidate_(cpu_ctx_t *cpu_ctx, addr_t addr);
#define CPU_REGION_INT (1 << 3)
#define CPU_TIMEOUT_INT (1 << 4) // never set, only returned as a status
#define CPU_SUSPEND_INT (1 << 5)
#define CPU_NON_HW_INT (CPU_ABORT_INT | CPU_A20_INT | CPU_REGION_INT | CPU_SUSPEND_INT)
#define CPU_HANDLER_INT (1 << 6)
#define CPU_NON_HW_INT (CPU_ABORT_INT | CPU_A20_INT | CPU_REGION_INT | CPU_SUSPEND_INT | CPU_HANDLER_INT)
#define CPU_ALL_INT (CPU_HW_INT | CPU_NON_HW_INT)

// mmu flags
Expand Down
31 changes: 26 additions & 5 deletions lib86cpu/core/translate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1652,11 +1652,32 @@ cpu_do_int(cpu_ctx_t *cpu_ctx, uint32_t int_flg)
}
}

if (int_flg & (CPU_A20_INT | CPU_REGION_INT)) {
if (int_flg & (CPU_A20_INT | CPU_REGION_INT | CPU_HANDLER_INT)) {
cpu_t *cpu = cpu_ctx->cpu;
uint32_t int_clear_flg;
uint32_t int_clear_flg = 0;
if (int_flg & CPU_HANDLER_INT) {
int_clear_flg |= CPU_HANDLER_INT;
std::for_each(cpu->regions_updated.begin(), cpu->regions_updated.end(), [cpu](const auto &data) {
if (data.io_space) {
auto io = const_cast<memory_region_t<port_t> *>(cpu->io_space_tree->search(data.start));
if (io->type == mem_type::pmio) {
io->handlers = data.handlers;
io->opaque = data.opaque;
}
}
else {
auto mmio = const_cast<memory_region_t<addr_t> *>(cpu->memory_space_tree->search(data.start));
if (mmio->type == mem_type::mmio) {
mmio->handlers = data.handlers;
mmio->opaque = data.opaque;
}
}
});
cpu->regions_updated.clear();
}

if (int_flg & CPU_A20_INT) {
int_clear_flg = CPU_A20_INT;
int_clear_flg |= CPU_A20_INT;
cpu->a20_mask = cpu->new_a20;
tlb_flush(cpu);
tc_cache_clear(cpu);
Expand All @@ -1674,8 +1695,8 @@ cpu_do_int(cpu_ctx_t *cpu_ctx, uint32_t int_flg)
cpu->regions_changed.clear();
}
}
else {
int_clear_flg = CPU_REGION_INT;
else if (int_flg & CPU_REGION_INT) {
int_clear_flg |= CPU_REGION_INT;
std::for_each(cpu->regions_changed.begin(), cpu->regions_changed.end(), [cpu](auto &pair) {
addr_t start = pair.second->start, end = pair.second->end;
if (pair.first) {
Expand Down
120 changes: 81 additions & 39 deletions lib86cpu/interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -629,57 +629,99 @@ mem_init_region_ram(cpu_t *cpu, addr_t start, uint32_t size, bool should_int)
* handlers: a struct of function pointers to call back when the region is accessed from the guest
* opaque: an arbitrary host pointer which is passed to the registered r/w function for the region
* should_int: raises a guest interrupt when true, otherwise the change takes effect immediately
* update: if 0, creates a new region. If 1, request an update to the handlers and the opaque member of an existing region that owns start. If 2,
* also request an interrupt to make visible the updated region to the cpu. When 3, only request the interrupt (the region arguments are ignored in this case).
* The last behaviour is useful to cache multiple update requests and only trigger the interrupt at the end. When not 0, should_int must be true.
* ret: the status of the operation
*/
lc86_status
mem_init_region_io(cpu_t *cpu, addr_t start, uint32_t size, bool io_space, io_handlers_t handlers, void *opaque, bool should_int)
mem_init_region_io(cpu_t *cpu, addr_t start, uint32_t size, bool io_space, io_handlers_t handlers, void *opaque, bool should_int, int update)
{
if (size == 0) {
return set_last_error(lc86_status::invalid_parameter);
}

if (io_space) {
if ((start > 65535) || ((start + size) > 65536)) {
if (update) {
if (!should_int) {
return set_last_error(lc86_status::invalid_parameter);
}

std::unique_ptr<memory_region_t<port_t>> io(new memory_region_t<port_t>);
io->start = static_cast<port_t>(start);
uint64_t end_io1 = io->start + size - 1;
io->end = std::min(end_io1, to_u64(0xFFFF));
io->type = mem_type::pmio;
io->handlers.fnr8 = handlers.fnr8 ? handlers.fnr8 : default_pmio_read_handler8;
io->handlers.fnr16 = handlers.fnr16 ? handlers.fnr16 : default_pmio_read_handler16;
io->handlers.fnr32 = handlers.fnr32 ? handlers.fnr32 : default_pmio_read_handler32;
io->handlers.fnw8 = handlers.fnw8 ? handlers.fnw8 : default_pmio_write_handler8;
io->handlers.fnw16 = handlers.fnw16 ? handlers.fnw16 : default_pmio_write_handler16;
io->handlers.fnw32 = handlers.fnw32 ? handlers.fnw32 : default_pmio_write_handler32;
io->opaque = opaque;

cpu->io_space_tree->insert(std::move(io));
if ((update == 1) || (update == 2)) {
if (io_space) {
handlers.fnr8 = handlers.fnr8 ? handlers.fnr8 : default_pmio_read_handler8;
handlers.fnr16 = handlers.fnr16 ? handlers.fnr16 : default_pmio_read_handler16;
handlers.fnr32 = handlers.fnr32 ? handlers.fnr32 : default_pmio_read_handler32;
handlers.fnw8 = handlers.fnw8 ? handlers.fnw8 : default_pmio_write_handler8;
handlers.fnw16 = handlers.fnw16 ? handlers.fnw16 : default_pmio_write_handler16;
handlers.fnw32 = handlers.fnw32 ? handlers.fnw32 : default_pmio_write_handler32;
}
else {
handlers.fnr8 = handlers.fnr8 ? handlers.fnr8 : default_mmio_read_handler8;
handlers.fnr16 = handlers.fnr16 ? handlers.fnr16 : default_mmio_read_handler16;
handlers.fnr32 = handlers.fnr32 ? handlers.fnr32 : default_mmio_read_handler32;
handlers.fnr64 = handlers.fnr64 ? handlers.fnr64 : default_mmio_read_handler64;
handlers.fnw8 = handlers.fnw8 ? handlers.fnw8 : default_mmio_write_handler8;
handlers.fnw16 = handlers.fnw16 ? handlers.fnw16 : default_mmio_write_handler16;
handlers.fnw32 = handlers.fnw32 ? handlers.fnw32 : default_mmio_write_handler32;
handlers.fnw64 = handlers.fnw64 ? handlers.fnw64 : default_mmio_write_handler64;
}

cpu->regions_updated.emplace_back(start, io_space, handlers, opaque);
if (update == 2) {
cpu->raise_int_fn(&cpu->cpu_ctx, CPU_HANDLER_INT);
}
}
else if (update == 3) {
cpu->raise_int_fn(&cpu->cpu_ctx, CPU_HANDLER_INT);
}
else {
return set_last_error(lc86_status::invalid_parameter);
}
}
else {
std::unique_ptr<memory_region_t<addr_t>> mmio(new memory_region_t<addr_t>);
mmio->start = start;
mmio->end = std::min(static_cast<uint64_t>(start) + size - 1, to_u64(0xFFFFFFFF));
mmio->type = mem_type::mmio;
mmio->handlers.fnr8 = handlers.fnr8 ? handlers.fnr8 : default_mmio_read_handler8;
mmio->handlers.fnr16 = handlers.fnr16 ? handlers.fnr16 : default_mmio_read_handler16;
mmio->handlers.fnr32 = handlers.fnr32 ? handlers.fnr32 : default_mmio_read_handler32;
mmio->handlers.fnr64 = handlers.fnr64 ? handlers.fnr64 : default_mmio_read_handler64;
mmio->handlers.fnw8 = handlers.fnw8 ? handlers.fnw8 : default_mmio_write_handler8;
mmio->handlers.fnw16 = handlers.fnw16 ? handlers.fnw16 : default_mmio_write_handler16;
mmio->handlers.fnw32 = handlers.fnw32 ? handlers.fnw32 : default_mmio_write_handler32;
mmio->handlers.fnw64 = handlers.fnw64 ? handlers.fnw64 : default_mmio_write_handler64;
mmio->opaque = opaque;
if (size == 0) {
return set_last_error(lc86_status::invalid_parameter);
}

if (should_int) {
cpu->regions_changed.push_back(std::make_pair(true, std::move(mmio)));
cpu->raise_int_fn(&cpu->cpu_ctx, CPU_REGION_INT);
if (io_space) {
if ((start > 65535) || ((start + size) > 65536)) {
return set_last_error(lc86_status::invalid_parameter);
}

std::unique_ptr<memory_region_t<port_t>> io(new memory_region_t<port_t>);
io->start = static_cast<port_t>(start);
uint64_t end_io1 = io->start + size - 1;
io->end = std::min(end_io1, to_u64(0xFFFF));
io->type = mem_type::pmio;
io->handlers.fnr8 = handlers.fnr8 ? handlers.fnr8 : default_pmio_read_handler8;
io->handlers.fnr16 = handlers.fnr16 ? handlers.fnr16 : default_pmio_read_handler16;
io->handlers.fnr32 = handlers.fnr32 ? handlers.fnr32 : default_pmio_read_handler32;
io->handlers.fnw8 = handlers.fnw8 ? handlers.fnw8 : default_pmio_write_handler8;
io->handlers.fnw16 = handlers.fnw16 ? handlers.fnw16 : default_pmio_write_handler16;
io->handlers.fnw32 = handlers.fnw32 ? handlers.fnw32 : default_pmio_write_handler32;
io->opaque = opaque;

cpu->io_space_tree->insert(std::move(io));
}
else {
cpu->memory_space_tree->insert(std::move(mmio));
tc_should_clear_cache_and_tlb<true>(cpu, start, start + size - 1);
std::unique_ptr<memory_region_t<addr_t>> mmio(new memory_region_t<addr_t>);
mmio->start = start;
mmio->end = std::min(static_cast<uint64_t>(start) + size - 1, to_u64(0xFFFFFFFF));
mmio->type = mem_type::mmio;
mmio->handlers.fnr8 = handlers.fnr8 ? handlers.fnr8 : default_mmio_read_handler8;
mmio->handlers.fnr16 = handlers.fnr16 ? handlers.fnr16 : default_mmio_read_handler16;
mmio->handlers.fnr32 = handlers.fnr32 ? handlers.fnr32 : default_mmio_read_handler32;
mmio->handlers.fnr64 = handlers.fnr64 ? handlers.fnr64 : default_mmio_read_handler64;
mmio->handlers.fnw8 = handlers.fnw8 ? handlers.fnw8 : default_mmio_write_handler8;
mmio->handlers.fnw16 = handlers.fnw16 ? handlers.fnw16 : default_mmio_write_handler16;
mmio->handlers.fnw32 = handlers.fnw32 ? handlers.fnw32 : default_mmio_write_handler32;
mmio->handlers.fnw64 = handlers.fnw64 ? handlers.fnw64 : default_mmio_write_handler64;
mmio->opaque = opaque;

if (should_int) {
cpu->regions_changed.push_back(std::make_pair(true, std::move(mmio)));
cpu->raise_int_fn(&cpu->cpu_ctx, CPU_REGION_INT);
}
else {
cpu->memory_space_tree->insert(std::move(mmio));
tc_should_clear_cache_and_tlb<true>(cpu, start, start + size - 1);
}
}
}

Expand Down
8 changes: 8 additions & 0 deletions lib86cpu/lib86cpu_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ struct memory_region_t {
memory_region_t(T s, T e) : memory_region_t() { start = buff_off_start = s; end = e; }
};

struct region_update_info_t {
addr_t start;
bool io_space;
io_handlers_t handlers;
void *opaque;
};

#include "as.h"

struct tlb_t {
Expand Down Expand Up @@ -187,6 +194,7 @@ struct cpu_t {
std::vector<wp_info<addr_t>> wp_data;
std::vector<wp_info<port_t>> wp_io;
std::vector<std::pair<bool, std::unique_ptr<memory_region_t<addr_t>>>> regions_changed;
std::vector<region_update_info_t> regions_updated;
std::bitset<SMC_MAX_SIZE> smc; // self-modifying code tracking
tlb_t itlb[ITLB_NUM_SETS][ITLB_NUM_LINES]; // instruction tlb
tlb_t dtlb[DTLB_NUM_SETS][DTLB_NUM_LINES]; // data tlb
Expand Down

0 comments on commit c579548

Please sign in to comment.