Commit 6d16c2f88f2a866bec27c4d170ddd97ee8e41a0e
1 parent
d268de04
Add target memory mapping API (Avi Kivity)
Devices accessing large amounts of memory (as with DMA) will wish to obtain a pointer to guest memory rather than access it indirectly via cpu_physical_memory_rw(). Add a new API to convert target addresses to host pointers. In case the target address does not correspond to RAM, a bounce buffer is allocated. To prevent the guest from causing the host to allocate unbounded amounts of bounce buffer, this memory is limited (currently to one page). Signed-off-by: Avi Kivity <avi@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com> git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@6394 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
2 changed files
with
108 additions
and
0 deletions
cpu-all.h
| ... | ... | @@ -923,6 +923,12 @@ static inline void cpu_physical_memory_write(target_phys_addr_t addr, |
| 923 | 923 | { |
| 924 | 924 | cpu_physical_memory_rw(addr, (uint8_t *)buf, len, 1); |
| 925 | 925 | } |
| 926 | +void *cpu_physical_memory_map(target_phys_addr_t addr, | |
| 927 | + target_phys_addr_t *plen, | |
| 928 | + int is_write); | |
| 929 | +void cpu_physical_memory_unmap(void *buffer, target_phys_addr_t len, | |
| 930 | + int is_write, target_phys_addr_t access_len); | |
| 931 | + | |
| 926 | 932 | uint32_t ldub_phys(target_phys_addr_t addr); |
| 927 | 933 | uint32_t lduw_phys(target_phys_addr_t addr); |
| 928 | 934 | uint32_t ldl_phys(target_phys_addr_t addr); | ... | ... |
exec.c
| ... | ... | @@ -3045,6 +3045,108 @@ void cpu_physical_memory_write_rom(target_phys_addr_t addr, |
| 3045 | 3045 | } |
| 3046 | 3046 | } |
| 3047 | 3047 | |
| 3048 | +typedef struct { | |
| 3049 | + void *buffer; | |
| 3050 | + target_phys_addr_t addr; | |
| 3051 | + target_phys_addr_t len; | |
| 3052 | +} BounceBuffer; | |
| 3053 | + | |
| 3054 | +static BounceBuffer bounce; | |
| 3055 | + | |
| 3056 | +/* Map a physical memory region into a host virtual address. | |
| 3057 | + * May map a subset of the requested range, given by and returned in *plen. | |
| 3058 | + * May return NULL if resources needed to perform the mapping are exhausted. | |
| 3059 | + * Use only for reads OR writes - not for read-modify-write operations. | |
| 3060 | + */ | |
| 3061 | +void *cpu_physical_memory_map(target_phys_addr_t addr, | |
| 3062 | + target_phys_addr_t *plen, | |
| 3063 | + int is_write) | |
| 3064 | +{ | |
| 3065 | + target_phys_addr_t len = *plen; | |
| 3066 | + target_phys_addr_t done = 0; | |
| 3067 | + int l; | |
| 3068 | + uint8_t *ret = NULL; | |
| 3069 | + uint8_t *ptr; | |
| 3070 | + target_phys_addr_t page; | |
| 3071 | + unsigned long pd; | |
| 3072 | + PhysPageDesc *p; | |
| 3073 | + unsigned long addr1; | |
| 3074 | + | |
| 3075 | + while (len > 0) { | |
| 3076 | + page = addr & TARGET_PAGE_MASK; | |
| 3077 | + l = (page + TARGET_PAGE_SIZE) - addr; | |
| 3078 | + if (l > len) | |
| 3079 | + l = len; | |
| 3080 | + p = phys_page_find(page >> TARGET_PAGE_BITS); | |
| 3081 | + if (!p) { | |
| 3082 | + pd = IO_MEM_UNASSIGNED; | |
| 3083 | + } else { | |
| 3084 | + pd = p->phys_offset; | |
| 3085 | + } | |
| 3086 | + | |
| 3087 | + if ((pd & ~TARGET_PAGE_MASK) != IO_MEM_RAM) { | |
| 3088 | + if (done || bounce.buffer) { | |
| 3089 | + break; | |
| 3090 | + } | |
| 3091 | + bounce.buffer = qemu_memalign(TARGET_PAGE_SIZE, TARGET_PAGE_SIZE); | |
| 3092 | + bounce.addr = addr; | |
| 3093 | + bounce.len = l; | |
| 3094 | + if (!is_write) { | |
| 3095 | + cpu_physical_memory_rw(addr, bounce.buffer, l, 0); | |
| 3096 | + } | |
| 3097 | + ptr = bounce.buffer; | |
| 3098 | + } else { | |
| 3099 | + addr1 = (pd & TARGET_PAGE_MASK) + (addr & ~TARGET_PAGE_MASK); | |
| 3100 | + ptr = phys_ram_base + addr1; | |
| 3101 | + } | |
| 3102 | + if (!done) { | |
| 3103 | + ret = ptr; | |
| 3104 | + } else if (ret + done != ptr) { | |
| 3105 | + break; | |
| 3106 | + } | |
| 3107 | + | |
| 3108 | + len -= l; | |
| 3109 | + addr += l; | |
| 3110 | + done += l; | |
| 3111 | + } | |
| 3112 | + *plen = done; | |
| 3113 | + return ret; | |
| 3114 | +} | |
| 3115 | + | |
| 3116 | +/* Unmaps a memory region previously mapped by cpu_physical_memory_map(). | |
| 3117 | + * Will also mark the memory as dirty if is_write == 1. access_len gives | |
| 3118 | + * the amount of memory that was actually read or written by the caller. | |
| 3119 | + */ | |
| 3120 | +void cpu_physical_memory_unmap(void *buffer, target_phys_addr_t len, | |
| 3121 | + int is_write, target_phys_addr_t access_len) | |
| 3122 | +{ | |
| 3123 | + if (buffer != bounce.buffer) { | |
| 3124 | + if (is_write) { | |
| 3125 | + unsigned long addr1 = (uint8_t *)buffer - phys_ram_base; | |
| 3126 | + while (access_len) { | |
| 3127 | + unsigned l; | |
| 3128 | + l = TARGET_PAGE_SIZE; | |
| 3129 | + if (l > access_len) | |
| 3130 | + l = access_len; | |
| 3131 | + if (!cpu_physical_memory_is_dirty(addr1)) { | |
| 3132 | + /* invalidate code */ | |
| 3133 | + tb_invalidate_phys_page_range(addr1, addr1 + l, 0); | |
| 3134 | + /* set dirty bit */ | |
| 3135 | + phys_ram_dirty[addr1 >> TARGET_PAGE_BITS] |= | |
| 3136 | + (0xff & ~CODE_DIRTY_FLAG); | |
| 3137 | + } | |
| 3138 | + addr1 += l; | |
| 3139 | + access_len -= l; | |
| 3140 | + } | |
| 3141 | + } | |
| 3142 | + return; | |
| 3143 | + } | |
| 3144 | + if (is_write) { | |
| 3145 | + cpu_physical_memory_write(bounce.addr, bounce.buffer, access_len); | |
| 3146 | + } | |
| 3147 | + qemu_free(bounce.buffer); | |
| 3148 | + bounce.buffer = NULL; | |
| 3149 | +} | |
| 3048 | 3150 | |
| 3049 | 3151 | /* warning: addr must be aligned */ |
| 3050 | 3152 | uint32_t ldl_phys(target_phys_addr_t addr) | ... | ... |