VOGONS


First post, by superfury

User metadata
Rank l33t++
Rank
l33t++

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?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 1 of 3, by superfury

User metadata
Rank l33t++
Rank
l33t++

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've been reading https://wiki.minix3.org/doku.php?id=developer … guide:earlyboot .
Since Paging is enabled, minix/kernel/arch/i386/head.S must have called pre_init.c:preinit before calling kmain.

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?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 2 of 3, by superfury

User metadata
Rank l33t++
Rank
l33t++

Looking at the kernel sources again, it seems to be mapping Big pages, which aren't supported on a 80386/80486 that's emulated on UniPCemu... 😖

Looking at the documentation again:
https://wiki.minix3.org/doku.php?id=releases: … arerequirements

[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?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io

Reply 3 of 3, by superfury

User metadata
Rank l33t++
Rank
l33t++

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):

flip:
pushf ! Push eflags
pop eax ! eax = eflags
mov edx, eax ! Save original eflags
xor eax, ecx ! Flip the bit to test
push eax ! Push modified eflags value
popf ! Load modified eflags register
pushf
pop eax ! Get it again
push edx
popf ! Restore original eflags register
xor eax, edx ! See if the bit changed
test eax, ecx
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:

!	getprocessor() - determine processor type	Author: Kees J. Bot
! 26 Jan 1994

.text

o32 = 0x66 ! 32 bit operand size prefix

! int getprocessor(void);
! Return 86, 186, 286, 386, 486, 586, ...

.define _getprocessor

_getprocessor:
push bp
mov bp, sp
push sp ! see if pushed sp == sp
pop ax
cmp ax, sp
jz new_processor
mov cx, #0x0120 ! see if shifts are mod 32
shlb ch, cl ! zero tells if 86
mov ax, #86
jz got_processor
mov ax, #186
jmp got_processor

new_processor: ! see if high bits are set in saved IDT
sub sp, #6 ! space for IDT ptr
sidt -6(bp) ! save 3 word IDT ptr
cmpb -1(bp), #0xFF ! top byte of IDT ptr is always FF on 286
mov ax, #286
je got_processor

! 386, 486, 586
and sp, #0xFFFC ! Align stack to avoid AC fault (needed?)
mov cx, #0x0004 ! Try to flip the AC bit introduced on the 486
call flip
mov ax, #386 ! 386 if it didn't react to "flipping"
jz got_processor
mov cx, #0x0020 ! Try to flip the ID bit introduced on the 586
call flip
mov ax, #486 ! 486 if it didn't react
jz got_processor
.data1 o32
pushf
.data1 o32
pusha ! Save the world
.data1 o32
xor ax, ax
inc ax ! eax = 1
.data1 0x0F, 0xA2 ! CPUID instruction tells the processor type
andb ah, #0x0F ! Extract the family (5, 6, ...)
movb al, ah
movb ah, #100
mulb ah ! 500, 600, ...
add ax, #86 ! 586, 686, ...
mov bx, sp
mov 7*4(bx), ax ! Pass ax through
.data1 o32
popa
Show last 32 lines
	.data1	o32
popf

got_processor:
mov sp, bp
pop bp
ret

flip:
push bx ! Save bx and realign stack to multiple of 4
.data1 o32 ! About to operate on a 32 bit object
pushf ! Push eflags
pop ax
pop dx ! dx:ax = eflags
mov bx, dx ! Save original eflags (high word only)
xor dx, cx ! Flip the bit to test
push dx
push ax ! Push modified eflags value
.data1 o32
popf ! Load modified eflags register
.data1 o32
pushf
pop ax
pop dx ! Get it again
push bx
push ax
.data1 o32
popf ! Restore original eflags register
xor dx, bx ! See if the bit changed
test dx, cx
pop bx ! Restore bx
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?

Author of the UniPCemu emulator.
UniPCemu Git repository
UniPCemu for Android, Windows, PSP, Vita and Switch on itch.io