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