I've been testing with Minix 2.0.4 so far, and it seems to run without any problems rights now.
Now I've been using the Plop BIOS to boot the latest Minix 3.X CD-ROM (with 80486SX CPU emulated), which seems to run quite far, at least until reporting that it's loading the "init" (driver?).
Somewhere at/after that, it tries to execute code at 0008:00401A39. CS is loaded with a 4GB descriptor. Paging is enabled. But said offset seems to be unmapped(The PDE=00400087, the PTE=90909090). This triggers a page fault. This page fault tries to call a page fault handler. Said page fault handling IDT entry uses the IDTR to locate the interrupt, where the base of the IDTR is 0 and limit 0xFFFF. Said entry isn't in memory, according to the paging check of the page fault interrupt handler(it's the NULL page because IDTR.base==0). So that triggers another page fault on address 00000070. But the page fault triggering a page fault isn't allowed, so it triggers a double fault instead(according to documentation and in the emulator). But the double fault handler in the IDT also is in the zero page(at address 00000040). So that will also raise a Page fault. But the double fault handler, trying to execute, faulting isn't allowed according to CPU specs. So that will in turn trigger a triple fault:S
Anyone knows what the cause might be? Besides a faulty LIDT instruction somehow?
Just ran it with a breakpoint on the 286+ and 386+ LIDT instructions. The last time it's called was before running the Plop BIOS and booting the CD-ROM. So Minix never calls LIDT before enabling Paging? That's very strange.
Edit: I'm using Minix 3.3.0, so the entry point should be 400000 (F0400000 after paging is enabled).
But EIP is still in the low range when it triple faults, so why is it faulting?
Edit: Set a breakpoint for 0000:00400000PO(which is offset 400000, protected mode, ignore CS filter). It traps after the mod11_init module is loaded, with a JMP to 00400038(opcode EB) at 0008:00400000.
So head.S is actually reached correctly:D
Although the cd9660(//cd9660/cd9660.kmod) couldn't be loaded. All other modules(/mod01_ds to /mod11_init) were loaded.
It reaches calling pre_init, which is at 00400750, at 0008:004000049(after setting up the __cdecl parameters on the stack correctly).
It then calls 004000e0 for get_parameters.
It then calls the pg_clear function(essentially a memset).
It then calls pgidentity with &kinfo, which was set up before calling pg_clear. pgidentity is at 004013b0.
Then mapkernel at 00401190(from 40077E).
Then the interesting part: it calls something from 400783 to 401200)?
Then it loads CR3 on 40078D(to 401380). which is supposed to load CR3. Edit: The above was shifted by one call.
Then it's supposed to enable paging at 004012b0(according to the call).
It then reaches a first page fault when enabling paging at 401a39.
Edit: Then the PTE contains 90909090... Hmmm.... Do I smell an uninitialized page table(that the PDE is pointing to) somehow for the kernel/4MB block that's promised?
[quote]MINIX 3 runs on Pentium-class hardware or later.[/code]
So even though it starts to boot on a 80386/80486, it can't EVER run on them, due to requiring a Pentium-class CPU to boot at all... Just when you thought you've found a nice OS to verify your CPU emulation with... 😒
Edit: According to the repository, Minix 3.1.4 still supported 4K pages(thus 80386 compatible). But I can't find the ISO image? The official website has a dead link to it?
3.1.5 still supported it...
Edit: It looks like Minix has never supported PTEs for everything? It always has required a Pentium for 4GB PDEs?
Well, interestingly enough, I just tried to run Minix 2.0.3 on UniPCemu's 80486 emulation and I noticed it detected the CPU as a 286 or 8086, depending on himem being loaded or not! So I took a look at the source code and found two candidates: getprocessor(which does a simple push sp check for detecting a 286, then if pushed sp is equal to sp(8086 if pushed sp!=sp before push(after popping it in AX), smsw to check for protected mode(else 8086 if real mode). Since I can see the CPU being in real mode at that point, it must jump to the wrapper calling and testing the 386+ CPUs(thus failing in _getcpumode).
Said function does some simple things(src/lib/i386/misc/getprocessor.s):
1. Try and flip AC bit. If not flipping, it's a 386(works on the 386, passthrough on 486).
2. Try and flip ID bit. My emulated 486 has a CPUID implemented, so it flips. It knows it's at least a 486, but continues on(for CPUID results).
3. It calls CPUID. Said instruction sets the low 4 bits of AH to 4(since it's a 80486SX).
4. It ANDs AH with 0xF, then extends it into EAX using MOVZXB EAX,AH(this might be failing. As to why, see below).
5. It multiplies EAX by 100(to obtain 400 in this case) and adds 86 for the result(486).
AND should work, so the issue is probably in step 4 or 5?
Edit: No CPUID on the 80486, nor 386 reported. So the code must still abort before getprocessor passes to _getprocessor, because the result is a 286(it becomes 386 before _getprocessor starts doing anything).
Edit: SMSW stores 10h into EAX.
Edit: It does seem to reach the _getprocessor function call by means of a E9 near JMP instruction, so the problem is somewhere within the _getprocessor function call?
Edit: And of course, Visual Studio craps out(starts hanging) while I'm single-stepping through my emulator's code... Yay! 😒
Edit: The AC/ID bit toggling routine looks very wrong(according to the source code):
1flip: 2 pushf ! Push eflags 3 pop eax ! eax = eflags 4 mov edx, eax ! Save original eflags 5 xor eax, ecx ! Flip the bit to test 6 push eax ! Push modified eflags value 7 popf ! Load modified eflags register 8 pushf 9 pop eax ! Get it again 10 push edx 11 popf ! Restore original eflags register 12 xor eax, edx ! See if the bit changed 13 test eax, ecx 14 ret
I understand pushing and popping 32-bit registers. But why would you use 16-bit flag push/pops, which don't modify the high parts of the EFLAGS register(what you're trying to test)?
Even if the code is running in 32-bit mode, the PUSH and POP of EFLAGS would be 32-bit, but the corresponding registers push/pop would be 16-bit, thus still having not a single effect on the result? Lol. 🤣
The code starts at 0556:40b5, which is the start of the _getprocessor function(the AND ESP,FFFFFFFC).
The code is running in 16-bit mode.
Edit: Once again, VC++ hangs while stepping through the start of the emulated cache flush(flushPIQ triggering a full PIQ reload in IPS cycle mode).
Edit: Minix 2.0.3 seems more sane:
1! getprocessor() - determine processor type Author: Kees J. Bot 2! 26 Jan 1994 3 4.text 5 6 o32 = 0x66 ! 32 bit operand size prefix 7 8! int getprocessor(void); 9! Return 86, 186, 286, 386, 486, 586, ... 10 11.define _getprocessor 12 13_getprocessor: 14 push bp 15 mov bp, sp 16 push sp ! see if pushed sp == sp 17 pop ax 18 cmp ax, sp 19 jz new_processor 20 mov cx, #0x0120 ! see if shifts are mod 32 21 shlb ch, cl ! zero tells if 86 22 mov ax, #86 23 jz got_processor 24 mov ax, #186 25 jmp got_processor 26 27new_processor: ! see if high bits are set in saved IDT 28 sub sp, #6 ! space for IDT ptr 29 sidt -6(bp) ! save 3 word IDT ptr 30 cmpb -1(bp), #0xFF ! top byte of IDT ptr is always FF on 286 31 mov ax, #286 32 je got_processor 33 34! 386, 486, 586 35 and sp, #0xFFFC ! Align stack to avoid AC fault (needed?) 36 mov cx, #0x0004 ! Try to flip the AC bit introduced on the 486 37 call flip 38 mov ax, #386 ! 386 if it didn't react to "flipping" 39 jz got_processor 40 mov cx, #0x0020 ! Try to flip the ID bit introduced on the 586 41 call flip 42 mov ax, #486 ! 486 if it didn't react 43 jz got_processor 44 .data1 o32 45 pushf 46 .data1 o32 47 pusha ! Save the world 48 .data1 o32 49 xor ax, ax 50 inc ax ! eax = 1 51 .data1 0x0F, 0xA2 ! CPUID instruction tells the processor type 52 andb ah, #0x0F ! Extract the family (5, 6, ...) 53 movb al, ah 54 movb ah, #100 55 mulb ah ! 500, 600, ... 56 add ax, #86 ! 586, 686, ... 57 mov bx, sp 58 mov 7*4(bx), ax ! Pass ax through 59 .data1 o32 60 popa
…Show last 32 lines
61 .data1 o32 62 popf 63 64got_processor: 65 mov sp, bp 66 pop bp 67 ret 68 69flip: 70 push bx ! Save bx and realign stack to multiple of 4 71 .data1 o32 ! About to operate on a 32 bit object 72 pushf ! Push eflags 73 pop ax 74 pop dx ! dx:ax = eflags 75 mov bx, dx ! Save original eflags (high word only) 76 xor dx, cx ! Flip the bit to test 77 push dx 78 push ax ! Push modified eflags value 79 .data1 o32 80 popf ! Load modified eflags register 81 .data1 o32 82 pushf 83 pop ax 84 pop dx ! Get it again 85 push bx 86 push ax 87 .data1 o32 88 popf ! Restore original eflags register 89 xor dx, bx ! See if the bit changed 90 test dx, cx 91 pop bx ! Restore bx 92 ret
It at least checks the flags correctly. It seems 2.0.4 might have been a bit too quick a release to make(according to the source code. It DID say it was slapped together in a hurry as a new base for building Minix). It essentially destroyed any detection of 80486 processors and up!
Edit: Maybe use the 2.0.3 boot.com with 2.0.4 minix.mnx disk image?