[SeaBIOS] [PATCH 5/6] sercon: split-output implementation

Gerd Hoffmann kraxel at redhat.com
Fri Sep 8 08:18:27 CEST 2017


Allows to run the serial console in parallel with a vga display.
Output will show up on both vga and serial line.
Input will be accepted from both keyboard and serial line.

Signed-off-by: Gerd Hoffmann <kraxel at redhat.com>
---
 src/optionroms.c |   2 ++
 src/sercon.c     | 106 ++++++++++++++++++++++++++++++++++++++++++++-----------
 src/romlayout.S  |  46 ++++++++++++++++++++++++
 3 files changed, 133 insertions(+), 21 deletions(-)

diff --git a/src/optionroms.c b/src/optionroms.c
index 8665db42e3..76a70ea686 100644
--- a/src/optionroms.c
+++ b/src/optionroms.c
@@ -442,6 +442,8 @@ vgarom_setup(void)
     }
 
     VgaROM = (void*)BUILD_ROM_START;
+    if (romfile_loadint("etc/sercon-enable", 0))
+        sercon_setup();
     enable_vga_console();
 }
 
diff --git a/src/sercon.c b/src/sercon.c
index f785aa8fd9..5699905f32 100644
--- a/src/sercon.c
+++ b/src/sercon.c
@@ -9,6 +9,7 @@
 #include "stacks.h" // yield
 #include "output.h" // dprintf
 #include "util.h" // irqtimer_calc_ticks
+#include "string.h" // memcpy
 #include "hw/serialio.h" // SEROFF_IER
 #include "cp437.h"
 
@@ -45,6 +46,9 @@ static void cursor_pos_set(u8 row, u8 col)
  ****************************************************************/
 
 VARLOW u16 sercon_port;
+VARLOW u8 sercon_split;
+VARLOW u8 sercon_enable;
+VARFSEG struct segoff_s sercon_real_vga_handler;
 
 /*
  * We have a small output buffer here, for lazy output.  That allows
@@ -64,6 +68,11 @@ VARLOW u8 sercon_attr = 0x07;
 
 static VAR16 u8 sercon_cmap[8] = { '0', '4', '2', '6', '1', '5', '3', '7' };
 
+static int sercon_splitmode(void)
+{
+    return GET_LOW(sercon_split);
+}
+
 static void sercon_putchar(u8 chr)
 {
     u16 addr = GET_LOW(sercon_port);
@@ -174,6 +183,15 @@ static void sercon_print_utf8(u8 chr)
     }
 }
 
+static void sercon_cursor_pos_set(u8 row, u8 col)
+{
+    if (!sercon_splitmode()) {
+        cursor_pos_set(row, col);
+    } else {
+        /* let vgabios update cursor */
+    }
+}
+
 static void sercon_lazy_cursor_sync(void)
 {
     u8 row = cursor_pos_row();
@@ -222,7 +240,7 @@ static void sercon_lazy_flush(void)
 
 static void sercon_lazy_cursor_update(u8 row, u8 col)
 {
-    cursor_pos_set(row, col);
+    sercon_cursor_pos_set(row, col);
     SET_LOW(sercon_row_last, row);
     SET_LOW(sercon_col_last, col);
 }
@@ -241,7 +259,7 @@ static void sercon_lazy_backspace(void)
 
 static void sercon_lazy_cr(void)
 {
-    cursor_pos_set(cursor_pos_row(), 0);
+    sercon_cursor_pos_set(cursor_pos_row(), 0);
 }
 
 static void sercon_lazy_lf(void)
@@ -256,7 +274,7 @@ static void sercon_lazy_lf(void)
             SET_LOW(sercon_row_last, GET_LOW(sercon_row_last) - 1);
         }
     }
-    cursor_pos_set(row, cursor_pos_col());
+    sercon_cursor_pos_set(row, cursor_pos_col());
 }
 
 static void sercon_lazy_move_cursor(void)
@@ -268,7 +286,7 @@ static void sercon_lazy_move_cursor(void)
         sercon_lazy_cr();
         sercon_lazy_lf();
     } else {
-        cursor_pos_set(cursor_pos_row(), col);
+        sercon_cursor_pos_set(cursor_pos_row(), col);
     }
 }
 
@@ -293,23 +311,28 @@ static void sercon_1000(struct bregs *regs)
     u8 mode = regs->al & 0x7f;
     u8 rows, cols;
 
