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
|
exception, the write memory callback gets the ram offset instead of
|
||||||
the physical address */
|
the physical address */
|
||||||
#define IO_MEM_ROMD (1)
|
#define IO_MEM_ROMD (1)
|
||||||
|
#define IO_MEM_SUBPAGE (2)
|
||||||
|
|
||||||
typedef void CPUWriteMemoryFunc(void *opaque, target_phys_addr_t addr, uint32_t value);
|
typedef void CPUWriteMemoryFunc(void *opaque, target_phys_addr_t addr, uint32_t value);
|
||||||
typedef uint32_t CPUReadMemoryFunc(void *opaque, target_phys_addr_t addr);
|
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_TLB_CHECK
|
||||||
|
|
||||||
//#define DEBUG_IOPORT
|
//#define DEBUG_IOPORT
|
||||||
|
//#define DEBUG_SUBPAGE
|
||||||
|
|
||||||
#if !defined(CONFIG_USER_ONLY)
|
#if !defined(CONFIG_USER_ONLY)
|
||||||
/* TB consistency checks only implemented for usermode emulation. */
|
/* 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_flush_count;
|
||||||
static int tb_phys_invalidate_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)
|
static void page_init(void)
|
||||||
{
|
{
|
||||||
/* NOTE: we can always suppose that qemu_host_page_size >=
|
/* 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) */
|
#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
|
/* register physical memory. 'size' must be a multiple of the target
|
||||||
page size. If (phys_offset & ~TARGET_PAGE_MASK) != 0, then it is an
|
page size. If (phys_offset & ~TARGET_PAGE_MASK) != 0, then it is an
|
||||||
io memory page */
|
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;
|
target_phys_addr_t addr, end_addr;
|
||||||
PhysPageDesc *p;
|
PhysPageDesc *p;
|
||||||
CPUState *env;
|
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;
|
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) {
|
||||||
for(addr = start_addr; addr != end_addr; addr += TARGET_PAGE_SIZE) {
|
p = phys_page_find(addr >> TARGET_PAGE_BITS);
|
||||||
p = phys_page_find_alloc(addr >> TARGET_PAGE_BITS, 1);
|
if (p && p->phys_offset != IO_MEM_UNASSIGNED) {
|
||||||
p->phys_offset = phys_offset;
|
unsigned long orig_memory = p->phys_offset;
|
||||||
if ((phys_offset & ~TARGET_PAGE_MASK) <= IO_MEM_ROM ||
|
target_phys_addr_t start_addr2, end_addr2;
|
||||||
(phys_offset & IO_MEM_ROMD))
|
int need_subpage = 0;
|
||||||
phys_offset += TARGET_PAGE_SIZE;
|
|
||||||
|
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
|
/* since each CPU stores ram addresses in its TLB cache, we must
|
||||||
@ -2158,6 +2232,149 @@ static CPUWriteMemoryFunc *watch_mem_write[3] = {
|
|||||||
};
|
};
|
||||||
#endif
|
#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)
|
static void io_mem_init(void)
|
||||||
{
|
{
|
||||||
cpu_register_io_memory(IO_MEM_ROM >> IO_MEM_SHIFT, error_mem_read, unassigned_mem_write, NULL);
|
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