Skip to contentSkip to conversation

Using Linux+dosemu to install Windows

DISCUSSION Stas Sergeev 2 participants 2 messages Jul 26, 2002 1 message not in the archive

lore
Share & tools
lore
#0 lore raw

Hello.

Quoted 4 lines · Patrick J. LoPresti

By instrumenting the code a bit, I learned
that winnt.exe is attempting to invoke the pushfd and popfd instructions (opcodes 0x9c and 0x9d, respectively), which dosemu does not support.

Good investigation but actually you are hacking the wrong place. These instructions are handled in handle_vm86_fault() which is in /usr/src/linux/arch/i386/kernel/vm86.c The attached patch must be a good start for fixing the problem. It is against the latest 2.4.19-pre-ac kernels and probably can't be applied to 2.4.18 because -ac tree have a lot of changes in vm86.

Quoted 3 lines

All winnt.exe is really doing is trying to determine whether the processor is a 486 or higher. It does this by using pushfd and popfd to attempt to alter the next-to-highest bit of the EFLAGS register.

No, it tries to alter the AC flag which is a bit 2 of the higher word of eflags. But dosemu explicitly clears this flag because due to some unknown bugs it sometimes sets spuriously.
grep the do_vm86.c for the string "BUG:" and comment the relevant code, then winnt.exe will work.

Quoted 4 lines

So, my question boils down to this: How can I modify the EFLAGS register from within vm86_GP_fault()? If someone will tell me this, I will submit a patch to add support for emulating the pushf/pushfd and popf/popfd opcodes.

Well, but this won't solve the AC problem...

