Commit db7b5426 authored by blueswir1's avatar blueswir1
Browse files

Implement generic sub-page I/O based on earlier work by J. Mayer.


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2868 c046a42c-6fe2-441c-8c8c-71466251a162
parent 57074f98
......@@ -858,6 +858,7 @@ extern uint8_t *phys_ram_dirty;
exception, the write memory callback gets the ram offset instead of
the physical address */
#define IO_MEM_ROMD (1)
#define IO_MEM_SUBPAGE (2)
typedef void CPUWriteMemoryFunc(void *opaque, target_phys_addr_t addr, uint32_t value);
typedef uint32_t CPUReadMemoryFunc(void *opaque, target_phys_addr_t addr);
......
......@@ -48,6 +48,7 @@
//#define DEBUG_TLB_CHECK
//#define DEBUG_IOPORT
//#define DEBUG_SUBPAGE
#if !defined(CONFIG_USER_ONLY)
/* TB consistency checks only implemented for usermode emulation. */
......@@ -157,6 +158,14 @@ static int tlb_flush_count;
static int tb_flush_count;
static int tb_phys_invalidate_count;
#define SUBPAGE_IDX(addr) ((addr) & ~TARGET_PAGE_MASK)
typedef struct subpage_t {
target_phys_addr_t base;
CPUReadMemoryFunc **mem_read[TARGET_PAGE_SIZE];
CPUWriteMemoryFunc **mem_write[TARGET_PAGE_SIZE];
void *opaque[TARGET_PAGE_SIZE];
} subpage_t;
static void page_init(void)
{
/* NOTE: we can always suppose that qemu_host_page_size >=
......@@ -1898,6 +1907,30 @@ static inline void tlb_set_dirty(CPUState *env,
}
#endif /* defined(CONFIG_USER_ONLY) */
static int subpage_register (subpage_t *mmio, uint32_t start, uint32_t end,
int memory);
static void *subpage_init (target_phys_addr_t base, uint32_t *phys,
int orig_memory);
#define CHECK_SUBPAGE(addr, start_addr, start_addr2, end_addr, end_addr2, \
need_subpage) \
do { \
if (addr > start_addr) \
start_addr2 = 0; \
else { \
start_addr2 = start_addr & ~TARGET_PAGE_MASK; \
if (start_addr2 > 0) \
need_subpage = 1; \
} \
\
if (end_addr - addr > TARGET_PAGE_SIZE) \
end_addr2 = TARGET_PAGE_SIZE - 1; \
else { \
end_addr2 = (start_addr + orig_size - 1) & ~TARGET_PAGE_MASK; \
if (end_addr2 < TARGET_PAGE_SIZE - 1) \
need_subpage = 1; \
} \
} while (0)
/* register physical memory. 'size' must be a multiple of the target
page size. If (phys_offset & ~TARGET_PAGE_MASK) != 0, then it is an
io memory page */
......@@ -1908,15 +1941,56 @@ void cpu_register_physical_memory(target_phys_addr_t start_addr,
target_phys_addr_t addr, end_addr;
PhysPageDesc *p;
CPUState *env;
unsigned long orig_size = size;
void *subpage;
end_addr = start_addr + (target_phys_addr_t)size;
size = (size + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK;
end_addr = start_addr + size;
for(addr = start_addr; addr != end_addr; addr += TARGET_PAGE_SIZE) {
p = phys_page_find_alloc(addr >> TARGET_PAGE_BITS, 1);
p->phys_offset = phys_offset;
if ((phys_offset & ~TARGET_PAGE_MASK) <= IO_MEM_ROM ||
(phys_offset & IO_MEM_ROMD))
phys_offset += TARGET_PAGE_SIZE;
for(addr = start_addr; addr < end_addr; addr += TARGET_PAGE_SIZE) {
p = phys_page_find(addr >> TARGET_PAGE_BITS);
if (p && p->phys_offset != IO_MEM_UNASSIGNED) {
unsigned long orig_memory = p->phys_offset;
target_phys_addr_t start_addr2, end_addr2;
int need_subpage = 0;
CHECK_SUBPAGE(addr, start_addr, start_addr2, end_addr, end_addr2,
need_subpage);
if (need_subpage) {
if (!(orig_memory & IO_MEM_SUBPAGE)) {
subpage = subpage_init((addr & TARGET_PAGE_MASK),
&p->phys_offset, orig_memory);
} else {
subpage = io_mem_opaque[(orig_memory & ~TARGET_PAGE_MASK)
>> IO_MEM_SHIFT];
}
subpage_register(subpage, start_addr2, end_addr2, phys_offset);
} else {
p->phys_offset = phys_offset;
if ((phys_offset & ~TARGET_PAGE_MASK) <= IO_MEM_ROM ||
(phys_offset & IO_MEM_ROMD))
phys_offset += TARGET_PAGE_SIZE;
}
} else {
p = phys_page_find_alloc(addr >> TARGET_PAGE_BITS, 1);
p->phys_offset = phys_offset;
if ((phys_offset & ~TARGET_PAGE_MASK) <= IO_MEM_ROM ||
(phys_offset & IO_MEM_ROMD))
phys_offset += TARGET_PAGE_SIZE;
else {
target_phys_addr_t start_addr2, end_addr2;
int need_subpage = 0;
CHECK_SUBPAGE(addr, start_addr, start_addr2, end_addr,
end_addr2, need_subpage);
if (need_subpage) {
subpage = subpage_init((addr & TARGET_PAGE_MASK),
&p->phys_offset, IO_MEM_UNASSIGNED);
subpage_register(subpage, start_addr2, end_addr2,
phys_offset);
}
}
}
}
/* since each CPU stores ram addresses in its TLB cache, we must
......@@ -2158,6 +2232,149 @@ static CPUWriteMemoryFunc *watch_mem_write[3] = {
};
#endif
static inline uint32_t subpage_readlen (subpage_t *mmio, target_phys_addr_t addr,
unsigned int len)
{
CPUReadMemoryFunc **mem_read;
uint32_t ret;
unsigned int idx;
idx = SUBPAGE_IDX(addr - mmio->base);
#if defined(DEBUG_SUBPAGE)
printf("%s: subpage %p len %d addr " TARGET_FMT_plx " idx %d\n", __func__,
mmio, len, addr, idx);
#endif
mem_read = mmio->mem_read[idx];
ret = (*mem_read[len])(mmio->opaque[idx], addr);
return ret;
}
static inline void subpage_writelen (subpage_t *mmio, target_phys_addr_t addr,
uint32_t value, unsigned int len)
{
CPUWriteMemoryFunc **mem_write;
unsigned int idx;
idx = SUBPAGE_IDX(addr - mmio->base);
#if defined(DEBUG_SUBPAGE)
printf("%s: subpage %p len %d addr " TARGET_FMT_plx " idx %d value %08x\n", __func__,
mmio, len, addr, idx, value);
#endif
mem_write = mmio->mem_write[idx];
(*mem_write[len])(mmio->opaque[idx], addr, value);
}
static uint32_t subpage_readb (void *opaque, target_phys_addr_t addr)
{
#if defined(DEBUG_SUBPAGE)
printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
#endif
return subpage_readlen(opaque, addr, 0);
}
static void subpage_writeb (void *opaque, target_phys_addr_t addr,
uint32_t value)
{
#if defined(DEBUG_SUBPAGE)
printf("%s: addr " TARGET_FMT_plx " val %08x\n", __func__, addr, value);
#endif
subpage_writelen(opaque, addr, value, 0);
}
static uint32_t subpage_readw (void *opaque, target_phys_addr_t addr)
{
#if defined(DEBUG_SUBPAGE)
printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
#endif
return subpage_readlen(opaque, addr, 1);
}
static void subpage_writew (void *opaque, target_phys_addr_t addr,
uint32_t value)
{
#if defined(DEBUG_SUBPAGE)
printf("%s: addr " TARGET_FMT_plx " val %08x\n", __func__, addr, value);
#endif
subpage_writelen(opaque, addr, value, 1);
}
static uint32_t subpage_readl (void *opaque, target_phys_addr_t addr)
{
#if defined(DEBUG_SUBPAGE)
printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr);
#endif
return subpage_readlen(opaque, addr, 2);
}
static void subpage_writel (void *opaque,
target_phys_addr_t addr, uint32_t value)
{
#if defined(DEBUG_SUBPAGE)
printf("%s: addr " TARGET_FMT_plx " val %08x\n", __func__, addr, value);
#endif
subpage_writelen(opaque, addr, value, 2);
}
static CPUReadMemoryFunc *subpage_read[] = {
&subpage_readb,
&subpage_readw,
&subpage_readl,
};
static CPUWriteMemoryFunc *subpage_write[] = {
&subpage_writeb,
&subpage_writew,
&subpage_writel,
};
static int subpage_register (subpage_t *mmio, uint32_t start, uint32_t end,
int memory)
{
int idx, eidx;
if (start >= TARGET_PAGE_SIZE || end >= TARGET_PAGE_SIZE)
return -1;
idx = SUBPAGE_IDX(start);
eidx = SUBPAGE_IDX(end);
#if defined(DEBUG_SUBPAGE)
printf("%s: %p start %08x end %08x idx %08x eidx %08x mem %d\n", __func__,
mmio, start, end, idx, eidx, memory);
#endif
memory >>= IO_MEM_SHIFT;
for (; idx <= eidx; idx++) {
mmio->mem_read[idx] = io_mem_read[memory];
mmio->mem_write[idx] = io_mem_write[memory];
mmio->opaque[idx] = io_mem_opaque[memory];
}
return 0;
}
static void *subpage_init (target_phys_addr_t base, uint32_t *phys,
int orig_memory)
{
subpage_t *mmio;
int subpage_memory;
mmio = qemu_mallocz(sizeof(subpage_t));
if (mmio != NULL) {
mmio->base = base;
subpage_memory = cpu_register_io_memory(0, subpage_read, subpage_write, mmio);
#if defined(DEBUG_SUBPAGE)
printf("%s: %p base " TARGET_FMT_plx " len %08x %d\n", __func__,
mmio, base, TARGET_PAGE_SIZE, subpage_memory);
#endif
*phys = subpage_memory | IO_MEM_SUBPAGE;
subpage_register(mmio, 0, TARGET_PAGE_SIZE - 1, orig_memory);
}
return mmio;
}
static void io_mem_init(void)
{
cpu_register_io_memory(IO_MEM_ROM >> IO_MEM_SHIFT, error_mem_read, unassigned_mem_write, NULL);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment