joncampbell123 / dosbox-x

DOSBox-X fork of the DOSBox project
GNU General Public License v2.0
2.68k stars 381 forks source link

80286 segment limits are not observed (?) #3526

Open nmlgc opened 2 years ago

nmlgc commented 2 years ago

Describe the bug

#include <dos.h>
#include <stdio.h>

int main(void)
{
    puts("1) 2 bytes at A800:FFFF...");
    *((int far *)(MK_FP(0xA800, 0xFFFF))) = -1;
    puts("2) 4 bytes at E000:FFFE...");
    *((long far *)(MK_FP(0xE000, 0xFFFE))) = -1;
    puts("Did not crash.");
    return 0;
}

Attempting the first write hangs the system on real hardware (PC-9821 Nw133) in DOS, and crashes with a GPF error box when trying it from the Windows 95 command prompt. It does not crash on DOSBox-X (and, in fact, on no other PC-98 emulator besides Anex86).

This is apparently caused by segment access limits that were introduced with the 286. According to #3368, these checks currently don't work with the dynamic core, but I'm getting the same result with any other non-dynamic cpucore and segment limits = true.

3123 further suggests that this particular check was removed with 0.83.21, but testing on 0.83.20 showed the same (non-hanging) behavior.

Steps to reproduce the behaviour

  1. Run the attached FFFFault.exe in DOSBox-X
  2. Compare the behavior to real hardware

Expected behavior

The current behavior (not crashing/hanging) is probably the best default, but it would be nice to have the crash available as an option.

What operating system(s) this bug have occurred on?

Windows 10 21H2

What version(s) of DOSBox-X have this bug?

0.83.20, 0de1c3149c7d18246be6133bce2d23ca090603bb

Used configuration

[dosbox]
machine = pc9821

[cpu]
core = simple
segment limits = true
cputype = 386

Output log

No response

Additional information

No response

Have you checked that no similar bug report(s) exist?

Code of Conduct & Contributing Guidelines

joncampbell123 commented 2 years ago

I tested segment limits too some time back on real hardware, here's what I found.

If at any time the segment limits are the maximum supported by the processor, then the processor acts as if there are no segment limits.

That means a segment limit of 0xFFFF on a 286, and segment limit of 0xFFFFFFFF on a 386, is treated as a special case to not check segment limits. Any other value enforces segment limits. However 0xFFFF on a 386 does enable segment limit checks. This was the behavior seen on actual Intel processors.

Virtual 8086 mode could be thought of as a fixed case where segment limits are always 0xFFFF and enforced. You said you tested this under a Windows 95 DOS box, which is a virtual 8086 environment. Please boot into real mode DOS and try your program again. You may also try booting into Windows 95 inside DOSBox-X and then trying your program inside a DOS box there.

My test code found that on a 386, a segment limit of 0xFFFFEFFF would catch accesses to 0xFFFFF000 including DWORD reads/writes that overlapped into that area, and so on. But a segment limit of 0xFFFFFFFF did not trigger any such faults, even if a DWORD read or write was issued to 0xFFFFFFFF that would wrap around back to 0x00000000.

What complicates things here is that HIMEM.SYS and/or Windows 95 real mode DOS tend to leave the processor in flat real mode (protected mode hack to allow real mode to access all 4GB of addressable memory I/O) and your code in real mode for that reason might not trigger any faults. DOSBox-X also emulates by default 4GB memory limits as does SVN and your program might not trigger there as well. I recall somewhere it was explained that HIMEM.SYS does this to improve performance when it or INT 15h is used to copy to/from extended memory or copy extended memory to extended memory.

Another complication is that DOSBox SVN never enforced segment limits. DOSBox-X enforces segment limits but only for specific MOV instructions that were seen when various Demoscene or DOS extender code (including a Windows 3.1 driver!) used segment limits as an oddball way of faking a linear framebuffer from the bank switching functions of SVGA hardware, or another case where the DOS "box" in Windows 95 OSR2 requires segment limit faults to work in order to not hang starting up a DOS session. Why that particular code used segment limit exceptions and not page fault exceptions is beyond me, except perhaps some clever way to draw like a linear framebuffer but in a way that is downward compatible with the 286.

nmlgc commented 2 years ago

I tested it in real mode DOS on the same system (PC-9821 Nw133) by directly booting into it via the Windows 95 boot menu and not loading anything, and the program also hangs at the first write. Loading HIMEM.SYS doesn't seem to make a difference though.