Commit db7b5426a4b424249b4aba3496bf14da69a6625b
1 parent
57074f98
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
Showing
2 changed files
with
225 additions
and
7 deletions
cpu-all.h
| ... | ... | @@ -858,6 +858,7 @@ extern uint8_t *phys_ram_dirty; |
| 858 | 858 | exception, the write memory callback gets the ram offset instead of |
| 859 | 859 | the physical address */ |
| 860 | 860 | #define IO_MEM_ROMD (1) |
| 861 | +#define IO_MEM_SUBPAGE (2) | |
| 861 | 862 | |
| 862 | 863 | typedef void CPUWriteMemoryFunc(void *opaque, target_phys_addr_t addr, uint32_t value); |
| 863 | 864 | typedef uint32_t CPUReadMemoryFunc(void *opaque, target_phys_addr_t addr); | ... | ... |
exec.c
| ... | ... | @@ -48,6 +48,7 @@ |
| 48 | 48 | //#define DEBUG_TLB_CHECK |
| 49 | 49 | |
| 50 | 50 | //#define DEBUG_IOPORT |
| 51 | +//#define DEBUG_SUBPAGE | |
| 51 | 52 | |
| 52 | 53 | #if !defined(CONFIG_USER_ONLY) |
| 53 | 54 | /* TB consistency checks only implemented for usermode emulation. */ |
| ... | ... | @@ -157,6 +158,14 @@ static int tlb_flush_count; |
| 157 | 158 | static int tb_flush_count; |
| 158 | 159 | static int tb_phys_invalidate_count; |
| 159 | 160 | |
| 161 | +#define SUBPAGE_IDX(addr) ((addr) & ~TARGET_PAGE_MASK) | |
| 162 | +typedef struct subpage_t { | |
| 163 | + target_phys_addr_t base; | |
| 164 | + CPUReadMemoryFunc **mem_read[TARGET_PAGE_SIZE]; | |
| 165 | + CPUWriteMemoryFunc **mem_write[TARGET_PAGE_SIZE]; | |
| 166 | + void *opaque[TARGET_PAGE_SIZE]; | |
| 167 | +} subpage_t; | |
| 168 | + | |
| 160 | 169 | static void page_init(void) |
| 161 | 170 | { |
| 162 | 171 | /* NOTE: we can always suppose that qemu_host_page_size >= |
| ... | ... | @@ -1898,6 +1907,30 @@ static inline void tlb_set_dirty(CPUState *env, |
| 1898 | 1907 | } |
| 1899 | 1908 | #endif /* defined(CONFIG_USER_ONLY) */ |
| 1900 | 1909 | |
| 1910 | +static int subpage_register (subpage_t *mmio, uint32_t start, uint32_t end, | |
| 1911 | + int memory); | |
| 1912 | +static void *subpage_init (target_phys_addr_t base, uint32_t *phys, | |
| 1913 | + int orig_memory); | |
| 1914 | +#define CHECK_SUBPAGE(addr, start_addr, start_addr2, end_addr, end_addr2, \ | |
| 1915 | + need_subpage) \ | |
| 1916 | + do { \ | |
| 1917 | + if (addr > start_addr) \ | |
| 1918 | + start_addr2 = 0; \ | |
| 1919 | + else { \ | |
| 1920 | + start_addr2 = start_addr & ~TARGET_PAGE_MASK; \ | |
| 1921 | + if (start_addr2 > 0) \ | |
| 1922 | + need_subpage = 1; \ | |
| 1923 | + } \ | |
| 1924 | + \ | |
| 1925 | + if (end_addr - addr > TARGET_PAGE_SIZE) \ | |
| 1926 | + end_addr2 = TARGET_PAGE_SIZE - 1; \ | |
| 1927 | + else { \ | |
| 1928 | + end_addr2 = (start_addr + orig_size - 1) & ~TARGET_PAGE_MASK; \ | |
| 1929 | + if (end_addr2 < TARGET_PAGE_SIZE - 1) \ | |
| 1930 | + need_subpage = 1; \ | |
| 1931 | + } \ | |
| 1932 | + } while (0) | |
| 1933 | + | |
| 1901 | 1934 | /* register physical memory. 'size' must be a multiple of the target |
| 1902 | 1935 | page size. If (phys_offset & ~TARGET_PAGE_MASK) != 0, then it is an |
| 1903 | 1936 | io memory page */ |
| ... | ... | @@ -1908,15 +1941,56 @@ void cpu_register_physical_memory(target_phys_addr_t start_addr, |
| 1908 | 1941 | target_phys_addr_t addr, end_addr; |
| 1909 | 1942 | PhysPageDesc *p; |
| 1910 | 1943 | CPUState *env; |
| 1944 | + unsigned long orig_size = size; | |
| 1945 | + void *subpage; | |
| 1911 | 1946 | |
| 1947 | + end_addr = start_addr + (target_phys_addr_t)size; | |
| 1912 | 1948 | size = (size + TARGET_PAGE_SIZE - 1) & TARGET_PAGE_MASK; |
| 1913 | - end_addr = start_addr + size; | |
| 1914 | - for(addr = start_addr; addr != end_addr; addr += TARGET_PAGE_SIZE) { | |
| 1915 | - p = phys_page_find_alloc(addr >> TARGET_PAGE_BITS, 1); | |
| 1916 | - p->phys_offset = phys_offset; | |
| 1917 | - if ((phys_offset & ~TARGET_PAGE_MASK) <= IO_MEM_ROM || | |
| 1918 | - (phys_offset & IO_MEM_ROMD)) | |
| 1919 | - phys_offset += TARGET_PAGE_SIZE; | |
| 1949 | + for(addr = start_addr; addr < end_addr; addr += TARGET_PAGE_SIZE) { | |
| 1950 | + p = phys_page_find(addr >> TARGET_PAGE_BITS); | |
| 1951 | + if (p && p->phys_offset != IO_MEM_UNASSIGNED) { | |
| 1952 | + unsigned long orig_memory = p->phys_offset; | |
| 1953 | + target_phys_addr_t start_addr2, end_addr2; | |
| 1954 | + int need_subpage = 0; | |
| 1955 | + | |
| 1956 | + CHECK_SUBPAGE(addr, start_addr, start_addr2, end_addr, end_addr2, | |
| 1957 | + need_subpage); | |
| 1958 | + if (need_subpage) { | |
| 1959 | + if (!(orig_memory & IO_MEM_SUBPAGE)) { | |
| 1960 | + subpage = subpage_init((addr & TARGET_PAGE_MASK), | |
| 1961 | + &p->phys_offset, orig_memory); | |
| 1962 | + } else { | |
| 1963 | + subpage = io_mem_opaque[(orig_memory & ~TARGET_PAGE_MASK) | |
| 1964 | + >> IO_MEM_SHIFT]; | |
| 1965 | + } | |
| 1966 | + subpage_register(subpage, start_addr2, end_addr2, phys_offset); | |
| 1967 | + } else { | |
| 1968 | + p->phys_offset = phys_offset; | |
| 1969 | + if ((phys_offset & ~TARGET_PAGE_MASK) <= IO_MEM_ROM || | |
| 1970 | + (phys_offset & IO_MEM_ROMD)) | |
| 1971 | + phys_offset += TARGET_PAGE_SIZE; | |
| 1972 | + } | |
| 1973 | + } else { | |
| 1974 | + p = phys_page_find_alloc(addr >> TARGET_PAGE_BITS, 1); | |
| 1975 | + p->phys_offset = phys_offset; | |
| 1976 | + if ((phys_offset & ~TARGET_PAGE_MASK) <= IO_MEM_ROM || | |
| 1977 | + (phys_offset & IO_MEM_ROMD)) | |
| 1978 | + phys_offset += TARGET_PAGE_SIZE; | |
| 1979 | + else { | |
| 1980 | + target_phys_addr_t start_addr2, end_addr2; | |
| 1981 | + int need_subpage = 0; | |
| 1982 | + | |
| 1983 | + CHECK_SUBPAGE(addr, start_addr, start_addr2, end_addr, | |
| 1984 | + end_addr2, need_subpage); | |
| 1985 | + | |
| 1986 | + if (need_subpage) { | |
| 1987 | + subpage = subpage_init((addr & TARGET_PAGE_MASK), | |
| 1988 | + &p->phys_offset, IO_MEM_UNASSIGNED); | |
| 1989 | + subpage_register(subpage, start_addr2, end_addr2, | |
| 1990 | + phys_offset); | |
| 1991 | + } | |
| 1992 | + } | |
| 1993 | + } | |
| 1920 | 1994 | } |
| 1921 | 1995 | |
| 1922 | 1996 | /* since each CPU stores ram addresses in its TLB cache, we must |
| ... | ... | @@ -2158,6 +2232,149 @@ static CPUWriteMemoryFunc *watch_mem_write[3] = { |
| 2158 | 2232 | }; |
| 2159 | 2233 | #endif |
| 2160 | 2234 | |
| 2235 | +static inline uint32_t subpage_readlen (subpage_t *mmio, target_phys_addr_t addr, | |
| 2236 | + unsigned int len) | |
| 2237 | +{ | |
| 2238 | + CPUReadMemoryFunc **mem_read; | |
| 2239 | + uint32_t ret; | |
| 2240 | + unsigned int idx; | |
| 2241 | + | |
| 2242 | + idx = SUBPAGE_IDX(addr - mmio->base); | |
| 2243 | +#if defined(DEBUG_SUBPAGE) | |
| 2244 | + printf("%s: subpage %p len %d addr " TARGET_FMT_plx " idx %d\n", __func__, | |
| 2245 | + mmio, len, addr, idx); | |
| 2246 | +#endif | |
| 2247 | + mem_read = mmio->mem_read[idx]; | |
| 2248 | + ret = (*mem_read[len])(mmio->opaque[idx], addr); | |
| 2249 | + | |
| 2250 | + return ret; | |
| 2251 | +} | |
| 2252 | + | |
| 2253 | +static inline void subpage_writelen (subpage_t *mmio, target_phys_addr_t addr, | |
| 2254 | + uint32_t value, unsigned int len) | |
| 2255 | +{ | |
| 2256 | + CPUWriteMemoryFunc **mem_write; | |
| 2257 | + unsigned int idx; | |
| 2258 | + | |
| 2259 | + idx = SUBPAGE_IDX(addr - mmio->base); | |
| 2260 | +#if defined(DEBUG_SUBPAGE) | |
| 2261 | + printf("%s: subpage %p len %d addr " TARGET_FMT_plx " idx %d value %08x\n", __func__, | |
| 2262 | + mmio, len, addr, idx, value); | |
| 2263 | +#endif | |
| 2264 | + mem_write = mmio->mem_write[idx]; | |
| 2265 | + (*mem_write[len])(mmio->opaque[idx], addr, value); | |
| 2266 | +} | |
| 2267 | + | |
| 2268 | +static uint32_t subpage_readb (void *opaque, target_phys_addr_t addr) | |
| 2269 | +{ | |
| 2270 | +#if defined(DEBUG_SUBPAGE) | |
| 2271 | + printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr); | |
| 2272 | +#endif | |
| 2273 | + | |
| 2274 | + return subpage_readlen(opaque, addr, 0); | |
| 2275 | +} | |
| 2276 | + | |
| 2277 | +static void subpage_writeb (void *opaque, target_phys_addr_t addr, | |
| 2278 | + uint32_t value) | |
| 2279 | +{ | |
| 2280 | +#if defined(DEBUG_SUBPAGE) | |
| 2281 | + printf("%s: addr " TARGET_FMT_plx " val %08x\n", __func__, addr, value); | |
| 2282 | +#endif | |
| 2283 | + subpage_writelen(opaque, addr, value, 0); | |
| 2284 | +} | |
| 2285 | + | |
| 2286 | +static uint32_t subpage_readw (void *opaque, target_phys_addr_t addr) | |
| 2287 | +{ | |
| 2288 | +#if defined(DEBUG_SUBPAGE) | |
| 2289 | + printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr); | |
| 2290 | +#endif | |
| 2291 | + | |
| 2292 | + return subpage_readlen(opaque, addr, 1); | |
| 2293 | +} | |
| 2294 | + | |
| 2295 | +static void subpage_writew (void *opaque, target_phys_addr_t addr, | |
| 2296 | + uint32_t value) | |
| 2297 | +{ | |
| 2298 | +#if defined(DEBUG_SUBPAGE) | |
| 2299 | + printf("%s: addr " TARGET_FMT_plx " val %08x\n", __func__, addr, value); | |
| 2300 | +#endif | |
| 2301 | + subpage_writelen(opaque, addr, value, 1); | |
| 2302 | +} | |
| 2303 | + | |
| 2304 | +static uint32_t subpage_readl (void *opaque, target_phys_addr_t addr) | |
| 2305 | +{ | |
| 2306 | +#if defined(DEBUG_SUBPAGE) | |
| 2307 | + printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr); | |
| 2308 | +#endif | |
| 2309 | + | |
| 2310 | + return subpage_readlen(opaque, addr, 2); | |
| 2311 | +} | |
| 2312 | + | |
| 2313 | +static void subpage_writel (void *opaque, | |
| 2314 | + target_phys_addr_t addr, uint32_t value) | |
| 2315 | +{ | |
| 2316 | +#if defined(DEBUG_SUBPAGE) | |
| 2317 | + printf("%s: addr " TARGET_FMT_plx " val %08x\n", __func__, addr, value); | |
| 2318 | +#endif | |
| 2319 | + subpage_writelen(opaque, addr, value, 2); | |
| 2320 | +} | |
| 2321 | + | |
| 2322 | +static CPUReadMemoryFunc *subpage_read[] = { | |
| 2323 | + &subpage_readb, | |
| 2324 | + &subpage_readw, | |
| 2325 | + &subpage_readl, | |
| 2326 | +}; | |
| 2327 | + | |
| 2328 | +static CPUWriteMemoryFunc *subpage_write[] = { | |
| 2329 | + &subpage_writeb, | |
| 2330 | + &subpage_writew, | |
| 2331 | + &subpage_writel, | |
| 2332 | +}; | |
| 2333 | + | |
| 2334 | +static int subpage_register (subpage_t *mmio, uint32_t start, uint32_t end, | |
| 2335 | + int memory) | |
| 2336 | +{ | |
| 2337 | + int idx, eidx; | |
| 2338 | + | |
| 2339 | + if (start >= TARGET_PAGE_SIZE || end >= TARGET_PAGE_SIZE) | |
| 2340 | + return -1; | |
| 2341 | + idx = SUBPAGE_IDX(start); | |
| 2342 | + eidx = SUBPAGE_IDX(end); | |
| 2343 | +#if defined(DEBUG_SUBPAGE) | |
| 2344 | + printf("%s: %p start %08x end %08x idx %08x eidx %08x mem %d\n", __func__, | |
| 2345 | + mmio, start, end, idx, eidx, memory); | |
| 2346 | +#endif | |
| 2347 | + memory >>= IO_MEM_SHIFT; | |
| 2348 | + for (; idx <= eidx; idx++) { | |
| 2349 | + mmio->mem_read[idx] = io_mem_read[memory]; | |
| 2350 | + mmio->mem_write[idx] = io_mem_write[memory]; | |
| 2351 | + mmio->opaque[idx] = io_mem_opaque[memory]; | |
| 2352 | + } | |
| 2353 | + | |
| 2354 | + return 0; | |
| 2355 | +} | |
| 2356 | + | |
| 2357 | +static void *subpage_init (target_phys_addr_t base, uint32_t *phys, | |
| 2358 | + int orig_memory) | |
| 2359 | +{ | |
| 2360 | + subpage_t *mmio; | |
| 2361 | + int subpage_memory; | |
| 2362 | + | |
| 2363 | + mmio = qemu_mallocz(sizeof(subpage_t)); | |
| 2364 | + if (mmio != NULL) { | |
| 2365 | + mmio->base = base; | |
| 2366 | + subpage_memory = cpu_register_io_memory(0, subpage_read, subpage_write, mmio); | |
| 2367 | +#if defined(DEBUG_SUBPAGE) | |
| 2368 | + printf("%s: %p base " TARGET_FMT_plx " len %08x %d\n", __func__, | |
| 2369 | + mmio, base, TARGET_PAGE_SIZE, subpage_memory); | |
| 2370 | +#endif | |
| 2371 | + *phys = subpage_memory | IO_MEM_SUBPAGE; | |
| 2372 | + subpage_register(mmio, 0, TARGET_PAGE_SIZE - 1, orig_memory); | |
| 2373 | + } | |
| 2374 | + | |
| 2375 | + return mmio; | |
| 2376 | +} | |
| 2377 | + | |
| 2161 | 2378 | static void io_mem_init(void) |
| 2162 | 2379 | { |
| 2163 | 2380 | cpu_register_io_memory(IO_MEM_ROM >> IO_MEM_SHIFT, error_mem_read, unassigned_mem_write, NULL); | ... | ... |