mirror of https://github.com/xemu-project/xemu.git
dma/rc4030: translate memory accesses only when they occur
This simplifies the code a lot, and this fixes big memory leaks
introduced in a3d586f704
Windows NT is now able to boot without using gigabytes of ram on the host.
Signed-off-by: Hervé Poussineau <hpoussin@reactos.org>
Reviewed-by: Yongbok Kim <yongbok.kim@imgtec.com>
Signed-off-by: Yongbok Kim <yongbok.kim@imgtec.com>
This commit is contained in:
parent
c0a3172fa6
commit
c627e7526a
158
hw/dma/rc4030.c
158
hw/dma/rc4030.c
|
@ -34,8 +34,6 @@
|
||||||
/********************************************************/
|
/********************************************************/
|
||||||
/* rc4030 emulation */
|
/* rc4030 emulation */
|
||||||
|
|
||||||
#define MAX_TL_ENTRIES 512
|
|
||||||
|
|
||||||
typedef struct dma_pagetable_entry {
|
typedef struct dma_pagetable_entry {
|
||||||
int32_t frame;
|
int32_t frame;
|
||||||
int32_t owner;
|
int32_t owner;
|
||||||
|
@ -91,14 +89,8 @@ typedef struct rc4030State
|
||||||
qemu_irq timer_irq;
|
qemu_irq timer_irq;
|
||||||
qemu_irq jazz_bus_irq;
|
qemu_irq jazz_bus_irq;
|
||||||
|
|
||||||
/* biggest translation table */
|
|
||||||
MemoryRegion dma_tt;
|
|
||||||
/* translation table memory region alias, added to system RAM */
|
|
||||||
MemoryRegion dma_tt_alias;
|
|
||||||
/* whole DMA memory region, root of DMA address space */
|
/* whole DMA memory region, root of DMA address space */
|
||||||
MemoryRegion dma_mr;
|
MemoryRegion dma_mr;
|
||||||
/* translation table entry aliases, added to DMA memory region */
|
|
||||||
MemoryRegion dma_mrs[MAX_TL_ENTRIES];
|
|
||||||
AddressSpace dma_as;
|
AddressSpace dma_as;
|
||||||
|
|
||||||
MemoryRegion iomem_chipset;
|
MemoryRegion iomem_chipset;
|
||||||
|
@ -256,96 +248,6 @@ static uint64_t rc4030_read(void *opaque, hwaddr addr, unsigned int size)
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rc4030_dma_as_update_one(rc4030State *s, int index, uint32_t frame)
|
|
||||||
{
|
|
||||||
if (index < MAX_TL_ENTRIES) {
|
|
||||||
memory_region_set_enabled(&s->dma_mrs[index], false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!frame) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index >= MAX_TL_ENTRIES) {
|
|
||||||
qemu_log_mask(LOG_UNIMP,
|
|
||||||
"rc4030: trying to use too high "
|
|
||||||
"translation table entry %d (max allowed=%d)",
|
|
||||||
index, MAX_TL_ENTRIES);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
memory_region_set_alias_offset(&s->dma_mrs[index], frame);
|
|
||||||
memory_region_set_enabled(&s->dma_mrs[index], true);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rc4030_dma_tt_write(void *opaque, hwaddr addr, uint64_t data,
|
|
||||||
unsigned int size)
|
|
||||||
{
|
|
||||||
rc4030State *s = opaque;
|
|
||||||
|
|
||||||
/* write memory */
|
|
||||||
memcpy(memory_region_get_ram_ptr(&s->dma_tt) + addr, &data, size);
|
|
||||||
|
|
||||||
/* update dma address space (only if frame field has been written) */
|
|
||||||
if (addr % sizeof(dma_pagetable_entry) == 0) {
|
|
||||||
int index = addr / sizeof(dma_pagetable_entry);
|
|
||||||
memory_region_transaction_begin();
|
|
||||||
rc4030_dma_as_update_one(s, index, (uint32_t)data);
|
|
||||||
memory_region_transaction_commit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const MemoryRegionOps rc4030_dma_tt_ops = {
|
|
||||||
.write = rc4030_dma_tt_write,
|
|
||||||
.impl.min_access_size = 4,
|
|
||||||
.impl.max_access_size = 4,
|
|
||||||
};
|
|
||||||
|
|
||||||
static void rc4030_dma_tt_update(rc4030State *s, uint32_t new_tl_base,
|
|
||||||
uint32_t new_tl_limit)
|
|
||||||
{
|
|
||||||
int entries, i;
|
|
||||||
dma_pagetable_entry *dma_tl_contents;
|
|
||||||
|
|
||||||
if (s->dma_tl_limit) {
|
|
||||||
/* write old dma tl table to physical memory */
|
|
||||||
memory_region_del_subregion(get_system_memory(), &s->dma_tt_alias);
|
|
||||||
cpu_physical_memory_write(s->dma_tl_limit & 0x7fffffff,
|
|
||||||
memory_region_get_ram_ptr(&s->dma_tt),
|
|
||||||
memory_region_size(&s->dma_tt_alias));
|
|
||||||
}
|
|
||||||
object_unparent(OBJECT(&s->dma_tt_alias));
|
|
||||||
|
|
||||||
s->dma_tl_base = new_tl_base;
|
|
||||||
s->dma_tl_limit = new_tl_limit;
|
|
||||||
new_tl_base &= 0x7fffffff;
|
|
||||||
|
|
||||||
if (s->dma_tl_limit) {
|
|
||||||
uint64_t dma_tt_size;
|
|
||||||
if (s->dma_tl_limit <= memory_region_size(&s->dma_tt)) {
|
|
||||||
dma_tt_size = s->dma_tl_limit;
|
|
||||||
} else {
|
|
||||||
dma_tt_size = memory_region_size(&s->dma_tt);
|
|
||||||
}
|
|
||||||
memory_region_init_alias(&s->dma_tt_alias, OBJECT(s),
|
|
||||||
"dma-table-alias",
|
|
||||||
&s->dma_tt, 0, dma_tt_size);
|
|
||||||
dma_tl_contents = memory_region_get_ram_ptr(&s->dma_tt);
|
|
||||||
cpu_physical_memory_read(new_tl_base, dma_tl_contents, dma_tt_size);
|
|
||||||
|
|
||||||
memory_region_transaction_begin();
|
|
||||||
entries = dma_tt_size / sizeof(dma_pagetable_entry);
|
|
||||||
for (i = 0; i < entries; i++) {
|
|
||||||
rc4030_dma_as_update_one(s, i, dma_tl_contents[i].frame);
|
|
||||||
}
|
|
||||||
memory_region_add_subregion(get_system_memory(), new_tl_base,
|
|
||||||
&s->dma_tt_alias);
|
|
||||||
memory_region_transaction_commit();
|
|
||||||
} else {
|
|
||||||
memory_region_init(&s->dma_tt_alias, OBJECT(s),
|
|
||||||
"dma-table-alias", 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rc4030_write(void *opaque, hwaddr addr, uint64_t data,
|
static void rc4030_write(void *opaque, hwaddr addr, uint64_t data,
|
||||||
unsigned int size)
|
unsigned int size)
|
||||||
{
|
{
|
||||||
|
@ -362,11 +264,11 @@ static void rc4030_write(void *opaque, hwaddr addr, uint64_t data,
|
||||||
break;
|
break;
|
||||||
/* DMA transl. table base */
|
/* DMA transl. table base */
|
||||||
case 0x0018:
|
case 0x0018:
|
||||||
rc4030_dma_tt_update(s, val, s->dma_tl_limit);
|
s->dma_tl_base = val;
|
||||||
break;
|
break;
|
||||||
/* DMA transl. table limit */
|
/* DMA transl. table limit */
|
||||||
case 0x0020:
|
case 0x0020:
|
||||||
rc4030_dma_tt_update(s, s->dma_tl_base, val);
|
s->dma_tl_limit = val;
|
||||||
break;
|
break;
|
||||||
/* DMA transl. table invalidated */
|
/* DMA transl. table invalidated */
|
||||||
case 0x0028:
|
case 0x0028:
|
||||||
|
@ -586,6 +488,38 @@ static const MemoryRegionOps jazzio_ops = {
|
||||||
.endianness = DEVICE_NATIVE_ENDIAN,
|
.endianness = DEVICE_NATIVE_ENDIAN,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static IOMMUTLBEntry rc4030_dma_translate(MemoryRegion *iommu, hwaddr addr,
|
||||||
|
bool is_write)
|
||||||
|
{
|
||||||
|
rc4030State *s = container_of(iommu, rc4030State, dma_mr);
|
||||||
|
IOMMUTLBEntry ret = {
|
||||||
|
.target_as = &address_space_memory,
|
||||||
|
.iova = addr & ~(DMA_PAGESIZE - 1),
|
||||||
|
.translated_addr = 0,
|
||||||
|
.addr_mask = DMA_PAGESIZE - 1,
|
||||||
|
.perm = IOMMU_NONE,
|
||||||
|
};
|
||||||
|
uint64_t i, entry_address;
|
||||||
|
dma_pagetable_entry entry;
|
||||||
|
|
||||||
|
i = addr / DMA_PAGESIZE;
|
||||||
|
if (i < s->dma_tl_limit / sizeof(entry)) {
|
||||||
|
entry_address = (s->dma_tl_base & 0x7fffffff) + i * sizeof(entry);
|
||||||
|
if (address_space_read(ret.target_as, entry_address,
|
||||||
|
MEMTXATTRS_UNSPECIFIED, (unsigned char *)&entry,
|
||||||
|
sizeof(entry)) == MEMTX_OK) {
|
||||||
|
ret.translated_addr = entry.frame & ~(DMA_PAGESIZE - 1);
|
||||||
|
ret.perm = IOMMU_RW;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const MemoryRegionIOMMUOps rc4030_dma_ops = {
|
||||||
|
.translate = rc4030_dma_translate,
|
||||||
|
};
|
||||||
|
|
||||||
static void rc4030_reset(DeviceState *dev)
|
static void rc4030_reset(DeviceState *dev)
|
||||||
{
|
{
|
||||||
rc4030State *s = RC4030(dev);
|
rc4030State *s = RC4030(dev);
|
||||||
|
@ -596,7 +530,6 @@ static void rc4030_reset(DeviceState *dev)
|
||||||
s->invalid_address_register = 0;
|
s->invalid_address_register = 0;
|
||||||
|
|
||||||
memset(s->dma_regs, 0, sizeof(s->dma_regs));
|
memset(s->dma_regs, 0, sizeof(s->dma_regs));
|
||||||
rc4030_dma_tt_update(s, 0, 0);
|
|
||||||
|
|
||||||
s->remote_failed_address = s->memory_failed_address = 0;
|
s->remote_failed_address = s->memory_failed_address = 0;
|
||||||
s->cache_maint = 0;
|
s->cache_maint = 0;
|
||||||
|
@ -735,7 +668,6 @@ static void rc4030_realize(DeviceState *dev, Error **errp)
|
||||||
{
|
{
|
||||||
rc4030State *s = RC4030(dev);
|
rc4030State *s = RC4030(dev);
|
||||||
Object *o = OBJECT(dev);
|
Object *o = OBJECT(dev);
|
||||||
int i;
|
|
||||||
|
|
||||||
s->periodic_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
|
s->periodic_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
|
||||||
rc4030_periodic_timer, s);
|
rc4030_periodic_timer, s);
|
||||||
|
@ -745,37 +677,19 @@ static void rc4030_realize(DeviceState *dev, Error **errp)
|
||||||
memory_region_init_io(&s->iomem_jazzio, NULL, &jazzio_ops, s,
|
memory_region_init_io(&s->iomem_jazzio, NULL, &jazzio_ops, s,
|
||||||
"rc4030.jazzio", 0x00001000);
|
"rc4030.jazzio", 0x00001000);
|
||||||
|
|
||||||
memory_region_init_rom_device(&s->dma_tt, o,
|
memory_region_init_iommu(&s->dma_mr, o, &rc4030_dma_ops,
|
||||||
&rc4030_dma_tt_ops, s, "dma-table",
|
"rc4030.dma", UINT32_MAX);
|
||||||
MAX_TL_ENTRIES * sizeof(dma_pagetable_entry),
|
|
||||||
NULL);
|
|
||||||
memory_region_init(&s->dma_tt_alias, o, "dma-table-alias", 0);
|
|
||||||
memory_region_init(&s->dma_mr, o, "dma", INT32_MAX);
|
|
||||||
for (i = 0; i < MAX_TL_ENTRIES; ++i) {
|
|
||||||
memory_region_init_alias(&s->dma_mrs[i], o, "dma-alias",
|
|
||||||
get_system_memory(), 0, DMA_PAGESIZE);
|
|
||||||
memory_region_set_enabled(&s->dma_mrs[i], false);
|
|
||||||
memory_region_add_subregion(&s->dma_mr, i * DMA_PAGESIZE,
|
|
||||||
&s->dma_mrs[i]);
|
|
||||||
}
|
|
||||||
address_space_init(&s->dma_as, &s->dma_mr, "rc4030-dma");
|
address_space_init(&s->dma_as, &s->dma_mr, "rc4030-dma");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rc4030_unrealize(DeviceState *dev, Error **errp)
|
static void rc4030_unrealize(DeviceState *dev, Error **errp)
|
||||||
{
|
{
|
||||||
rc4030State *s = RC4030(dev);
|
rc4030State *s = RC4030(dev);
|
||||||
int i;
|
|
||||||
|
|
||||||
timer_free(s->periodic_timer);
|
timer_free(s->periodic_timer);
|
||||||
|
|
||||||
address_space_destroy(&s->dma_as);
|
address_space_destroy(&s->dma_as);
|
||||||
object_unparent(OBJECT(&s->dma_tt));
|
|
||||||
object_unparent(OBJECT(&s->dma_tt_alias));
|
|
||||||
object_unparent(OBJECT(&s->dma_mr));
|
object_unparent(OBJECT(&s->dma_mr));
|
||||||
for (i = 0; i < MAX_TL_ENTRIES; ++i) {
|
|
||||||
memory_region_del_subregion(&s->dma_mr, &s->dma_mrs[i]);
|
|
||||||
object_unparent(OBJECT(&s->dma_mrs[i]));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rc4030_class_init(ObjectClass *klass, void *class_data)
|
static void rc4030_class_init(ObjectClass *klass, void *class_data)
|
||||||
|
|
Loading…
Reference in New Issue