1 /////////////////////////////////////////////////////////////////////////
2 // $Id: memory.cc 14091 2021-01-30 17:37:42Z sshwarts $
3 /////////////////////////////////////////////////////////////////////////
4 //
5 // Copyright (C) 2001-2020 The Bochs Project
6 //
7 // This library is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU Lesser General Public
9 // License as published by the Free Software Foundation; either
10 // version 2 of the License, or (at your option) any later version.
11 //
12 // This library is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 // Lesser General Public License for more details.
16 //
17 // You should have received a copy of the GNU Lesser General Public
18 // License along with this library; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 //
21 /////////////////////////////////////////////////////////////////////////
22
23 #include "bochs.h"
24 #include "cpu/cpu.h"
25 #include "iodev/iodev.h"
26 #define LOG_THIS BX_MEM_THIS
27
28 //
29 // Memory map inside the 1st megabyte:
30 //
31 // 0x00000 - 0x7ffff DOS area (512K)
32 // 0x80000 - 0x9ffff Optional fixed memory hole (128K)
33 // 0xa0000 - 0xbffff Standard PCI/ISA Video Mem / SMMRAM (128K)
34 // 0xc0000 - 0xdffff Expansion Card BIOS and Buffer Area (128K)
35 // 0xe0000 - 0xeffff Lower BIOS Area (64K)
36 // 0xf0000 - 0xfffff Upper BIOS Area (64K)
37 //
38
writePhysicalPage(BX_CPU_C * cpu,bx_phy_address addr,unsigned len,void * data)39 void BX_MEM_C::writePhysicalPage(BX_CPU_C *cpu, bx_phy_address addr, unsigned len, void *data)
40 {
41 Bit8u *data_ptr;
42 bx_phy_address a20addr = A20ADDR(addr);
43 struct memory_handler_struct *memory_handler = NULL;
44
45 // Note: accesses should always be contained within a single page
46 if ((addr>>12) != ((addr+len-1)>>12)) {
47 BX_PANIC(("writePhysicalPage: cross page access at address 0x" FMT_PHY_ADDRX ", len=%d", addr, len));
48 }
49
50 #if BX_SUPPORT_MONITOR_MWAIT
51 BX_MEM_THIS check_monitor(a20addr, len);
52 #endif
53
54 bool is_bios = (a20addr >= (bx_phy_address)BX_MEM_THIS bios_rom_addr);
55 #if BX_PHY_ADDRESS_LONG
56 if (a20addr > BX_CONST64(0xffffffff)) is_bios = 0;
57 #endif
58
59 if (cpu != NULL) {
60 #if BX_SUPPORT_IODEBUG
61 bx_devices.pluginIODebug->mem_write(cpu, a20addr, len, data);
62 #endif
63
64 if ((a20addr >= 0x000a0000 && a20addr < 0x000c0000) && BX_MEM_THIS smram_available)
65 {
66 // SMRAM memory space
67 if (BX_MEM_THIS smram_enable || (cpu->smm_mode() && !BX_MEM_THIS smram_restricted))
68 goto mem_write;
69 }
70 }
71
72 memory_handler = BX_MEM_THIS memory_handlers[a20addr >> 20];
73 while (memory_handler) {
74 if (memory_handler->write_handler != NULL) {
75 if (memory_handler->begin <= a20addr &&
76 memory_handler->end >= a20addr &&
77 memory_handler->write_handler(a20addr, len, data, memory_handler->param))
78 {
79 return;
80 }
81 }
82 memory_handler = memory_handler->next;
83 }
84
85 mem_write:
86
87 // all memory access fits in single 4K page
88 if ((a20addr < BX_MEM_THIS len) && !is_bios) {
89 // all of data is within limits of physical memory
90 if (a20addr < 0x000a0000 || a20addr >= 0x00100000)
91 {
92 if (len == 8) {
93 pageWriteStampTable.decWriteStamp(a20addr, 8);
94 WriteHostQWordToLittleEndian((Bit64u*) BX_MEM_THIS get_vector(a20addr), *(Bit64u*)data);
95 return;
96 }
97 if (len == 4) {
98 pageWriteStampTable.decWriteStamp(a20addr, 4);
99 WriteHostDWordToLittleEndian((Bit32u*) BX_MEM_THIS get_vector(a20addr), *(Bit32u*)data);
100 return;
101 }
102 if (len == 2) {
103 pageWriteStampTable.decWriteStamp(a20addr, 2);
104 WriteHostWordToLittleEndian((Bit16u*) BX_MEM_THIS get_vector(a20addr), *(Bit16u*)data);
105 return;
106 }
107 if (len == 1) {
108 pageWriteStampTable.decWriteStamp(a20addr, 1);
109 * (BX_MEM_THIS get_vector(a20addr)) = * (Bit8u *) data;
110 return;
111 }
112 // len == other, just fall thru to special cases handling
113 }
114
115 #ifdef BX_LITTLE_ENDIAN
116 data_ptr = (Bit8u *) data;
117 #else // BX_BIG_ENDIAN
118 data_ptr = (Bit8u *) data + (len - 1);
119 #endif
120
121 if (a20addr < 0x000a0000 || a20addr >= 0x00100000)
122 {
123 // addr *not* in range 000A0000 .. 000FFFFF
124 while(1) {
125 // Write in chunks of 8 bytes if we can
126 if ((len & 7) == 0) {
127 pageWriteStampTable.decWriteStamp(a20addr, 8);
128 WriteHostQWordToLittleEndian((Bit64u*) BX_MEM_THIS get_vector(a20addr), *(Bit64u*)data_ptr);
129 len -= 8;
130 a20addr += 8;
131 #ifdef BX_LITTLE_ENDIAN
132 data_ptr += 8;
133 #else
134 data_ptr -= 8;
135 #endif
136
137 if (len == 0) return;
138 } else {
139 pageWriteStampTable.decWriteStamp(a20addr, 1);
140 *(BX_MEM_THIS get_vector(a20addr)) = *data_ptr;
141 if (len == 1) return;
142 len--;
143 a20addr++;
144 #ifdef BX_LITTLE_ENDIAN
145 data_ptr++;
146 #else // BX_BIG_ENDIAN
147 data_ptr--;
148 #endif
149 }
150 }
151 }
152
153 pageWriteStampTable.decWriteStamp(a20addr);
154
155 // addr must be in range 000A0000 .. 000FFFFF
156
157 for(unsigned i=0; i<len; i++) {
158
159 // SMMRAM
160 if (a20addr < 0x000c0000) {
161 // devices are not allowed to access SMMRAM under VGA memory
162 if (cpu) {
163 *(BX_MEM_THIS get_vector(a20addr)) = *data_ptr;
164 }
165 goto inc_one;
166 }
167
168 // adapter ROM C0000 .. DFFFF
169 // ROM BIOS memory E0000 .. FFFFF
170 #if BX_SUPPORT_PCI == 0
171 // ignore write to ROM
172 #else
173 // Write Based on 440fx Programming
174 if (BX_MEM_THIS pci_enabled && ((a20addr & 0xfffc0000) == 0x000c0000)) {
175 unsigned area = (unsigned)(a20addr >> 14) & 0x0f;
176 if (area > BX_MEM_AREA_F0000) area = BX_MEM_AREA_F0000;
177 if (BX_MEM_THIS memory_type[area][1] == 1) {
178 // Writes to ShadowRAM
179 BX_DEBUG(("Writing to ShadowRAM: address 0x" FMT_PHY_ADDRX ", data %02x", a20addr, *data_ptr));
180 *(BX_MEM_THIS get_vector(a20addr)) = *data_ptr;
181 } else if ((area >= BX_MEM_AREA_E0000) && BX_MEM_THIS bios_write_enabled) {
182 // volatile BIOS write support
183 #ifdef BX_LITTLE_ENDIAN
184 data_ptr = (Bit8u *) data;
185 #else // BX_BIG_ENDIAN
186 data_ptr = (Bit8u *) data + (len - 1);
187 #endif
188 for (unsigned i = 0; i < len; i++) {
189 if (BX_MEM_THIS flash_type > 0) {
190 BX_MEM_THIS flash_write(BIOS_MAP_LAST128K(a20addr), *data_ptr);
191 } else {
192 BX_MEM_THIS rom[BIOS_MAP_LAST128K(a20addr)] = *data_ptr;
193 }
194 a20addr++;
195 #ifdef BX_LITTLE_ENDIAN
196 data_ptr++;
197 #else // BX_BIG_ENDIAN
198 data_ptr--;
199 #endif
200 }
201 } else {
202 // Writes to ROM, Inhibit
203 BX_DEBUG(("Write to ROM ignored: address 0x" FMT_PHY_ADDRX ", data %02x", a20addr, *data_ptr));
204 }
205 }
206 #endif
207
208 inc_one:
209 a20addr++;
210 #ifdef BX_LITTLE_ENDIAN
211 data_ptr++;
212 #else // BX_BIG_ENDIAN
213 data_ptr--;
214 #endif
215
216 }
217 } else if (BX_MEM_THIS bios_write_enabled && is_bios) {
218 // volatile BIOS write support
219 #ifdef BX_LITTLE_ENDIAN
220 data_ptr = (Bit8u *) data;
221 #else // BX_BIG_ENDIAN
222 data_ptr = (Bit8u *) data + (len - 1);
223 #endif
224 for (unsigned i = 0; i < len; i++) {
225 if (BX_MEM_THIS flash_type > 0) {
226 BX_MEM_THIS flash_write(a20addr & BIOS_MASK, *data_ptr);
227 } else {
228 BX_MEM_THIS rom[a20addr & BIOS_MASK] = *data_ptr;
229 }
230 a20addr++;
231 #ifdef BX_LITTLE_ENDIAN
232 data_ptr++;
233 #else // BX_BIG_ENDIAN
234 data_ptr--;
235 #endif
236 }
237 } else {
238 // access outside limits of physical memory, ignore
239 BX_DEBUG(("Write outside the limits of physical memory (0x" FMT_PHY_ADDRX ") (ignore)", a20addr));
240 }
241 }
242
readPhysicalPage(BX_CPU_C * cpu,bx_phy_address addr,unsigned len,void * data)243 void BX_MEM_C::readPhysicalPage(BX_CPU_C *cpu, bx_phy_address addr, unsigned len, void *data)
244 {
245 Bit8u *data_ptr;
246 bx_phy_address a20addr = A20ADDR(addr);
247 struct memory_handler_struct *memory_handler = NULL;
248
249 // Note: accesses should always be contained within a single page
250 if ((addr>>12) != ((addr+len-1)>>12)) {
251 BX_PANIC(("readPhysicalPage: cross page access at address 0x" FMT_PHY_ADDRX ", len=%d", addr, len));
252 }
253
254 bool is_bios = (a20addr >= (bx_phy_address)BX_MEM_THIS bios_rom_addr);
255 #if BX_PHY_ADDRESS_LONG
256 if (a20addr > BX_CONST64(0xffffffff)) is_bios = 0;
257 #endif
258
259 if (cpu != NULL) {
260 #if BX_SUPPORT_IODEBUG
261 bx_devices.pluginIODebug->mem_read(cpu, a20addr, len, data);
262 #endif
263
264 if ((a20addr >= 0x000a0000 && a20addr < 0x000c0000) && BX_MEM_THIS smram_available)
265 {
266 // SMRAM memory space
267 if (BX_MEM_THIS smram_enable || (cpu->smm_mode() && !BX_MEM_THIS smram_restricted))
268 goto mem_read;
269 }
270 }
271
272 memory_handler = BX_MEM_THIS memory_handlers[a20addr >> 20];
273 while (memory_handler) {
274 if (memory_handler->begin <= a20addr &&
275 memory_handler->end >= a20addr &&
276 memory_handler->read_handler(a20addr, len, data, memory_handler->param))
277 {
278 return;
279 }
280 memory_handler = memory_handler->next;
281 }
282
283 mem_read:
284
285 if ((a20addr < BX_MEM_THIS len) && !is_bios) {
286 // all of data is within limits of physical memory
287 if (a20addr < 0x000a0000 || a20addr >= 0x00100000)
288 {
289 if (len == 8) {
290 * (Bit64u*) data = ReadHostQWordFromLittleEndian((Bit64u*) BX_MEM_THIS get_vector(a20addr));
291 return;
292 }
293 if (len == 4) {
294 * (Bit32u*) data = ReadHostDWordFromLittleEndian((Bit32u*) BX_MEM_THIS get_vector(a20addr));
295 return;
296 }
297 if (len == 2) {
298 * (Bit16u*) data = ReadHostWordFromLittleEndian((Bit16u*) BX_MEM_THIS get_vector(a20addr));
299 return;
300 }
301 if (len == 1) {
302 * (Bit8u *) data = * (BX_MEM_THIS get_vector(a20addr));
303 return;
304 }
305 // len == other case can just fall thru to special cases handling
306 }
307
308 #ifdef BX_LITTLE_ENDIAN
309 data_ptr = (Bit8u *) data;
310 #else // BX_BIG_ENDIAN
311 data_ptr = (Bit8u *) data + (len - 1);
312 #endif
313
314 if (a20addr < 0x000a0000 || a20addr >= 0x00100000)
315 {
316 // addr *not* in range 000A0000 .. 000FFFFF
317 while(1) {
318 // Read in chunks of 8 bytes if we can
319 if ((len & 7) == 0) {
320 *((Bit64u*)data_ptr) = ReadHostQWordFromLittleEndian((Bit64u*) BX_MEM_THIS get_vector(a20addr));
321 len -= 8;
322 a20addr += 8;
323 #ifdef BX_LITTLE_ENDIAN
324 data_ptr += 8;
325 #else
326 data_ptr -= 8;
327 #endif
328
329 if (len == 0) return;
330 } else {
331 *data_ptr = *(BX_MEM_THIS get_vector(a20addr));
332 if (len == 1) return;
333 len--;
334 a20addr++;
335 #ifdef BX_LITTLE_ENDIAN
336 data_ptr++;
337 #else // BX_BIG_ENDIAN
338 data_ptr--;
339 #endif
340 }
341 }
342 }
343
344 // addr must be in range 000A0000 .. 000FFFFF
345
346 for (unsigned i=0; i<len; i++) {
347
348 // SMMRAM
349 if (a20addr < 0x000c0000) {
350 // devices are not allowed to access SMMRAM under VGA memory
351 if (cpu) *data_ptr = *(BX_MEM_THIS get_vector(a20addr));
352 goto inc_one;
353 }
354
355 #if BX_SUPPORT_PCI
356 if (BX_MEM_THIS pci_enabled && ((a20addr & 0xfffc0000) == 0x000c0000)) {
357 unsigned area = (unsigned)(a20addr >> 14) & 0x0f;
358 if (area > BX_MEM_AREA_F0000) area = BX_MEM_AREA_F0000;
359 if (BX_MEM_THIS memory_type[area][0] == 0) {
360 // Read from ROM
361 if ((a20addr & 0xfffe0000) == 0x000e0000) {
362 // last 128K of BIOS ROM mapped to 0xE0000-0xFFFFF
363 if (BX_MEM_THIS flash_type > 0) {
364 *data_ptr = BX_MEM_THIS flash_read(BIOS_MAP_LAST128K(a20addr));
365 } else {
366 *data_ptr = BX_MEM_THIS rom[BIOS_MAP_LAST128K(a20addr)];
367 }
368 } else {
369 *data_ptr = BX_MEM_THIS rom[(a20addr & EXROM_MASK) + BIOSROMSZ];
370 }
371 } else {
372 // Read from ShadowRAM
373 *data_ptr = *(BX_MEM_THIS get_vector(a20addr));
374 }
375 }
376 else
377 #endif // #if BX_SUPPORT_PCI
378 {
379 if ((a20addr & 0xfffc0000) != 0x000c0000) {
380 *data_ptr = *(BX_MEM_THIS get_vector(a20addr));
381 }
382 else if ((a20addr & 0xfffe0000) == 0x000e0000) {
383 // last 128K of BIOS ROM mapped to 0xE0000-0xFFFFF
384 *data_ptr = BX_MEM_THIS rom[BIOS_MAP_LAST128K(a20addr)];
385 }
386 else {
387 *data_ptr = BX_MEM_THIS rom[(a20addr & EXROM_MASK) + BIOSROMSZ];
388 }
389 }
390
391 inc_one:
392 a20addr++;
393 #ifdef BX_LITTLE_ENDIAN
394 data_ptr++;
395 #else // BX_BIG_ENDIAN
396 data_ptr--;
397 #endif
398
399 }
400 }
401 else // access outside limits of physical memory
402 {
403 #if BX_PHY_ADDRESS_LONG
404 if (a20addr > BX_CONST64(0xffffffff)) {
405 memset(data, 0xFF, len);
406 return;
407 }
408 #endif
409
410 #ifdef BX_LITTLE_ENDIAN
411 data_ptr = (Bit8u *) data;
412 #else // BX_BIG_ENDIAN
413 data_ptr = (Bit8u *) data + (len - 1);
414 #endif
415
416 if (is_bios) {
417 for (unsigned i = 0; i < len; i++) {
418 if (BX_MEM_THIS flash_type > 0) {
419 *data_ptr = BX_MEM_THIS flash_read(a20addr & BIOS_MASK);
420 } else {
421 *data_ptr = BX_MEM_THIS rom[a20addr & BIOS_MASK];
422 }
423 a20addr++;
424 #ifdef BX_LITTLE_ENDIAN
425 data_ptr++;
426 #else // BX_BIG_ENDIAN
427 data_ptr--;
428 #endif
429 }
430 } else {
431 // bogus memory
432 memset(data, 0xFF, len);
433 }
434 }
435 }
436
dmaReadPhysicalPage(bx_phy_address addr,unsigned len,Bit8u * data)437 void BX_MEM_C::dmaReadPhysicalPage(bx_phy_address addr, unsigned len, Bit8u *data)
438 {
439 // Note: accesses should always be contained within a single page
440 if ((addr>>12) != ((addr+len-1)>>12)) {
441 BX_PANIC(("dmaReadPhysicalPage: cross page access at address 0x" FMT_PHY_ADDRX ", len=%d", addr, len));
442 }
443
444 Bit8u *memptr = getHostMemAddr(NULL, addr, BX_READ);
445 if (memptr != NULL) {
446 memcpy(data, memptr, len);
447 }
448 else {
449 for (unsigned i=0;i < len; i++) {
450 readPhysicalPage(NULL, addr+i, 1, &data[i]);
451 }
452 }
453 }
454
dmaWritePhysicalPage(bx_phy_address addr,unsigned len,Bit8u * data)455 void BX_MEM_C::dmaWritePhysicalPage(bx_phy_address addr, unsigned len, Bit8u *data)
456 {
457 // Note: accesses should always be contained within a single page
458 if ((addr>>12) != ((addr+len-1)>>12)) {
459 BX_PANIC(("dmaWritePhysicalPage: cross page access at address 0x" FMT_PHY_ADDRX ", len=%d", addr, len));
460 }
461
462 Bit8u *memptr = getHostMemAddr(NULL, addr, BX_WRITE);
463 if (memptr != NULL) {
464 pageWriteStampTable.decWriteStamp(addr);
465 memcpy(memptr, data, len);
466 }
467 else {
468 for (unsigned i=0;i < len; i++) {
469 writePhysicalPage(NULL, addr+i, 1, &data[i]);
470 }
471 }
472 }
473