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
;
20 static u32 vbios_size
;
23 unsigned int size
: 20;
24 unsigned int free
: 1;
30 struct mem_block blocks
[REAL_MEM_BLOCKS
];
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 (mem_ebda
&& addr
>= ebda_start
&& addr
< ebda_start
+ ebda_size
)
48 return (mem_ebda
+ addr
- ebda_start
+ ebda_diff
);
50 ulog(LOG_WARNING
, "Trying to access an unsupported memory region at %x", addr
);
55 /* We don't care about memory accesses at boundaries of different memory
56 * regions, since our v86 memory is non contiguous anyway. */
58 return *(u8
*) vptr(addr
);
62 return *(u16
*) vptr(addr
);
66 return *(u32
*) vptr(addr
);
69 void v_wrb(u32 addr
, u8 val
) {
74 void v_wrw(u32 addr
, u16 val
) {
79 void v_wrl(u32 addr
, u32 val
) {
84 static void *map_file(void *start
, size_t length
, int prot
, int flags
, char *name
, long offset
)
89 fd
= open(name
, (flags
& MAP_SHARED
) ? O_RDWR
: O_RDONLY
);
92 ulog(LOG_ERR
, "Open '%s' failed with: %s\n", name
, strerror(errno
));
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
));
108 static int real_mem_init(void)
113 mem_real
= map_file(NULL
, REAL_MEM_SIZE
, PROT_READ
| PROT_WRITE
,
114 MAP_PRIVATE
, "/dev/zero", 0);
120 mem_info
.blocks
[0].size
= REAL_MEM_SIZE
;
121 mem_info
.blocks
[0].free
= 1;
126 static void real_mem_deinit(void)
128 if (mem_info
.ready
) {
129 munmap(mem_real
, REAL_MEM_SIZE
);
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
));
141 static void delete_block(int i
)
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
)
151 u32 r
= REAL_MEM_BASE
;
156 if (mem_info
.count
== REAL_MEM_BLOCKS
)
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
) {
165 mem_info
.blocks
[i
].size
= size
;
166 mem_info
.blocks
[i
].free
= 0;
167 mem_info
.blocks
[i
+ 1].size
-= size
;
172 r
+= mem_info
.blocks
[i
].size
;
178 void v86_mem_free(u32 m
)
181 u32 r
= REAL_MEM_BASE
;
188 r
+= mem_info
.blocks
[i
].size
;
190 if (i
== mem_info
.count
)
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
;
201 if (i
- 1 >= 0 && mem_info
.blocks
[i
- 1].free
) {
202 mem_info
.blocks
[i
- 1].size
+= mem_info
.blocks
[i
].size
;
207 static int get_bytes_from_phys(u32 addr
, int num_bytes
, void *dest
)
210 int size
= num_bytes
;
214 t
= addr
& -getpagesize();
221 mem_tmp
= map_file(NULL
, size
, PROT_READ
| PROT_WRITE
,
222 MAP_SHARED
, "/dev/mem", addr
);
226 memcpy(dest
, mem_tmp
+ diff
, num_bytes
);
227 munmap(mem_tmp
, size
);
231 int v86_mem_init(void)
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
,
244 MAP_SHARED
, "/dev/mem", IVTBDA_BASE
);
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
);
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
;
266 ulog(LOG_DEBUG
, "EBDA at %5x-%5x\n", ebda_start
, ebda_start
+ ebda_size
- 1);
267 u32 t
= ebda_start
& -getpagesize();
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
);
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
);
282 ulog(LOG_ERR
, "Failed to mmap the Video RAM.");
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
);
294 vbios_size
= tmp
[2] * 0x200;
295 ulog(LOG_DEBUG
, "VBIOS at %5x-%5x\n", VBIOS_BASE
, VBIOS_BASE
+ vbios_size
- 1);
298 * The Video BIOS and the System BIOS have to be mapped with PROT_WRITE.
299 * There is at least one case where mapping them without this flag causes
300 * a segfault during the emulation: https://bugs.gentoo.org/show_bug.cgi?id=245254
302 mem_vbios
= map_file(NULL
, vbios_size
, PROT_READ
| PROT_WRITE
,
303 MAP_SHARED
, "/dev/mem", VBIOS_BASE
);
306 ulog(LOG_ERR
, "Failed to mmap the Video BIOS.");
311 /* Map the system BIOS */
312 mem_sbios
= map_file(NULL
, SBIOS_SIZE
, PROT_READ
| PROT_WRITE
,
313 MAP_SHARED
, "/dev/mem", SBIOS_BASE
);
315 ulog(LOG_ERR
, "Failed to mmap the System BIOS as %5x.", SBIOS_BASE
);
323 void v86_mem_cleanup(void)
326 munmap(mem_low
, IVTBDA_SIZE
);
329 munmap(mem_ebda
, ebda_size
+ ebda_diff
);
332 munmap(mem_vram
, VRAM_SIZE
);
335 munmap(mem_vbios
, vbios_size
);
338 munmap(mem_sbios
, SBIOS_SIZE
);