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
This commit is contained in:
parent
57074f98bb
commit
db7b5426a4
@ -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);
|
||||
|
231
exec.c
231
exec.c
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user