I'm just thinking:
- For 8-bit memory operands, the location is checked against the descriptor and paging, faulting on the faults in predetermined order.
- For 16-bit memory operands, the first location is checked against the descriptor and paging, then the next location.
But what happens with 32-bit memory locations(the same might be said for 64-bit memory locations on x86_64)? Are each of the four locations checked? Or are only the first and last locations checked against paging and memory(which would speed up 32-bit memory access checks by a factor of twice as fast and four times as fast for 64-bit memory operands)?
A memory-mapped device won't help, because it's a CPU internal process(except TLB fetches from RAM)? It's all segment descriptor cache(segmentation) and Paging-related, thus happening without the bus activating(since those should be executed after verification only(except when counting modern CPUs executing, then reverting memory accesses due to prediction missing))?
The only issue that might be there, is that the priority of faults against each other?
When an operand crosses paging 4K borders and segmentation does fault on the higher location of the access(e.g. at n+3), but paging faults at the lower location of the access(e.g. up to n+1 is valid, n+2 is an invalid page). In UniPCemu, currently all memory accesses are faulted at a byte level, checking n against segmentation and paging, then n+1, n+2 and finally n+3.
What happens when a segment limit is broken with the segment selector value that's loaded from memory, where the segment selector value's location(e.g. the "LDS ESI,DS:xxxx" offset, thus xxxx+4 and xxxx+5 offsets) is past the limit of the segment selector, the offset(from the LDS operand) of the offset to load into ESI is in valid memory(not breaking the DS segment descriptor's limit) but the paging PTE/PDE for the xxxx memory location isn't valid?
Will it throw a #GP(0) for the invalid segment selector(to load into DS) at DS:[xxxx+4] offset(invalid offset) or a #PF(...) exception for the DS:[xxxx] memory location(invalid page)?
So far made the segmentation system itself able to throw seperate segmentation, (data) breakpoint and page faults. Now it's mostly modifying all calls to the checkMMUaccess function(which checks for said segmentation and paging faults) in all CPU modules.
I guess the order of segmentation, then data breakpoints(which mainly just sets flags for faulting after the instruction completes and doesn't fault(no interrupt/exception raised)), and finally after those pass for all addresses(one byte at a time) check for paging faults?
Edit: After about 4-6 hours of manual work searching and replacing all memory access checks in the cpu modules, it should now (excluding common/instruction-specific split instructions like ADD etc., due to being checked in two places(once in the instruction handler and once in the internal instruction handler) first check all available parameters for segmentation faults(and also adding debug register checks(memory breakpoints) after each byte in a byte/word/dword is verified against the segment descriptors). Also stack accesses are counted as one block(sizing up to the total stack space to process) instead of one byte now(on both levels)), then once those are validated(and registered as data breakpoint if applicable internally) it will start checking the memory data against the paging mechanism.
The only instructions that don't fully follow said law are currently the split instruction parameters(where e.g. the opcode 01h ADD handler will check segmentation, debugger and paging in the order specified above as blocks(of 16-bit or 32-bit), executes a read from the source operand, after which (only if the destination is a memory operand) the internal ADD handler(which is common for all ADD instructions) will check the destination operand as specified above. Although that usually isn't a problem, since only one of the two operands is in memory(thus becoming entirely as documented as a result).
One place that will act kind of odd is the extended read opcodes like FFh JMP/CALL instructions, as the GRP handler will check(both layers in order as blocks) and read extra data, after the first 16 or 32-bits are already checked(through all layers), since only then it knows that there's some extra data(the segment to JMP/CALL) is to be read(and checked against).
All other single-variable instructions should work as documented(through both segmentation and paging after that). The major exception would be the conditional-write instructions(like ARPL, which only checks for writing to the destination when the privilege level of the destination privilege level is higher(numerically lower) than the source. The same kind of conditional applies to the 80486+ CMPXCHG instruction).
Whoops, just tried to run Windows 95 again, noticed that it infinitely tries to execute a page fault due to the flag 0x10 being set, which I've added in checkMMUaccess to disable the segmentation check(used together with 0x20 to just run segmentation checks). Apparently it's also used to NOP the Paging unit... Oops... Having fixed said bug, the mechanism shouldn't bug out anymore(the only exceptions being conditional-write instructions).
Just fixed the only two operands needing more data: FF/3 and FF/5 far CALL/JMP instructions to behave accordingly.
Edit: The crash at segment 117h still remains. One thing I did find out is that it's a protected-mode driver that's causing the issue, not a Virtual 8086 one(although one does get executed during boot, probably some real-mode drivers running in a Virtual machine).
The strange POP FS with the protected-mode driver(privilege level 3) is still odd though. Anyone? The bootlog.txt goes up to some esdi_506.pdr protected-mode driver when not in safe mode, but this problem(occuring in safe mode) seems to happen in privilege level 3 protected mode?
The safe mode log ends with something saying "Starting kernel" or something like that (as far as I can remember).
Edit: Hmmm... I see opcode 8E loading 12F into FS, where the base is 0(which usually is invalid in Windows, since that's starting with a NULL pointer on protected-mode software, an invalidated area, at which the entire TIB should be invalid to start with)? ESP is F88(just like at the location the invalid segmentation load is executed), CR3 is 295000h, CS:EIP is 117:00007DB8(the error location, or at least close to it)?
Bootlog in Safe mode with the blue screen cause(because of program at 117:7BDx):
Edit: Hmmmm... Those reads and writes to CS: and ES: (also DS:?)... It might be using said value as some kind of FS handler(exception handler?) buffer?
Edit: Linear address 000095c2 seems to contain said data?
Another strange thing is that EIP is within 64K limits the entire time, even though it's a 4GB code segment? Why start within 64K? Isn't it usually loaded way higher? 0x40000 for executables and 0x80000000+ for drivers etc.? See https://www.tenouk.com/visualcplusmfc/visualcplusmfc20.html figure 3?
Just fixed the XCHG instructions to properly check segmentation first, then paging.
Also optimized up to 80286+ instructions to check writes against writes only and not against reads too(since write access on both paging and segmentation also requires read access by x86 design). Only 80386+ opcodes are left to do(normal and 0F opcodes). 80486 doesn't need changes(no new opcodes with checks).
Just fixed a bug in the 80386+ BSF/BSR memory protection checks(checking for writes against the src operand instead of the dest operand).
Also optimized the 80386 0F opcodes for memory writes(see previous post). Now only the 32-bit 8086/80186 (the 80386 module) opcodes are left.
Edit: Just optimized the 32-bity 8086/80186(the 80386 module) opcodes. Now all memory checks should only be checked for once, during execution of an instruction(instead of multiple times for modifying memory operands, e.g. add [memory],reg will execute one check for reg and one for memory(except when memory has multiple operands, e.g. L*S instructions and far jmp/call instructions from memory operands, as well as MOV [memory],reg and MOV reg,[memory])).