avoid allocating memory for mmio regions

This commit is contained in:
Anthony Pesch 2016-07-13 00:09:00 -07:00
parent cf01684a61
commit 27ff52335f
2 changed files with 157 additions and 113 deletions

View File

@ -10,13 +10,15 @@ struct memory {
shmem_handle_t shmem;
uint32_t shmem_size;
struct memory_region regions[MAX_REGIONS];
int num_regions;
struct physical_region physical_regions[MAX_REGIONS];
int num_physical_regions;
struct mmio_region mmio_regions[MAX_REGIONS];
int num_mmio_regions;
};
static bool is_page_aligned(uint32_t start, uint32_t size) {
return (start & (PAGE_OFFSET_BITS - 1)) == 0 &&
((start + size) & (PAGE_OFFSET_BITS - 1)) == 0;
static int is_page_aligned(uint32_t start, uint32_t size) {
return (start & ((1 << PAGE_OFFSET_BITS) - 1)) == 0 &&
((start + size) & ((1 << PAGE_OFFSET_BITS) - 1)) == 0;
}
static int get_total_page_size(int num_pages) {
@ -33,22 +35,27 @@ static uint32_t get_page_offset(uint32_t addr) {
}
// pack and unpack page entry bitstrings
static page_entry_t pack_page_entry(const struct memory_region *region,
uint32_t region_offset) {
return region_offset | (region->dynamic ? 0 : REGION_TYPE_MASK) |
region->handle;
static page_entry_t pack_page_entry(int physical_handle,
uint32_t physical_offset, int mmio_handle,
uint32_t mmio_offset) {
return ((page_entry_t)(physical_offset | physical_handle) << 32) |
(mmio_handle | mmio_offset);
}
static uint32_t get_region_offset(page_entry_t page) {
static uint32_t get_physical_offset(page_entry_t page) {
return (page >> 32) & REGION_OFFSET_MASK;
}
static int get_physical_handle(page_entry_t page) {
return (page >> 32) & REGION_HANDLE_MASK;
}
static int get_mmio_offset(page_entry_t page) {
return page & REGION_OFFSET_MASK;
}
static int is_region_static(page_entry_t page) {
return page & REGION_TYPE_MASK;
}
static int get_region_index(page_entry_t page) {
return page & REGION_INDEX_MASK;
static int get_mmio_handle(page_entry_t page) {
return page & REGION_HANDLE_MASK;
}
// iterate mirrors for a given address and mask
@ -122,38 +129,41 @@ static bool reserve_address_space(uint8_t **base) {
return false;
}
static struct memory_region *memory_alloc_region(struct memory *memory,
uint32_t size) {
CHECK_LT(memory->num_regions, MAX_REGIONS);
CHECK(is_page_aligned(memory->shmem_size, size));
struct physical_region *memory_create_physical_region(struct memory *memory,
uint32_t size) {
CHECK_LT(memory->num_physical_regions, MAX_REGIONS);
struct memory_region *region = &memory->regions[memory->num_regions];
memset(region, 0, sizeof(*region));
region->handle = memory->num_regions++;
memory->num_physical_regions++;
struct physical_region *region =
&memory->physical_regions[memory->num_physical_regions];
region->handle = memory->num_physical_regions;
region->shmem_offset = memory->shmem_size;
region->size = size;
// ensure the shared memory regions are aligned to the allocation granularity,
// otherwise it will confusingly fail to map further down the line
size_t granularity = get_allocation_granularity();
CHECK((memory->shmem_size & (granularity - 1)) == 0 &&
((memory->shmem_size + size) & (granularity - 1)) == 0);
memory->shmem_size += size;
return region;
}
struct memory_region *memory_create_region(struct memory *memory,
uint32_t size) {
CHECK(is_page_aligned(memory->shmem_size, size));
struct mmio_region *memory_create_mmio_region(struct memory *memory,
uint32_t size, void *data,
r8_cb r8, r16_cb r16, r32_cb r32,
r64_cb r64, w8_cb w8, w16_cb w16,
w32_cb w32, w64_cb w64) {
CHECK_LT(memory->num_mmio_regions, MAX_REGIONS);
struct memory_region *region = memory_alloc_region(memory, size);
region->dynamic = false;
memory->num_mmio_regions++;
return region;
}
struct memory_region *memory_create_dynamic_region(
struct memory *memory, uint32_t size, r8_cb r8, r16_cb r16, r32_cb r32,
r64_cb r64, w8_cb w8, w16_cb w16, w32_cb w32, w64_cb w64, void *data) {
struct memory_region *region = memory_alloc_region(memory, size);
region->dynamic = true;
struct mmio_region *region = &memory->mmio_regions[memory->num_mmio_regions];
region->handle = memory->num_mmio_regions;
region->size = size;
region->data = data;
region->read8 = r8;
region->read16 = r16;
region->read32 = r32;
@ -162,7 +172,6 @@ struct memory_region *memory_create_dynamic_region(
region->write16 = w16;
region->write32 = w32;
region->write64 = w64;
region->data = data;
return region;
}
@ -210,7 +219,7 @@ struct memory *memory_create(struct dreamcast *dc) {
memory->dc = dc;
memory->shmem = SHMEM_INVALID;
// 0 page is reserved, meaning all valid page entries must be non-zero
memory->num_regions = 1;
memory->num_physical_regions = 1;
return memory;
}
@ -228,19 +237,28 @@ static struct address_map_entry *address_map_alloc_entry(
return entry;
}
void am_mount_region(struct address_map *am, struct memory_region *region,
uint32_t size, uint32_t addr, uint32_t addr_mask) {
void am_physical(struct address_map *am, struct physical_region *region,
uint32_t size, uint32_t addr, uint32_t addr_mask) {
struct address_map_entry *entry = address_map_alloc_entry(am);
entry->type = MAP_ENTRY_MOUNT;
entry->type = MAP_ENTRY_PHYSICAL;
entry->size = size;
entry->addr = addr;
entry->addr_mask = addr_mask;
entry->mount.region = region;
entry->physical.region = region;
}
void am_mount_device(struct address_map *am, void *device,
address_map_cb mapper, uint32_t size, uint32_t addr,
uint32_t addr_mask) {
void am_mmio(struct address_map *am, struct mmio_region *region, uint32_t size,
uint32_t addr, uint32_t addr_mask) {
struct address_map_entry *entry = address_map_alloc_entry(am);
entry->type = MAP_ENTRY_MMIO;
entry->size = size;
entry->addr = addr;
entry->addr_mask = addr_mask;
entry->mmio.region = region;
}
void am_device(struct address_map *am, void *device, address_map_cb mapper,
uint32_t size, uint32_t addr, uint32_t addr_mask) {
struct address_map_entry *entry = address_map_alloc_entry(am);
entry->type = MAP_ENTRY_DEVICE;
entry->size = size;
@ -264,16 +282,14 @@ void am_mirror(struct address_map *am, uint32_t physical_addr, uint32_t size,
type as_##name(struct address_space *space, uint32_t addr) { \
page_entry_t page = space->pages[get_page_index(addr)]; \
DCHECK(page); \
\
if (is_region_static(page)) { \
int mmio_handle = get_mmio_handle(page); \
if (!mmio_handle) { \
return *(type *)(space->base + addr); \
} \
\
struct memory_region *region = \
&space->dc->memory->regions[get_region_index(page)]; \
uint32_t region_offset = get_region_offset(page); \
struct mmio_region *region = \
&space->dc->memory->mmio_regions[mmio_handle]; \
uint32_t region_offset = get_mmio_offset(page); \
uint32_t page_offset = get_page_offset(addr); \
\
return region->name(region->data, region_offset + page_offset); \
}
@ -286,17 +302,15 @@ define_read_bytes(read64, uint64_t);
void as_##name(struct address_space *space, uint32_t addr, type value) { \
page_entry_t page = space->pages[get_page_index(addr)]; \
DCHECK(page); \
\
if (is_region_static(page)) { \
int mmio_handle = get_mmio_handle(page); \
if (!mmio_handle) { \
*(type *)(space->base + addr) = value; \
return; \
} \
\
struct memory_region *region = \
&space->dc->memory->regions[get_region_index(page)]; \
uint32_t region_offset = get_region_offset(page); \
struct mmio_region *region = \
&space->dc->memory->mmio_regions[mmio_handle]; \
uint32_t region_offset = get_mmio_offset(page); \
uint32_t page_offset = get_page_offset(addr); \
\
region->name(region->data, region_offset + page_offset, value); \
}
@ -344,17 +358,21 @@ void as_memcpy(struct address_space *space, uint32_t dst, uint32_t src,
}
void as_lookup(struct address_space *space, uint32_t addr, uint8_t **ptr,
struct memory_region **region, uint32_t *offset) {
struct physical_region **physical_region,
uint32_t *physical_offset, struct mmio_region **mmio_region,
uint32_t *mmio_offset) {
page_entry_t page = space->pages[get_page_index(addr)];
int physical_handle = get_physical_handle(page);
int mmio_handle = get_mmio_handle(page);
if (is_region_static(page)) {
*ptr = space->base + addr;
} else {
*ptr = NULL;
}
*region = &space->dc->memory->regions[get_region_index(page)];
*offset = get_region_offset(page) + get_page_offset(addr);
*ptr = space->base + addr;
*physical_region = physical_handle
? &space->dc->memory->physical_regions[physical_handle]
: NULL;
*physical_offset = get_physical_offset(page) + get_page_offset(addr);
*mmio_region =
mmio_handle ? &space->dc->memory->mmio_regions[mmio_handle] : NULL;
*mmio_offset = get_mmio_offset(page) + get_page_offset(addr);
}
static void as_merge_map(struct address_space *space,
@ -378,15 +396,30 @@ static void as_merge_map(struct address_space *space,
int num_pages = size >> PAGE_OFFSET_BITS;
switch (entry->type) {
case MAP_ENTRY_MOUNT: {
struct memory_region *region = entry->mount.region;
case MAP_ENTRY_PHYSICAL: {
struct physical_region *physical_region = entry->physical.region;
// create an entry in the page table for each page the region occupies
for (int i = 0; i < num_pages; i++) {
uint32_t region_offset = get_total_page_size(i);
uint32_t physical_offset = get_total_page_size(i);
space->pages[first_page + i] =
pack_page_entry(region, region_offset);
pack_page_entry(physical_region->handle, physical_offset, 0, 0);
}
} break;
case MAP_ENTRY_MMIO: {
struct mmio_region *mmio_region = entry->mmio.region;
for (int i = 0; i < num_pages; i++) {
uint32_t mmio_offset = get_total_page_size(i);
page_entry_t page = space->pages[first_page + i];
int physical_handle = get_physical_handle(page);
uint32_t physical_offset = get_physical_offset(page);
space->pages[first_page + i] =
pack_page_entry(physical_handle, physical_offset,
mmio_region->handle, mmio_offset);
}
} break;
@ -415,9 +448,9 @@ static void as_merge_map(struct address_space *space,
static uint32_t as_get_page_offset(struct address_space *space,
page_entry_t page) {
const struct memory_region *region =
&space->dc->memory->regions[get_region_index(page)];
return region->shmem_offset + get_region_offset(page);
const struct physical_region *region =
&space->dc->memory->physical_regions[get_physical_handle(page)];
return region->shmem_offset + get_physical_offset(page);
}
static int as_num_adj_pages(struct address_space *space, int first_page_index) {
@ -486,8 +519,9 @@ bool as_map(struct address_space *space, const struct address_map *map) {
// protect dynamic regions in the protected address space
for (int page_index = 0; page_index < NUM_PAGES; page_index++) {
page_entry_t page = space->pages[page_index];
int mmio_index = get_mmio_handle(page);
if (is_region_static(page)) {
if (!mmio_index) {
continue;
}

View File

@ -19,12 +19,16 @@ typedef void (*w64_cb)(void *, uint32_t, uint64_t);
struct memory;
struct memory_region {
struct physical_region {
int handle;
uint32_t shmem_offset;
uint32_t size;
bool dynamic;
uint32_t shmem_offset;
};
struct mmio_region {
int handle;
uint32_t size;
void *data;
r8_cb read8;
r16_cb read16;
r32_cb read32;
@ -33,15 +37,15 @@ struct memory_region {
w16_cb write16;
w32_cb write32;
w64_cb write64;
void *data;
};
struct memory_region *memory_create_region(struct memory *memory,
uint32_t size);
struct memory_region *memory_create_dynamic_region(
struct memory *memory, uint32_t size, r8_cb r8, r16_cb r16, r32_cb r32,
r64_cb r64, w8_cb w8, w16_cb w16, w32_cb w32, w64_cb w64, void *data);
struct physical_region *memory_create_physical_region(struct memory *memory,
uint32_t size);
struct mmio_region *memory_create_mmio_region(struct memory *memory,
uint32_t size, void *data,
r8_cb r8, r16_cb r16, r32_cb r32,
r64_cb r64, w8_cb w8, w16_cb w16,
w32_cb w32, w64_cb w64);
bool memory_init(struct memory *memory);
@ -68,27 +72,26 @@ void memory_destroy(struct memory *memory);
size = end_ - begin_ + 1; \
mask = 0xffffffff;
#define AM_MASK(mask_) mask = mask_;
#define AM_MOUNT() \
{ \
struct memory_region *region = \
memory_create_region(machine->memory, size); \
am_mount_region(map, region, size, begin, mask); \
#define AM_MOUNT() \
{ \
struct physical_region *region = \
memory_create_physical_region(machine->memory, size); \
am_physical(map, region, size, begin, mask); \
}
#define AM_HANDLE(r8, r16, r32, r64, w8, w16, w32, w64) \
{ \
struct memory_region *region = memory_create_dynamic_region( \
machine->memory, size, r8, r16, r32, r64, w8, w16, w32, w64, self); \
am_mount_region(map, region, size, begin, mask); \
struct mmio_region *region = memory_create_mmio_region( \
machine->memory, size, self, r8, r16, r32, r64, w8, w16, w32, w64); \
am_mmio(map, region, size, begin, mask); \
}
#define AM_DEVICE(name, cb) \
{ \
struct device *device = dc_get_device(machine, name); \
CHECK_NOTNULL(device); \
am_mount_device(map, device, &cb, size, begin, mask); \
am_device(map, device, &cb, size, begin, mask); \
}
#define AM_MIRROR(addr) am_mirror(map, addr, size, begin);
#define AM_END() }
// clang-format on
#define MAX_MAP_ENTRIES 1024
@ -98,7 +101,8 @@ typedef void (*address_map_cb)(void *, struct dreamcast *,
struct address_map *);
enum map_entry_type {
MAP_ENTRY_MOUNT,
MAP_ENTRY_PHYSICAL,
MAP_ENTRY_MMIO,
MAP_ENTRY_DEVICE,
MAP_ENTRY_MIRROR,
};
@ -112,8 +116,12 @@ struct address_map_entry {
union {
struct {
struct memory_region *region;
} mount;
struct physical_region *region;
} physical;
struct {
struct mmio_region *region;
} mmio;
struct {
void *device;
@ -131,11 +139,12 @@ struct address_map {
int num_entries;
};
void am_mount_region(struct address_map *am, struct memory_region *region,
uint32_t size, uint32_t addr, uint32_t addr_mask);
void am_mount_device(struct address_map *am, void *device,
address_map_cb mapper, uint32_t size, uint32_t addr,
uint32_t addr_mask);
void am_physical(struct address_map *am, struct physical_region *region,
uint32_t size, uint32_t addr, uint32_t addr_mask);
void am_mmio(struct address_map *am, struct mmio_region *region, uint32_t size,
uint32_t addr, uint32_t addr_mask);
void am_device(struct address_map *am, void *device, address_map_cb mapper,
uint32_t size, uint32_t addr, uint32_t addr_mask);
void am_mirror(struct address_map *am, uint32_t physical_addr, uint32_t size,
uint32_t addr);
@ -147,13 +156,12 @@ void am_mirror(struct address_map *am, uint32_t physical_addr, uint32_t size,
#define PAGE_INDEX_MASK (uint32_t)(~PAGE_OFFSET_MASK)
#define NUM_PAGES (1 << PAGE_BITS)
// helpers for accessing region information out of a page table entry
#define REGION_INDEX_MASK (uintptr_t)(MAX_REGIONS - 1)
#define REGION_TYPE_MASK (uintptr_t)(MAX_REGIONS)
#define REGION_OFFSET_MASK (uintptr_t)(~(REGION_TYPE_MASK | REGION_INDEX_MASK))
#define MAX_REGIONS (1 << (PAGE_OFFSET_BITS - 1))
// helpers for region information out of a page table entry
#define REGION_HANDLE_MASK (page_entry_t)(MAX_REGIONS - 1)
#define REGION_OFFSET_MASK (page_entry_t)(~REGION_HANDLE_MASK)
#define MAX_REGIONS (1 << PAGE_OFFSET_BITS)
typedef uintptr_t page_entry_t;
typedef uint64_t page_entry_t;
struct address_space {
struct dreamcast *dc;
@ -169,7 +177,9 @@ void as_memcpy_to_host(struct address_space *space, void *ptr,
void as_memcpy(struct address_space *space, uint32_t virtual_dest,
uint32_t virtual_src, uint32_t size);
void as_lookup(struct address_space *space, uint32_t virtual_addr,
uint8_t **ptr, struct memory_region **region, uint32_t *offset);
uint8_t **ptr, struct physical_region **physical_region,
uint32_t *physical_offset, struct mmio_region **mmio_region,
uint32_t *mmio_offset);
uint8_t as_read8(struct address_space *space, uint32_t addr);
uint16_t as_read16(struct address_space *space, uint32_t addr);