3 * Code to detect the surrounding DOS/Windows environment and support routines to work with it
4 * (C) 2009-2012 Jonathan Campbell.
5 * Hackipedia DOS library.
7 * This code is licensed under the LGPL.
8 * <insert LGPL legal text here>
16 #include <conio.h> /* this is where Open Watcom hides the outp() etc. functions */
26 #include <hw/cpu/cpu.h>
27 #include <hw/dos/dos.h>
28 #include <hw/dos/doswin.h>
29 #include <hw/dos/dosntvdm.h>
31 /* TODO: Since VCPI/EMM386.EXE can affect 16-bit real mode, why not enable this API for 16-bit real mode too? */
32 /* TODO: Also why not enable this function for 16-bit protected mode under Windows 3.1? */
33 #if TARGET_MSDOS == 32
34 struct dos_linear_to_phys_info dos_ltp_info;
35 unsigned char dos_ltp_info_init=0;
37 /* WARNING: Caller must have called probe_dos() and detect_windows() */
39 if (!dos_ltp_info_init) {
40 memset(&dos_ltp_info,0,sizeof(dos_ltp_info));
42 /* part of our hackery needs to know what CPU we're running under */
43 if (cpu_basic_level < 0)
48 #if defined(TARGET_WINDOWS)
49 /* TODO: Careful analsys of what version and mode Windows we're running under */
50 /* start with the assumption that we don't know where we are and we can't translate to physical. */
51 dos_ltp_info.vcpi_xlate = 0; /* TODO: It is said Windows 3.0 has VCPI at the core. Can we detect that? Use it? */
52 dos_ltp_info.paging = (windows_mode <= WINDOWS_STANDARD ? 0 : 1); /* paging is not used in REAL or STANDARD modes */
53 dos_ltp_info.dos_remap = dos_ltp_info.paging;
54 dos_ltp_info.should_lock_pages = 1;
55 dos_ltp_info.cant_xlate = 1;
56 dos_ltp_info.using_pae = 0; /* TODO: Windows XP SP2 and later can and do use PAE. How to detect that? */
57 dos_ltp_info.dma_dos_xlate = 0;
59 # if TARGET_MSDOS == 32
61 /* TODO: Use GetWinFlags() and version info */
64 /* ================ MS-DOS specific =============== */
65 /* we need to know if VCPI is present */
68 /* NTS: Microsoft Windows 3.0/3.1 Enhanced mode and Windows 95/98/ME all trap access to the control registers.
69 * But then they emulate the instruction in such a way that we get weird nonsense values.
71 * Windows 95/98/ME: We get CR0 = 2. Why??
72 * Windows 3.0/3.1: We get CR0 = 0.
74 * So basically what Windows is telling us... is that we're 32-bit protected mode code NOT running in
75 * protected mode? What? */
76 if (windows_mode == WINDOWS_ENHANCED) {
77 /* it's pointless, the VM will trap and return nonsense for control register contents */
78 dos_ltp_info.cr0 = 0x80000001UL;
79 dos_ltp_info.cr3 = 0x00000000UL;
80 dos_ltp_info.cr4 = 0x00000000UL;
82 else if (windows_mode == WINDOWS_NT) {
83 /* Windows NTVDM will let us read CR0, but CR3 and CR4 come up blank. So what's the point then? */
93 dos_ltp_info.cr0 = r0 | 0x80000001UL; /* paging and protected mode are ALWAYS enabled, even if NTVDM should lie to us */
94 dos_ltp_info.cr3 = 0x00000000UL;
95 dos_ltp_info.cr4 = 0x00000000UL;
98 uint32_t r0=0,r3=0,r4=0;
112 dos_ltp_info.cr0 = r0;
113 dos_ltp_info.cr3 = r3;
114 dos_ltp_info.cr4 = r4;
117 dos_ltp_info.vcpi_xlate = vcpi_present?1:0; /* if no other methods available, try asking the VCPI server */
118 dos_ltp_info.paging = (dos_ltp_info.cr0 >> 31)?1:0; /* if bit 31 of CR0 is set, the extender has paging enabled */
119 dos_ltp_info.dos_remap = vcpi_present?1:0; /* most DOS extenders map 1:1 the lower 1MB, but VCPI can violate that */
120 dos_ltp_info.should_lock_pages = dos_ltp_info.paging; /* it's a good assumption if paging is enabled the extender probably pages to disk and may move things around */
121 dos_ltp_info.cant_xlate = dos_ltp_info.paging; /* assume we can't translate addresses yet if paging is enabled */
122 dos_ltp_info.using_pae = (dos_ltp_info.cr4 & 0x20)?1:0; /* take note if PAE is enabled */
123 dos_ltp_info.dma_dos_xlate = dos_ltp_info.paging; /* assume the extender translates DMA if paging is enabled */
125 if (windows_mode == WINDOWS_ENHANCED || windows_mode == WINDOWS_NT) {
126 dos_ltp_info.should_lock_pages = 1; /* Windows is known to page to disk (the swapfile) */
127 dos_ltp_info.dma_dos_xlate = 1; /* Windows virtualizes the DMA controller */
128 dos_ltp_info.cant_xlate = 1; /* Windows provides us no way to determine the physical memory address from linear */
129 dos_ltp_info.dos_remap = 1; /* Windows remaps the DOS memory area. This is how it makes multiple DOS VMs possible */
130 dos_ltp_info.vcpi_xlate = 0; /* Windows does not like VCPI */
131 dos_ltp_info.paging = 1; /* Windows uses paging. Always */
134 /* this code is ill prepared for PAE modes for the time being, since PAE makes page table entries 64-bit
135 * wide instead of 32-bit wide. Then again, 99% of DOS out there probably couldn't handle PAE well either. */
136 if (dos_ltp_info.using_pae)
137 dos_ltp_info.cant_xlate = 1;
139 /* if NOT running under Windows, and the CR3 register shows something, then we can translate by directly peeking at the page tables */
140 if (windows_mode == WINDOWS_NONE && dos_ltp_info.cr3 >= 0x1000)
141 dos_ltp_info.cant_xlate = 0;
144 dos_ltp_info_init = 1;
151 #if TARGET_MSDOS == 32
152 /* WARNINGS: Worst case scanario this function cannot translate anything at all.
153 * It will return 0xFFFFFFFFUL if it cannot determine the address.
154 * If paging is disabled, the linear address will be returned.
155 * If the environment requires you to lock pages, then you must do so
156 * before calling this function. Failure to do so will mean erratic
157 * behavior when the page you were working on moves out from under you! */
159 /* "There is no way in a DPMI environment to determine the physical address corresponding to a given linear address. This is part of the design of DPMI. You must design your application accordingly."
162 * I need the damn physical address and you're not gonna stop me! */
166 * QEMU + Windows 95 + EMM386.EXE:
168 * I don't know if the DOS extender is doing this, or EMM386.EXE is enforcing it, but
169 * a dump of the first 4MB in the test program reveals our linear address space is
170 * randomly constructed from 16KB pages taken from all over extended memory. Some of
171 * them, the pages are arranged BACKWARDS. Yeah, backwards.
173 * Anyone behind DOS4/GW and DOS32a care to explain that weirdness?
175 * Also revealed, DOS4/GW follows the DPMI spec and refuses to map pages from conventional
176 * memory. So if DOS memory is not mapped 1:1 and the page tables are held down there we
177 * literally cannot read them! */
178 /* NOTE: The return value is 64-bit so that in the future, if we ever run under DOS with PAE or
179 * PSE-36 trickery, we can still report the correct address even if above 4GB. The parameter
180 * supplied however remains 32-bit, because as a 32-bit DOS program there's no way any
181 * linear address will ever exceed 4GB. */
182 uint64_t dos_linear_to_phys(uint32_t linear) {
184 uint64_t ret = DOS_LTP_FAILED;
185 unsigned char lock1=0,lock2=0;
186 uint32_t *l1=NULL,*l2=NULL;
188 if (!dos_ltp_info.paging)
190 if (linear < 0x100000UL && !dos_ltp_info.dos_remap) /* if below 1MB boundary and DOS is not remapped, then no translation */
193 /* if VCPI translation is to be used, and lower DOS memory is remapped OR the page requested is >= 1MB (or in adapter ROM/RAM), then call the VCPI server and ask */
194 if (dos_ltp_info.vcpi_xlate && (dos_ltp_info.dos_remap || linear >= 0xC0000UL)) {
195 ent = dos_linear_to_phys_vcpi(linear>>12);
196 if (ent != 0xFFFFFFFFUL) return ent;
197 /* Most likely requests for memory >= 1MB will fail, because VCPI is only required to
198 * provide the system call for lower 1MB DOS conventional memory */
201 /* if we can't use VCPI and cannot translate, then give up. */
202 /* also, this code does not yet support PAE */
203 if (dos_ltp_info.using_pae || dos_ltp_info.cant_xlate)
206 /* re-read control reg because EMM386, etc. is free to change it at any time */
219 dos_ltp_info.cr3 = r3;
220 dos_ltp_info.cr4 = r4;
223 /* OK then, we have to translate */
224 off = linear & 0xFFFUL;
226 if (dos_ltp_info.cr3 < 0x1000) /* if the contents of CR3 are not available, then we cannot translate */
229 /* VCPI possibility: the page table might reside below 1MB in DOS memory, and remain unmapped. */
230 if (dos_ltp_info.dos_remap && dos_ltp_info.vcpi_xlate && dos_ltp_info.cr3 < 0x100000UL) {
232 if (dos_linear_to_phys_vcpi(dos_ltp_info.cr3>>12) == (dos_ltp_info.cr3&0xFFFFF000UL)) /* if VCPI says it's a 1:1 mapping down there, then it's OK */
233 l1 = (uint32_t*)(dos_ltp_info.cr3 & 0xFFFFF000UL);
235 /* DOS4/GW and DOS32A Goodie: the first level of the page table is in conventional memory... and the extender maps DOS 1:1 :) */
236 else if (!dos_ltp_info.dos_remap && dos_ltp_info.cr3 < 0x100000UL) {
238 l1 = (uint32_t*)(dos_ltp_info.cr3 & 0xFFFFF000UL);
241 /* well, then we gotta map it */
242 l1 = (uint32_t*)dpmi_phys_addr_map(dos_ltp_info.cr3 & 0xFFFFF000UL,4096);
243 if (l1 != NULL) lock1 = 1;
248 ent = l1[linear >> 10UL];
249 if (ent & 1) { /* if the page is actually present... */
250 /* if the CPU supports PSE (Page Size Extensions) and has them enabled (via CR4) and the page is marked PS=1 */
251 if ((cpu_cpuid_features.a.raw[2/*EDX*/] & (1 << 3)) && (dos_ltp_info.cr4 & 0x10) && (ent & 0x80)) {
252 /* it's a 4MB page, and we stop searching the page hierarchy here */
253 ret = ent & 0xFFC00000UL; /* bits 31-22 are the actual page address */
255 /* but wait: if the CPU supports PSE-36, then we need to readback address bits 35...32,
256 * or if an AMD64 processor, address bits 39...32 */
257 /* FIXME: So, we can assume if the CPU supports 64-bit long mode, that we can use bits 39...32?
258 * Perhaps this is a more in-depth check that we should be doing in the ltp_probe function? */
259 if (cpu_cpuid_features.a.raw[2/*E2X*/] & (1 << 17)) { /* If PSE support exists */
260 if (cpu_cpuid_features.a.raw[3/*ECX*/] & (1 << 29)) { /* If CPU supports 64-bit long mode */
261 /* AMD64 compatible, up to 40 bits */
262 ret |= ((uint64_t)((ent >> 13UL) & 0xFFUL)) << 32ULL;
264 else { /* else, assume Pentium III compatible, up to 36 bits */
265 ret |= ((uint64_t)((ent >> 13UL) & 0xFUL)) << 32ULL;
270 /* VCPI possibility: the page table might reside below 1MB in DOS memory, and remain unmapped. */
271 if (dos_ltp_info.dos_remap && dos_ltp_info.vcpi_xlate && ent < 0x100000UL) {
273 if (dos_linear_to_phys_vcpi(ent>>12) == (ent&0xFFFFF000UL)) /* if VCPI says it's a 1:1 mapping down there, then it's OK */
274 l2 = (uint32_t*)(ent & 0xFFFFF000UL);
276 /* again the second level is usually in DOS memory where we can assume 1:1 mapping */
277 else if (!dos_ltp_info.dos_remap && !dos_ltp_info.dos_remap && ent < 0x100000UL) {
279 l2 = (uint32_t*)(ent & 0xFFFFF000UL);
282 /* well, then we gotta map it */
283 l2 = (uint32_t*)dpmi_phys_addr_map(ent & 0xFFFFF000UL,4096);
284 if (l2 != NULL) lock2 = 1;
292 ent = l2[linear & 0x3FF];
293 if (ent & 1) { /* if the page is actually present... */
294 ret = ent & 0xFFFFF000UL;
298 if (lock2) dpmi_phys_addr_free((void*)l2);
299 if (lock1) dpmi_phys_addr_free((void*)l1);