-    switch (mode) {
-    case 0x03:
-    default:
-        cols = 80;
-        rows = 25;
-        regs->al = 0x30;
+    if (!sercon_splitmode()) {
+        switch (mode) {
+        case 0x03:
+        default:
+            cols = 80;
+            rows = 25;
+            regs->al = 0x30;
+        }
+        cursor_pos_set(0, 0);
+        SET_BDA(video_mode, mode);
+        SET_BDA(video_cols, cols);
+        SET_BDA(video_rows, rows-1);
+        SET_BDA(cursor_type, 0x0007);
+    } else {
+        SET_LOW(sercon_enable, (mode == 0x03));
+        /* let vgabios handle mode init */
     }
+
     SET_LOW(sercon_col_last, 0);
     SET_LOW(sercon_row_last, 0);
     SET_LOW(sercon_attr_last, 0);
 
-    cursor_pos_set(0, 0);
-    SET_BDA(video_mode, mode);
-    SET_BDA(video_cols, cols);
-    SET_BDA(video_rows, rows-1);
-    SET_BDA(cursor_type, 0x0007);
-
     sercon_term_reset();
     sercon_term_no_linewrap();
     if (clearscreen)
@@ -326,10 +349,7 @@ static void sercon_1001(struct bregs *regs)
 /* Set cursor position */
 static void sercon_1002(struct bregs *regs)
 {
-    u8 row = regs->dh;
-    u8 col = regs->dl;
-
-    cursor_pos_set(row, col);
+    sercon_cursor_pos_set(regs->dh, regs->dl);
 }
 
 /* Get cursor position */
@@ -427,7 +447,13 @@ static void sercon_100f(struct bregs *regs)
 /* VBE 2.0 */
 static void sercon_104f(struct bregs *regs)
 {
-    regs->ax = 0x0100;
+    if (!sercon_splitmode()) {
+        regs->ax = 0x0100;
+    } else {
+        // Disable sercon entry point on any vesa modeset
+        if (regs->al == 0x00)
+            SET_LOW(sercon_enable, 0);
+    }
 }
 
 static void sercon_10XX(struct bregs *regs)
@@ -458,6 +484,31 @@ sercon_10(struct bregs *regs)
     }
 }
 
+void VISIBLE16
+sercon_10_splitmode(struct bregs *regs)
+{
+    if (!CONFIG_SERCON)
+        return;
+    if (!GET_LOW(sercon_port))
+        return;
+
+    switch (regs->ah) {
+    case 0x01:
+    case 0x02:
+    case 0x03:
+    case 0x08:
+    case 0x0f:
+        /* nothing, vgabios did all work */
+        break;
+    case 0x00: sercon_1000(regs); break;
+    case 0x06: sercon_1006(regs); break;
+    case 0x09: sercon_1009(regs); break;
+    case 0x0e: sercon_100e(regs); break;
+    case 0x4f: sercon_104f(regs); break;
+    default:   sercon_10XX(regs); break;
+    }
+}
+
 void sercon_setup(void)
 {
     if (!CONFIG_SERCON)
@@ -466,6 +517,19 @@ void sercon_setup(void)
     struct segoff_s seabios, vgabios;
     u16 addr = PORT_SERIAL1;
 
+    vgabios = GET_IVT(0x10);
+    seabios = FUNC16(entry_10);
+    if (vgabios.seg != seabios.seg ||
+        vgabios.offset != seabios.offset) {
+        dprintf(1, "%s:%d: using splitmode (vgabios %04x:%04x, hook %04x:%04x)\n",
+                __func__, __LINE__,
+                vgabios.seg, vgabios.offset,
+                seabios.seg, seabios.offset);
+        sercon_real_vga_handler = vgabios;
+        SET_IVT(0x10, FUNC16(entry_sercon));
+        SET_LOW(sercon_split, 1);
+    }
+
     SET_LOW(sercon_port, addr);
     outb(0x03, addr + SEROFF_LCR); // 8N1
     outb(0x01, addr + 0x02);       // enable fifo
diff --git a/src/romlayout.S b/src/romlayout.S
index 89b3784d60..1c9d56df1b 100644
--- a/src/romlayout.S
+++ b/src/romlayout.S
@@ -414,6 +414,52 @@ __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
+
+2:      popl %eax
+        popw %ds
+        pushl $sercon_10_splitmode
+#if CONFIG_ENTRY_EXTRASTACK
+        jmp irqentry_arg_extrastack
+#else
+        jmp irqentry_arg
+#endif
+
+        // 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
-- 
2.9.3




More information about the SeaBIOS mailing list