Map 0xe0000-0xfffff instead of 0xf0000-0xfffff.
[v86d.git] / v86_mem.c
blob644c76965cf57d2d25d9e011aa5ea14b44e1cd5b
1 #include <errno.h>
2 #include <fcntl.h>
3 #include <string.h>
4 #include <sys/mman.h>
5 #include <unistd.h>
6 #include "v86.h"
8 #define REAL_MEM_BLOCKS 0x100
10 u8 *mem_low; /* 0x000000 - 0x001000 */
11 u8 *mem_real; /* 0x010000 - 0x09ffff */
12 u8 *mem_vbios; /* 0x0c0000 - 0x0cxxxx */
13 u8 *mem_sbios; /* 0x0f0000 - 0x0fffff */
14 u8 *mem_vram; /* 0x0a0000 - 0xbfffff */
15 u8 *mem_ebda; /* usually: 0x9fc00 - 0x9ffff */
17 static u32 ebda_start;
18 static u32 ebda_size;
19 static u32 ebda_diff;
20 static u32 vbios_size;
22 struct mem_block {
23 unsigned int size : 20;
24 unsigned int free : 1;
27 static struct {
28 int ready;
29 int count;
30 struct mem_block blocks[REAL_MEM_BLOCKS];
31 } mem_info = { 0 };
33 void *vptr(u32 addr) {
35 /* Order the if's in the expected probability of access to the
36 * given region of memory. */
37 if (addr >= REAL_MEM_BASE && addr < REAL_MEM_BASE + REAL_MEM_SIZE)
38 return (mem_real + addr - REAL_MEM_BASE);
39 else if (addr >= VBIOS_BASE && addr < VBIOS_BASE + vbios_size)
40 return (mem_vbios + addr - VBIOS_BASE);
41 else if (addr >= SBIOS_BASE && addr < SBIOS_BASE + SBIOS_SIZE)
42 return (mem_sbios + addr - SBIOS_BASE);
43 else if (addr >= VRAM_BASE && addr < VRAM_BASE + VRAM_SIZE)
44 return (mem_vram + addr - VRAM_BASE);
45 else if (addr < IVTBDA_SIZE)
46 return (mem_low + addr);
47 else if (addr >= ebda_start && addr < ebda_start + ebda_size)
48 return (mem_ebda + addr - ebda_start + ebda_diff);
49 else {
50 ulog(LOG_WARNING, "Trying to access an unsupported memory region at %x", addr);
51 return NULL;
55 /* We don't care about memory accesses at boundaries of different memory
56 * regions, since our v86 memory is non contiguous anyway. */
57 u8 v_rdb(u32 addr) {
58 return *(u8*) vptr(addr);
61 u16 v_rdw(u32 addr) {
62 return *(u16*) vptr(addr);
65 u32 v_rdl(u32 addr) {
66 return *(u32*) vptr(addr);
69 void v_wrb(u32 addr, u8 val) {
70 u8 *t = vptr(addr);
71 *t = val;
74 void v_wrw(u32 addr, u16 val) {
75 u16 *t = vptr(addr);
76 *t = val;
79 void v_wrl(u32 addr, u32 val) {
80 u32 *t = vptr(addr);
81 *t = val;
84 static void *map_file(void *start, size_t length, int prot, int flags, char *name, long offset)
86 void *m;
87 int fd;
89 fd = open(name, (flags & MAP_SHARED) ? O_RDWR : O_RDONLY);
91 if (fd == -1) {
92 ulog(LOG_ERR, "Open '%s' failed with: %s\n", name, strerror(errno));
93 return NULL;
96 m = mmap(start, length, prot, flags, fd, offset);
98 if (m == (void *)-1) {
99 ulog(LOG_ERR, "mmap '%s' failed with: %s\n", name, strerror(errno));
100 close(fd);
101 return NULL;
104 close(fd);
105 return m;
108 static int real_mem_init(void)
110 if (mem_info.ready)
111 return 0;
113 mem_real = map_file(NULL, REAL_MEM_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
114 MAP_PRIVATE, "/dev/zero", 0);
115 if (!mem_real)
116 return 1;
118 mem_info.ready = 1;
119 mem_info.count = 1;
120 mem_info.blocks[0].size = REAL_MEM_SIZE;
121 mem_info.blocks[0].free = 1;
123 return 0;
126 static void real_mem_deinit(void)
128 if (mem_info.ready) {
129 munmap(mem_real, REAL_MEM_SIZE);
130 mem_info.ready = 0;
134 static void insert_block(int i)
136 memmove(mem_info.blocks + i + 1, mem_info.blocks + i,
137 (mem_info.count - i) * sizeof(struct mem_block));
138 mem_info.count++;
141 static void delete_block(int i)
143 mem_info.count--;
144 memmove(mem_info.blocks + i, mem_info.blocks + i + 1,
145 (mem_info.count - i) * sizeof(struct mem_block));
148 u32 v86_mem_alloc(int size)
150 int i;
151 u32 r = REAL_MEM_BASE;
153 if (!mem_info.ready)
154 return 0;
156 if (mem_info.count == REAL_MEM_BLOCKS)
157 return 0;
159 size = (size + 15) & ~15;
161 for (i = 0; i < mem_info.count; i++) {
162 if (mem_info.blocks[i].free && size < mem_info.blocks[i].size) {
163 insert_block(i);
165 mem_info.blocks[i].size = size;
166 mem_info.blocks[i].free = 0;
167 mem_info.blocks[i + 1].size -= size;
169 return r;
172 r += mem_info.blocks[i].size;
175 return 0;
178 void v86_mem_free(u32 m)
180 int i;
181 u32 r = REAL_MEM_BASE;
183 if (!mem_info.ready)
184 return;
186 i = 0;
187 while (m != r) {
188 r += mem_info.blocks[i].size;
189 i++;
190 if (i == mem_info.count)
191 return;
194 mem_info.blocks[i].free = 1;
196 if (i + 1 < mem_info.count && mem_info.blocks[i + 1].free) {
197 mem_info.blocks[i].size += mem_info.blocks[i + 1].size;
198 delete_block(i + 1);
201 if (i - 1 >= 0 && mem_info.blocks[i - 1].free) {
202 mem_info.blocks[i - 1].size += mem_info.blocks[i].size;
203 delete_block(i);
207 static int get_bytes_from_phys(u32 addr, int num_bytes, void *dest)
209 u8 *mem_tmp;
210 int size = num_bytes;
211 u32 diff = 0;
212 u32 t;
214 t = addr & -getpagesize();
215 if (t) {
216 diff = addr - t;
217 addr = t;
218 size += diff;
221 mem_tmp = map_file(NULL, size, PROT_READ | PROT_WRITE,
222 MAP_SHARED, "/dev/mem", addr);
223 if (!mem_tmp)
224 return -1;
226 memcpy(dest, mem_tmp + diff, num_bytes);
227 munmap(mem_tmp, size);
228 return 0;
231 int v86_mem_init(void)
233 u8 tmp[4];
235 if (real_mem_init())
236 return 1;
239 * We have to map the IVTBDA as shared. Without it, setting video
240 * modes will not work correctly on some cards (e.g. nVidia GeForce
241 * 8600M, PCI ID 10de:0425).
243 mem_low = map_file(NULL, IVTBDA_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
244 MAP_SHARED, "/dev/mem", IVTBDA_BASE);
245 if (!mem_low) {
246 real_mem_deinit();
247 return 1;
250 /* Try to find the start of the EBDA */
251 ebda_start = (*(u16*)(mem_low + 0x40e)) << 4;
252 if (!ebda_start || ebda_start > EBDA_BASE)
253 ebda_start = EBDA_BASE;
255 if (get_bytes_from_phys(ebda_start, 1, tmp)) {
256 ulog(LOG_WARNING, "Failed to read EBDA size from %x. Ignoring EBDA.", ebda_start);
257 } else {
258 /* The first byte in the EBDA is its size in kB */
259 ebda_size = ((u32) tmp[0]) << 10;
260 if (ebda_start + ebda_size > VRAM_BASE) {
261 ulog(LOG_WARNING, "EBDA too big (%x), truncating.", ebda_size);
262 ebda_size = VRAM_BASE - ebda_start;
265 /* Map the EBDA */
266 ulog(LOG_DEBUG, "EBDA at %5x-%5x\n", ebda_start, ebda_start + ebda_size - 1);
267 u32 t = ebda_start & -getpagesize();
269 if (t) {
270 ebda_diff = ebda_start - t;
273 mem_ebda = map_file(NULL, ebda_size + ebda_diff, PROT_READ | PROT_WRITE, MAP_SHARED, "/dev/mem", ebda_start - ebda_diff);
274 if (!mem_ebda) {
275 ulog(LOG_WARNING, "Failed to mmap EBDA. Proceeding without it.");
279 /* Map the Video RAM */
280 mem_vram = map_file(NULL, VRAM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, "/dev/mem", VRAM_BASE);
281 if (!mem_vram) {
282 ulog(LOG_ERR, "Failed to mmap the Video RAM.");
283 v86_mem_cleanup();
284 return 1;
287 /* Map the Video BIOS */
288 get_bytes_from_phys(VBIOS_BASE, 4, tmp);
289 if (tmp[0] != 0x55 || tmp[1] != 0xAA) {
290 ulog(LOG_ERR, "Video BIOS not found at %x.", VBIOS_BASE);
291 v86_mem_cleanup();
292 return 1;
294 vbios_size = tmp[2] * 0x200;
295 ulog(LOG_DEBUG, "VBIOS at %5x-%5x\n", VBIOS_BASE, VBIOS_BASE + vbios_size - 1);
296 mem_vbios = map_file(NULL, vbios_size, PROT_READ | PROT_WRITE | PROT_EXEC,
297 MAP_SHARED, "/dev/mem", VBIOS_BASE);
299 if (!mem_vbios) {
300 ulog(LOG_ERR, "Failed to mmap the Video BIOS.");
301 v86_mem_cleanup();
302 return 1;
305 /* Map the system BIOS */
306 mem_sbios = map_file(NULL, SBIOS_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,
307 MAP_SHARED, "/dev/mem", SBIOS_BASE);
308 if (!mem_sbios) {
309 ulog(LOG_ERR, "Failed to mmap the System BIOS as %5x.", SBIOS_BASE);
310 v86_mem_cleanup();
311 return 1;
314 return 0;
317 void v86_mem_cleanup(void)
319 if (mem_low)
320 munmap(mem_low, IVTBDA_SIZE);
322 if (mem_ebda)
323 munmap(mem_ebda, ebda_size + ebda_diff);
325 if (mem_vram)
326 munmap(mem_vram, VRAM_SIZE);
328 if (mem_vbios)
329 munmap(mem_vbios, vbios_size);
331 if (mem_sbios)
332 munmap(mem_sbios, SBIOS_SIZE);
334 real_mem_deinit();