On Tue, Sep 05, 2017 at 10:01:41AM +0200, Gerd Hoffmann wrote:
Note, though, in addition to checking for broken x86emu, the assembler entry code would also have to check that the last mode was not set via a vesa call (to avoid the "skifree" bug).
Ok, Is there some easy way to access the BDA from assembler code (specifically the video_mode field)? Guess I must set a segment register for that. Maybe it's easier to just place an additional flag in the fseg which we can easily reach via cs override ...
The flag can't be in the f-segment because it needs to be read/writable at runtime. I ran some tests - it looks like it is okay to access the varlow segment - one just can't write to it. Unfortunately, it's not enough to simply test for a legacy modeset call (int 0x10 ah=0x00) as it looks like Vista calls that in its emulation mode during startup. However, it looks like it is okay if one verifies that the legacy modeset call is for a text mode (eg, int 0x10 ax=0x0003).
I put together the patch below (based off of current seabios master). It doesn't do anything except test that one can safely enter the C code. It survives winxp, winvista, winvista+skifree, and xorg on fedora 13 (these were my typical trouble spots with vgabios testing).
-Kevin
From ad37047c3bbd7b5d92b849acfee3844a64ac1a28 Mon Sep 17 00:00:00 2001
From: Kevin O'Connor kevin@koconnor.net Date: Tue, 5 Sep 2017 11:45:08 -0400 Subject: [PATCH] sercon: Add support for hooking vga handler
Signed-off-by: Gerd Hoffmann kraxel@redhat.com Signed-off-by: Kevin O'Connor kevin@koconnor.net --- Makefile | 1 + src/Kconfig | 5 +++++ src/config.h | 1 + src/optionroms.c | 6 +++++- src/romlayout.S | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/sercon.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/util.h | 3 +++ 7 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 src/sercon.c
diff --git a/Makefile b/Makefile index 946df7e..07ac747 100644 --- a/Makefile +++ b/Makefile @@ -30,6 +30,7 @@ LD32BIT_FLAG:=-melf_i386 # Source files SRCBOTH=misc.c stacks.c output.c string.c block.c cdrom.c disk.c mouse.c kbd.c \ system.c serial.c clock.c resume.c pnpbios.c vgahooks.c pcibios.c apm.c \ + sercon.c \ hw/pci.c hw/timer.c hw/rtc.c hw/dma.c hw/pic.c hw/ps2port.c hw/serialio.c \ hw/usb.c hw/usb-uhci.c hw/usb-ohci.c hw/usb-ehci.c \ hw/usb-hid.c hw/usb-msc.c hw/usb-uas.c \ diff --git a/src/Kconfig b/src/Kconfig index 77ec9c7..55a87cb 100644 --- a/src/Kconfig +++ b/src/Kconfig @@ -306,6 +306,11 @@ menu "Hardware support" default y help Support serial ports. This also enables int 14 serial port calls. + config SERCON + bool "Serial console" + default y + help + Support redirecting vga output to the serial console. config LPT bool "Parallel port" default y diff --git a/src/config.h b/src/config.h index baca029..e56d4a7 100644 --- a/src/config.h +++ b/src/config.h @@ -100,6 +100,7 @@ #define DEBUG_HDL_pmm 1 #define DEBUG_HDL_pcibios 9 #define DEBUG_HDL_apm 9 +#define DEBUG_HDL_sercon 9
#define DEBUG_unimplemented 2 #define DEBUG_invalid 3 diff --git a/src/optionroms.c b/src/optionroms.c index 65f7fe0..4f6611e 100644 --- a/src/optionroms.c +++ b/src/optionroms.c @@ -404,8 +404,10 @@ struct rom_header *VgaROM; void vgarom_setup(void) { - if (! CONFIG_OPTIONROMS) + if (! CONFIG_OPTIONROMS) { + sercon_setup(); return; + }
dprintf(1, "Scan for VGA option rom\n");
@@ -432,6 +434,8 @@ vgarom_setup(void) run_file_roms("vgaroms/", 1, NULL); rom_reserve(0);
+ sercon_setup(); + if (rom_get_last() == BUILD_ROM_START) // No VGA rom found return; diff --git a/src/romlayout.S b/src/romlayout.S index 89b3784..8ed53dc 100644 --- a/src/romlayout.S +++ b/src/romlayout.S @@ -414,6 +414,61 @@ __csm_return: popfw lretw
+// Serial console "hooked vga" entry point + DECLFUNC entry_sercon +entry_sercon: + // Setup for chain loading to real vga handler + pushfw + pushl %cs:sercon_real_vga_handler + + // Set %ds to varlow segment + cli + cld + pushw %ds + pushl %eax + movl $_zonelow_seg, %eax + movl %eax, %ds + + // Test if the sercon handler can be called + movl %esp, %eax // Test for broken x86emu + pushl $1f + retl +1: cmpl %esp, %eax + jne 4f + cmpb $0, sercon_enable // Test that sercon is enabled + je 3f + + // Call handle_sercon() on the extra stack +2: movl StackPos, %eax + subl $PUSHBREGS_size+8, %eax + SAVEBREGS_POP_DSEAX + movl %esp, PUSHBREGS_size(%eax) + movw %ss, PUSHBREGS_size+4(%eax) + + movw %ds, %dx // Setup %ss/%esp and call function + movw %dx, %ss + movl %eax, %esp + calll handle_sercon + + movl %esp, %eax // Restore registers and return + movw PUSHBREGS_size+4(%eax), %ss + movl PUSHBREGS_size(%eax), %esp + RESTOREBREGS_DSEAX + iretw + + // sercon disabled - verify not 0x03 modeset and otherwise exit +3: popl %eax + cmpw $0x0003, %ax + jne 5f + pushl %eax + jmp 2b + + // Running on broken x86emu - restore stack and exit +4: movl %eax, %esp + popl %eax +5: popw %ds + iretw +
/**************************************************************** * Interrupt entry points diff --git a/src/sercon.c b/src/sercon.c new file mode 100644 index 0000000..7307772 --- /dev/null +++ b/src/sercon.c @@ -0,0 +1,54 @@ +// Serial console support +// +// Copyright (C) 2017 Kevin O'Connor kevin@koconnor.net +// +// This file may be distributed under the terms of the GNU LGPLv3 license. + +#include "biosvar.h" // GET_IVT +#include "bregs.h" // struct bregs +#include "output.h" // dprintf +#include "romfile.h" // romfile_loadint +#include "util.h" // sercon_setup + +struct segoff_s sercon_real_vga_handler VARFSEG; +u8 sercon_enable VARLOW; + +void +sercon_setup(void) +{ + ASSERT32FLAT(); + if (!CONFIG_SERCON || (GET_IVT(0x10).segoff != FUNC16(entry_10).segoff + && !romfile_loadint("etc/sercon-hook", 0))) + return; + + dprintf(3, "init sercon\n"); + sercon_real_vga_handler = GET_IVT(0x10); + SET_IVT(0x10, FUNC16(entry_sercon)); +} + +static void +sercon_1000(struct bregs *regs) +{ + int mode = regs->al & 0x7f; + SET_LOW(sercon_enable, (mode == 0x03)); +} + +static void +sercon_104f(struct bregs *regs) +{ + // Disable sercon entry point on any vesa modeset + if (regs->al == 0x00) + SET_LOW(sercon_enable, 0); +} + +void VISIBLE16 +handle_sercon(struct bregs *regs) +{ + if (!CONFIG_SERCON) + return; + debug_enter(regs, DEBUG_HDL_sercon); + switch (regs->ah) { + case 0x00: sercon_1000(regs); break; + case 0x4f: sercon_104f(regs); break; + } +} diff --git a/src/util.h b/src/util.h index 8269057..8b747b3 100644 --- a/src/util.h +++ b/src/util.h @@ -229,6 +229,9 @@ void startBoot(void); void reloc_preinit(void *f, void *arg); void code_mutable_preinit(void);
+// sercon.c +void sercon_setup(void); + // serial.c void serial_setup(void); void lpt_setup(void);