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); | ... | ... |