On Mon, Apr 06, 2015 at 10:05:36AM +0200, Jon Doe wrote:
Here's a log showing a boot into X, then switching to console, then trying to kldload vesa.ko. Looks like X is happily probing vbe modes, but the console chokes with "SeaVGABIOS: x86emu leal trap!".
That error message indicates that the vga bios is running in an older version of the x86emu emulator that is too broken to be capable of running the vga bios. (Specifically, the "leal" instruction is not properly implemented.)
Unfortunately, there isn't much that can be done about this on the vga bios side. There are newer versions of x86emu available that are capable of emulating SeaVGABIOS, but I don't know if someone has already integrated it into FreeBSD or how one would go about integrating it. Alternatively, since the log shows the vesa mode scan was run natively instead of via x86emu, it may be possible to change the code to switch modes using native execution instead of via x86emu.
-Kevin
On Mon, Apr 6, 2015 at 3:43 PM, Kevin O'Connor kevin@koconnor.net wrote:
On Mon, Apr 06, 2015 at 10:05:36AM +0200, Jon Doe wrote:
Here's a log showing a boot into X, then switching to console, then trying to kldload vesa.ko. Looks like X is happily probing vbe modes, but the console chokes with "SeaVGABIOS: x86emu leal trap!".
That error message indicates that the vga bios is running in an older version of the x86emu emulator that is too broken to be capable of running the vga bios. (Specifically, the "leal" instruction is not properly implemented.)
Unfortunately, there isn't much that can be done about this on the vga bios side. There are newer versions of x86emu available that are capable of emulating SeaVGABIOS, but I don't know if someone has already integrated it into FreeBSD or how one would go about integrating it. Alternatively, since the log shows the vesa mode scan was run natively instead of via x86emu, it may be possible to change the code to switch modes using native execution instead of via x86emu.
-Kevin
Ah, I see :-( Thanks for explaining the problem. I think the native vesa mode scan you're referring to might have been X, not FreeBSD.
Kevin O'Connor wrote:
(Specifically, the "leal" instruction is not properly implemented.)
Unfortunately, there isn't much that can be done about this on the vga bios side.
Really? Impossible to save flags, use other opcodes, and restore flags?
lea isn't used in vgasrc/ besides in the trap that triggers the fault.
In src/romlayout.S lea is used in two places to bump esp before calls. If those code paths are used also by SeaVGABIOS then maybe they could be rewritten with simpler instructions?
//Peter
On Wed, Apr 08, 2015 at 08:48:11PM +0200, Peter Stuge wrote:
Kevin O'Connor wrote:
(Specifically, the "leal" instruction is not properly implemented.)
Unfortunately, there isn't much that can be done about this on the vga bios side.
Really? Impossible to save flags, use other opcodes, and restore flags?
lea isn't used in vgasrc/ besides in the trap that triggers the fault.
In src/romlayout.S lea is used in two places to bump esp before calls. If those code paths are used also by SeaVGABIOS then maybe they could be rewritten with simpler instructions?
The problem is not with leal in hand written assembler - the problem is with leal instructions generated by gcc. To see the assembler gcc produces for the vgabios one can look at out/vgaccode16.raw.s . Or, alternatively, one can run: objdump -m i386 -M i8086 -M suffix -ldr out/vgarom.o
We've fixed up gcc assembler in the past (see scripts/vgafixup.py) to work around x86emu. However, the leal instruction seems painful to patch out - particularly so when %esp is one of the registers read or written in the leal instruction. If anyone wants to take a stab at a workaround, feel free to submit a patch.
-Kevin
On Thu, Apr 9, 2015 at 4:32 AM, Kevin O'Connor kevin@koconnor.net wrote:
On Wed, Apr 08, 2015 at 08:48:11PM +0200, Peter Stuge wrote:
Kevin O'Connor wrote:
(Specifically, the "leal" instruction is not properly implemented.)
Unfortunately, there isn't much that can be done about this on the vga bios side.
Really? Impossible to save flags, use other opcodes, and restore flags?
lea isn't used in vgasrc/ besides in the trap that triggers the fault.
In src/romlayout.S lea is used in two places to bump esp before calls. If those code paths are used also by SeaVGABIOS then maybe they could be rewritten with simpler instructions?
The problem is not with leal in hand written assembler - the problem is with leal instructions generated by gcc. To see the assembler gcc produces for the vgabios one can look at out/vgaccode16.raw.s . Or, alternatively, one can run: objdump -m i386 -M i8086 -M suffix -ldr out/vgarom.o
We've fixed up gcc assembler in the past (see scripts/vgafixup.py) to work around x86emu. However, the leal instruction seems painful to patch out - particularly so when %esp is one of the registers read or written in the leal instruction. If anyone wants to take a stab at a workaround, feel free to submit a patch.
-Kevin
Might be instructive to look at how vmware and virtualbox BIOSes is able to work around this problem. Surely their BIOS code is written in C?
Fixing this in x86emu is probably the right thing to do (just checked, PCBSD 10.1's x86emu is still broken), but this won't help if you're stuck with an old release.
On Wed, Apr 08, 2015 at 10:32:23PM -0400, Kevin O'Connor wrote:
The problem is not with leal in hand written assembler - the problem is with leal instructions generated by gcc. To see the assembler gcc produces for the vgabios one can look at out/vgaccode16.raw.s . Or, alternatively, one can run: objdump -m i386 -M i8086 -M suffix -ldr out/vgarom.o
We've fixed up gcc assembler in the past (see scripts/vgafixup.py) to work around x86emu. However, the leal instruction seems painful to patch out - particularly so when %esp is one of the registers read or written in the leal instruction. If anyone wants to take a stab at a workaround, feel free to submit a patch.
It occurred to me that it might be possible to replace the leal instruction with a function call. The generic form of leal is:
leal $offset(%base, %index, $scale), %destination
which does:
%destination = $offset + %base + (%index * $scale)
if the above is found in the gcc assembler it could be replaced with something like:
pushl %base pushl %index pushl $offset pushl $scale callw fake_leal popl %destination
(The fake_leal assembler function that performs the actual calculation would also need to be written.) The above wouldn't work if %index is %esp, but I don't think that would happen in practice.
I'm not going to tackle the above right now, but maybe someone else is interested in attempting it. A word of caution though - I know leal doesn't work in very old x86emu versions, but it's unclear if any other gcc produced instructions are also broken. The leal instruction might just be one of many instructions that don't work.
-Kevin
On Thu, Apr 09, 2015 at 09:56:07AM -0400, Kevin O'Connor wrote:
On Wed, Apr 08, 2015 at 10:32:23PM -0400, Kevin O'Connor wrote:
The problem is not with leal in hand written assembler - the problem is with leal instructions generated by gcc. To see the assembler gcc produces for the vgabios one can look at out/vgaccode16.raw.s . Or, alternatively, one can run: objdump -m i386 -M i8086 -M suffix -ldr out/vgarom.o
We've fixed up gcc assembler in the past (see scripts/vgafixup.py) to work around x86emu. However, the leal instruction seems painful to patch out - particularly so when %esp is one of the registers read or written in the leal instruction. If anyone wants to take a stab at a workaround, feel free to submit a patch.
It occurred to me that it might be possible to replace the leal instruction with a function call. The generic form of leal is:
leal $offset(%base, %index, $scale), %destination
which does:
%destination = $offset + %base + (%index * $scale)
if the above is found in the gcc assembler it could be replaced with something like:
pushl %base pushl %index pushl $offset pushl $scale callw fake_leal popl %destination
(The fake_leal assembler function that performs the actual calculation would also need to be written.) The above wouldn't work if %index is %esp, but I don't think that would happen in practice.
I'm not going to tackle the above right now, but maybe someone else is interested in attempting it. A word of caution though - I know leal doesn't work in very old x86emu versions, but it's unclear if any other gcc produced instructions are also broken. The leal instruction might just be one of many instructions that don't work.
I gave it a shot anyway - seabios patch is below.
It's incredibly ugly. But, it does allow FC11 and FC12 to boot into graphics which wasn't possible before. Also, I don't see any indication in the x86emu commit logs ( http://anongit.freedesktop.org/git/xorg/xserver.git ) that any other instructions besides leal will present a problem.
-Kevin
commit c900162603f19552ec8ad3648bbb71f2effc227d Author: Kevin O'Connor kevin@koconnor.net Date: Thu Apr 9 11:59:41 2015 -0400
vgabios: Hack for leal emulation
Signed-off-by: Kevin O'Connor kevin@koconnor.net
diff --git a/scripts/vgafixup.py b/scripts/vgafixup.py index a981bbf..f32c055 100644 --- a/scripts/vgafixup.py +++ b/scripts/vgafixup.py @@ -8,19 +8,26 @@ # The x86emu code widely used in Linux distributions when running Xorg # in vesamode is known to have issues with "retl", "leavel", "entryl", # and some variants of "calll". This code modifies those instructions -# (ret and leave) that are known to be generated by gcc to avoid -# triggering the x86emu bugs. +# that are known to be generated by gcc to avoid triggering the x86emu +# bugs. It also translates "leal" instructions into a function call +# that emulates it at runtime.
# It is also known that the Windows vgabios emulator has issues with # addressing negative offsets to the %esp register. That has been # worked around by not using the gcc parameter "-fomit-frame-pointer" # when compiling.
-import sys +import sys, re
def main(): infilename, outfilename = sys.argv[1:] infile = open(infilename, 'r') + re_leal = re.compile( + r'^(?P<offset>[^(]*)(' + r'(?P<base>[^,)]*)' + r'(?:,(?P<index>[^,)]*))?' + r'(?:,(?P<scale>[^,)]*))?)' + r', (?P<dest>.*)$') out = [] for line in infile: sline = line.strip() @@ -30,6 +37,25 @@ def main(): out.append('movl %ebp, %esp ; popl %ebp\n') elif sline.startswith('call'): out.append('pushw %ax ; callw' + sline[4:] + '\n') + elif sline.startswith('leal'): + m = re_leal.match(sline[5:]) + if m is None or m.group('index') == '%esp': + print("Invalid leal instruction: %s" % (sline,)) + sys.exit(-1) + offset, base, index, scale, dest = m.group( + 'offset', 'base', 'index', 'scale', 'dest') + if not offset: + offset = '0' + if not base: + base = '$0' + if not index: + index = '$0' + if not scale: + scale = '1' + scale = {'1': 0, '2': 1, '4': 2, '8': 3}[scale] + out.append('pushl %s ; pushl %s ; pushl $%s ; pushw $%d' + ' ; callw fake_leal ; popl %s\n' % ( + base, index, offset, scale, dest)) else: out.append(line) infile.close() diff --git a/vgasrc/vgaentry.S b/vgasrc/vgaentry.S index e0ab954..21128c5 100644 --- a/vgasrc/vgaentry.S +++ b/vgasrc/vgaentry.S @@ -45,31 +45,32 @@ _rom_header_signature: * Entry points ****************************************************************/
- // Force a fault if found to be running on broken x86emu versions. - DECLFUNC x86emu_fault -msg: .ascii "SeaVGABIOS: x86emu leal trap!\n" -x86emu_fault: -#if CONFIG_DEBUG_IO - movw %cs:DebugOutputPort, %dx - movw $msg, %si -1: movb %cs:(%si), %al - outb %al, (%dx) - incw %si - cmpw $x86emu_fault, %si - jl 1b -#endif -1: hlt - jmp 1b + // Emulate a leal instruction (see scripts/vgafixup.py) + // On entry stack contains: base, index, offset, scale + DECLFUNC fake_leal + .global fake_leal +fake_leal: + pushl %ebp + movl %esp, %ebp + pushfl + pushl %eax + pushl %ecx + + movb 6(%ebp), %cl + movl 12(%ebp), %eax + shll %cl, %eax + addl 8(%ebp), %eax + addl %eax, 16(%ebp) + + popl %ecx + popl %eax + popfl + popl %ebp + retw $10
// This macro implements a call while avoiding instructions // that old versions of x86emu have problems with. .macro VGA_CALLL cfunc - // Make sure leal instruction works. - movl $0x8000, %ecx - leal (%ecx, %ecx, 1), %ecx - cmpl $0x10000, %ecx - jne x86emu_fault - // Use callw instead of calll push %ax callw \cfunc .endm
On Thu, Apr 9, 2015 at 6:54 PM, Kevin O'Connor kevin@koconnor.net wrote:
On Thu, Apr 09, 2015 at 09:56:07AM -0400, Kevin O'Connor wrote:
On Wed, Apr 08, 2015 at 10:32:23PM -0400, Kevin O'Connor wrote:
The problem is not with leal in hand written assembler - the problem is with leal instructions generated by gcc. To see the assembler gcc produces for the vgabios one can look at out/vgaccode16.raw.s . Or, alternatively, one can run: objdump -m i386 -M i8086 -M suffix -ldr out/vgarom.o
We've fixed up gcc assembler in the past (see scripts/vgafixup.py) to work around x86emu. However, the leal instruction seems painful to patch out - particularly so when %esp is one of the registers read or written in the leal instruction. If anyone wants to take a stab at a workaround, feel free to submit a patch.
It occurred to me that it might be possible to replace the leal instruction with a function call. The generic form of leal is:
leal $offset(%base, %index, $scale), %destination
which does:
%destination = $offset + %base + (%index * $scale)
if the above is found in the gcc assembler it could be replaced with something like:
pushl %base pushl %index pushl $offset pushl $scale callw fake_leal popl %destination
(The fake_leal assembler function that performs the actual calculation would also need to be written.) The above wouldn't work if %index is %esp, but I don't think that would happen in practice.
I'm not going to tackle the above right now, but maybe someone else is interested in attempting it. A word of caution though - I know leal doesn't work in very old x86emu versions, but it's unclear if any other gcc produced instructions are also broken. The leal instruction might just be one of many instructions that don't work.
I gave it a shot anyway - seabios patch is below.
It's incredibly ugly. But, it does allow FC11 and FC12 to boot into graphics which wasn't possible before. Also, I don't see any indication in the x86emu commit logs ( http://anongit.freedesktop.org/git/xorg/xserver.git ) that any other instructions besides leal will present a problem.
-Kevin
commit c900162603f19552ec8ad3648bbb71f2effc227d Author: Kevin O'Connor kevin@koconnor.net Date: Thu Apr 9 11:59:41 2015 -0400
vgabios: Hack for leal emulation Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
diff --git a/scripts/vgafixup.py b/scripts/vgafixup.py index a981bbf..f32c055 100644 --- a/scripts/vgafixup.py +++ b/scripts/vgafixup.py @@ -8,19 +8,26 @@ # The x86emu code widely used in Linux distributions when running Xorg # in vesamode is known to have issues with "retl", "leavel", "entryl", # and some variants of "calll". This code modifies those instructions -# (ret and leave) that are known to be generated by gcc to avoid -# triggering the x86emu bugs. +# that are known to be generated by gcc to avoid triggering the x86emu +# bugs. It also translates "leal" instructions into a function call +# that emulates it at runtime.
# It is also known that the Windows vgabios emulator has issues with # addressing negative offsets to the %esp register. That has been # worked around by not using the gcc parameter "-fomit-frame-pointer" # when compiling.
-import sys +import sys, re
def main(): infilename, outfilename = sys.argv[1:] infile = open(infilename, 'r')
- re_leal = re.compile(
r'^(?P<offset>[^(]*)\('
r'(?P<base>[^,)]*)'
r'(?:,(?P<index>[^,)]*))?'
r'(?:,(?P<scale>[^,)]*))?\)'
out = [] for line in infile: sline = line.strip()r', (?P<dest>.*)$')
@@ -30,6 +37,25 @@ def main(): out.append('movl %ebp, %esp ; popl %ebp\n') elif sline.startswith('call'): out.append('pushw %ax ; callw' + sline[4:] + '\n')
elif sline.startswith('leal'):
m = re_leal.match(sline[5:])
if m is None or m.group('index') == '%esp':
print("Invalid leal instruction: %s" % (sline,))
sys.exit(-1)
offset, base, index, scale, dest = m.group(
'offset', 'base', 'index', 'scale', 'dest')
if not offset:
offset = '0'
if not base:
base = '$0'
if not index:
index = '$0'
if not scale:
scale = '1'
scale = {'1': 0, '2': 1, '4': 2, '8': 3}[scale]
out.append('pushl %s ; pushl %s ; pushl $%s ; pushw $%d'
' ; callw fake_leal ; popl %s\n' % (
infile.close()base, index, offset, scale, dest)) else: out.append(line)
diff --git a/vgasrc/vgaentry.S b/vgasrc/vgaentry.S index e0ab954..21128c5 100644 --- a/vgasrc/vgaentry.S +++ b/vgasrc/vgaentry.S @@ -45,31 +45,32 @@ _rom_header_signature:
- Entry points
****************************************************************/
// Force a fault if found to be running on broken x86emu versions.
DECLFUNC x86emu_fault
-msg: .ascii "SeaVGABIOS: x86emu leal trap!\n" -x86emu_fault: -#if CONFIG_DEBUG_IO
movw %cs:DebugOutputPort, %dx
movw $msg, %si
-1: movb %cs:(%si), %al
outb %al, (%dx)
incw %si
cmpw $x86emu_fault, %si
jl 1b
-#endif -1: hlt
jmp 1b
// Emulate a leal instruction (see scripts/vgafixup.py)
// On entry stack contains: base, index, offset, scale
DECLFUNC fake_leal
.global fake_leal
+fake_leal:
pushl %ebp
movl %esp, %ebp
pushfl
pushl %eax
pushl %ecx
movb 6(%ebp), %cl
movl 12(%ebp), %eax
shll %cl, %eax
addl 8(%ebp), %eax
addl %eax, 16(%ebp)
popl %ecx
popl %eax
popfl
popl %ebp
retw $10 // This macro implements a call while avoiding instructions // that old versions of x86emu have problems with. .macro VGA_CALLL cfunc
// Make sure leal instruction works.
movl $0x8000, %ecx
leal (%ecx, %ecx, 1), %ecx
cmpl $0x10000, %ecx
jne x86emu_fault
// Use callw instead of calll push %ax callw \cfunc .endm
I can report that this patch also works for FreeBSD 8.2's VESA console. There seems to be some unrelated problem when running 1.8-stable so I applied it to rel-1.7.5.2