arch/i386/kernel/vm86.c +63 -50
@@ -502,8 +502,9 @@
 
 void handle_vm86_fault(struct kernel_vm86_regs * regs, long error_code)
 {
-	unsigned char *csp, *ssp;
+	unsigned char *csp, *ssp, opcode;
 	unsigned short ip, sp;
+	int prefix66, pref_done;
 
 #define CHECK_IF_IN_TRAP \
 	if (VMPI.vm86dbg_active && VMPI.vm86dbg_TFpendig) \
@@ -518,63 +519,61 @@
 	sp = SP(regs);
 	ip = IP(regs);
 
-	switch (popb(csp, ip, simulate_sigsegv)) {
-
-	/* operand size override */
-	case 0x66:
-		switch (popb(csp, ip, simulate_sigsegv)) {
+	prefix66 = 0;
+	pref_done = 0;
+	do {
+		switch (opcode = popb(csp, ip, simulate_sigsegv)) {
+			case 0x66:      /* operand prefix */  prefix66=1; break;
+			case 0x67:      /* address prefix */  break;
+			case 0x2e:      /* CS */              break;
+			case 0x3e:      /* DS */              break;
+			case 0x26:      /* ES */              break;
+			case 0x36:      /* SS */              break;
+			case 0x65:      /* GS */              break;
+			case 0x64:      /* FS */              break;
+			case 0xf2:      /* repnz */	      break;
+			case 0xf3:      /* rep */             break;
+			default: pref_done = 1;
+		}
 
-		/* pushfd */
-		case 0x9c:
-			pushl(ssp, sp, get_vflags(regs), simulate_sigsegv);
-			SP(regs) -= 4;
-			IP(regs) += 2;
-			VM86_FAULT_RETURN;
+		if (!pref_done)
+			IP(regs)++;	/* prefix found, adjust IP */
+		else
+			break;		/* no more prefixes */
+	} while (1);
 
-		/* popfd */
-		case 0x9d:
-			{
-			unsigned long newflags=popl(ssp, sp, simulate_sigsegv);
-			SP(regs) += 4;
-			IP(regs) += 2;
-			CHECK_IF_IN_TRAP;
-			set_vflags_long(newflags, regs);
-			VM86_FAULT_RETURN;
-			}
-
-		/* iretd */
-		case 0xcf:
-			{
-			unsigned long newip=popl(ssp, sp, simulate_sigsegv);
-			unsigned long newcs=popl(ssp, sp, simulate_sigsegv);
-			unsigned long newflags=popl(ssp, sp, simulate_sigsegv);
-			SP(regs) += 12;
-			IP(regs) = (unsigned short)newip;
-			regs->cs = (unsigned short)newcs;
-			CHECK_IF_IN_TRAP;
-			set_vflags_long(newflags, regs);
-			VM86_FAULT_RETURN;
-			}
-		/* need this to avoid a fallthrough */
-		default:
-			return_to_32bit(regs, VM86_UNKNOWN);
-		}
+	switch (opcode) {
 
 	/* pushf */
 	case 0x9c:
-		pushw(ssp, sp, get_vflags(regs), simulate_sigsegv);
-		SP(regs) -= 2;
+		if (prefix66) {
+			pushl(ssp, sp, get_vflags(regs), simulate_sigsegv);
+			SP(regs) -= 4;
+		} else {
+			pushw(ssp, sp, get_vflags(regs), simulate_sigsegv);
+			SP(regs) -= 2;
+		}
 		IP(regs)++;
 		VM86_FAULT_RETURN;
 
 	/* popf */
 	case 0x9d:
 		{
-		unsigned short newflags=popw(ssp, sp, simulate_sigsegv);
-		SP(regs) += 2;
+		unsigned long newflags;
+		if (prefix66) {
+			newflags=popl(ssp, sp, simulate_sigsegv);
+			SP(regs) += 4;
+		} else {
+			newflags = popw(ssp, sp, simulate_sigsegv);
+			SP(regs) += 2;
+		}
 		IP(regs)++;
 		CHECK_IF_IN_TRAP;
-		set_vflags_short(newflags, regs);
+		if (prefix66) {
+			set_vflags_long(newflags, regs);
+		} else {
+			set_vflags_short(newflags, regs);
+		}
 		VM86_FAULT_RETURN;
 		}
 
@@ -593,14 +592,28 @@
 	/* iret */
 	case 0xcf:
 		{
-		unsigned short newip=popw(ssp, sp, simulate_sigsegv);
-		unsigned short newcs=popw(ssp, sp, simulate_sigsegv);
-		unsigned short newflags=popw(ssp, sp, simulate_sigsegv);
-		SP(regs) += 6;
+		unsigned long newip;
+		unsigned long newcs;
+		unsigned long newflags;
+		if (prefix66) {
+			newip=popl(ssp, sp, simulate_sigsegv);
+			newcs=popl(ssp, sp, simulate_sigsegv);
+			newflags=popl(ssp, sp, simulate_sigsegv);
+			SP(regs) += 12;
+		} else {
+			newip = popw(ssp, sp, simulate_sigsegv);
+			newcs = popw(ssp, sp, simulate_sigsegv);
+			newflags = popw(ssp, sp, simulate_sigsegv);
+			SP(regs) += 6;
+		}
 		IP(regs) = newip;
 		regs->cs = newcs;
 		CHECK_IF_IN_TRAP;
-		set_vflags_short(newflags, regs);
+		if (prefix66) {
+			set_vflags_long(newflags, regs);
+		} else {
+			set_vflags_short(newflags, regs);
+		}
 		VM86_FAULT_RETURN;
 		}
 

A message that is not in the archive (sent off-list or not yet received): mit.lcs.mail.linux-msdos/[email protected]

#2 lore raw
Quoted 4 lines · Stas Sergeev

Good investigation but actually you are hacking the wrong place. These instructions are handled in handle_vm86_fault() which is in /usr/src/linux/arch/i386/kernel/vm86.c The attached patch must be a good start for fixing the problem.

Ah, so the problem is that the kernel's emulation does not account for multiple prefix bytes?

Will you submit your patch to Alan Cox? It would be nice to get this fixed in the long run.

One more thing. How precise should the emuluation be? According to this:

http://webster.cs.ucr.edu/Page_TechDocs/Doc386/popf.html

The popfd instruction should not modify the VM or RF bits. (Not to mention any bits which are reserved in the current revision of the CPU.) Or does the kernel already take care of these concerns?

Quoted 2 lines

No, it tries to alter the AC flag which is a bit 2 of the higher word of eflags.

Yup, my mistake.

Quoted 4 lines

But dosemu explicitly clears this flag because due to some unknown bugs it sometimes sets spuriously. grep the do_vm86.c for the string "BUG:" and comment the relevant code, then winnt.exe will work.

Thank you!

I believe clearing those flags is a bad idea. They are both used routinely to detect the CPU version:

http://people.freebsd.org/~kato/cpuident.html

(Similar sample code can be found all over the Web.)

I suggest removing the BUG: stuff from the dosemu source code, then figuring out what the real problem is and fixing it properly.

What do you think?

- Pat