[SeaBIOS] [PATCH 1/3] sercon: Initial support for VGA emulation over serial port

Kevin O'Connor kevin at koconnor.net
Thu Oct 27 02:34:55 CEST 2016


Signed-off-by: Kevin O'Connor <kevin at koconnor.net>
---
 Makefile         |   2 +-
 vgasrc/Kconfig   |   6 ++
 vgasrc/sercon.c  | 244 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 vgasrc/vgabios.c |  13 ++-
 vgasrc/vgahw.h   |  72 +++++++++++++++-
 vgasrc/vgautil.h |  23 ++++++
 6 files changed, 348 insertions(+), 12 deletions(-)
 create mode 100644 vgasrc/sercon.c

diff --git a/Makefile b/Makefile
index 3b94ee0..2715662 100644
--- a/Makefile
+++ b/Makefile
@@ -211,7 +211,7 @@ SRCVGA=src/output.c src/string.c src/hw/pci.c src/hw/serialio.c \
     vgasrc/vgainit.c vgasrc/vgabios.c vgasrc/vgafb.c vgasrc/swcursor.c \
     vgasrc/vgafonts.c vgasrc/vbe.c \
     vgasrc/stdvga.c vgasrc/stdvgamodes.c vgasrc/stdvgaio.c \
-    vgasrc/clext.c vgasrc/bochsvga.c vgasrc/geodevga.c \
+    vgasrc/clext.c vgasrc/bochsvga.c vgasrc/geodevga.c vgasrc/sercon.c \
     src/fw/coreboot.c vgasrc/cbvga.c
 
 ifeq "$(CONFIG_VGA_FIXUP_ASM)" "y"
diff --git a/vgasrc/Kconfig b/vgasrc/Kconfig
index f5098a4..d017176 100644
--- a/vgasrc/Kconfig
+++ b/vgasrc/Kconfig
@@ -55,6 +55,12 @@ menu "VGA ROM"
                 Build support for a vgabios wrapper around video
                 devices initialized using coreboot native vga init.
 
+        config VGA_SERCON
+            bool "VGA emulation over serial port"
+            help
+                Build support for a vga option rom that forwards text
+                updates over a serial port.
+
     endchoice
 
     choice
diff --git a/vgasrc/sercon.c b/vgasrc/sercon.c
new file mode 100644
index 0000000..35b60a5
--- /dev/null
+++ b/vgasrc/sercon.c
@@ -0,0 +1,244 @@
+// Video emulation over serial port support
+//
+// Copyright (C) 2016  Kevin O'Connor <kevin at koconnor.net>
+//
+// This file may be distributed under the terms of the GNU LGPLv3 license.
+
+#include <stdarg.h> // va_list
+
+#include "biosvar.h" // GET_GLOBAL
+#include "output.h" // dprintf
+#include "vgabios.h" // struct vgamode_s
+#include "vgafb.h" // struct cursorpos
+#include "vgautil.h" // sercon_write_pixel
+#include "hw/serialio.h" // SEROFF_LSR
+#include "x86.h" // inb
+
+#define SERCON_PORT 0x3f8 // XXX - should be configurable
+#define SERCON_TIMEOUT 100000
+
+
+/****************************************************************
+ * Serial port writing
+ ****************************************************************/
+
+// Write a character to the serial port
+static void
+sercon_putc(char c)
+{
+    int timeout = SERCON_TIMEOUT;
+    while ((inb(SERCON_PORT+SEROFF_LSR) & 0x20) != 0x20)
+        if (!timeout--)
+            // Ran out of time.
+            return;
+    outb(c, SERCON_PORT+SEROFF_DATA);
+}
+
+// Write a small unsigned integer in ascii to the serial port
+static void
+sercon_puti(int v)
+{
+    switch (v) {
+    default:        sercon_putc('0' + ((v / 100) % 10));
+    case 10 ... 99: sercon_putc('0' + ((v /  10) % 10));
+    case  0 ...  9: sercon_putc('0' + (        v % 10));
+    }
+}
+
+// Write a multi-byte escape sequence to the serial port
+static void
+sercon_send_esc(const char *fmt, ...)
+{
+    va_list args;
+    va_start(args, fmt);
+    sercon_putc('\x1b');
+    const char *s = fmt;
+    for (;;) {
+        char c = GET_GLOBAL(*(u8*)s);
+        s++;
+        if (!c)
+            break;
+        if (c == '*') {
+            sercon_puti(va_arg(args, int));
+            continue;
+        }
+        sercon_putc(c);
+    }
+    va_end(args);
+}
+
+static void
+sercon_set_cursor(struct cursorpos pos)
+{
+    // XXX - check if already at given position
+    sercon_send_esc("[*;*H", pos.y+1, pos.x+1);
+}
+
+static void
+sercon_set_attr(struct carattr ca)
+{
+    if (!ca.use_attr)
+        // Assume last sent attribute is still valid
+        return;
+    // XXX - check if new attribute matches last sent attribute
+    static VAR16 u8 sercon_cmap[8] = { 0, 4, 2, 6, 1, 5, 3, 7 };
+    u8 fg = 30 + GET_GLOBAL(sercon_cmap[ca.attr & 7]);
+    u8 bg = 40 + GET_GLOBAL(sercon_cmap[(ca.attr >> 4) & 7]);
+    u8 bold = !!(ca.attr & 0x08);
+    if (bold)
+        sercon_send_esc("[0;*;*;1m", fg, bg);
+    else
+        sercon_send_esc("[0;*;*m", fg, bg);
+}
+
+static void
+sercon_clear_screen(void)
+{
+    sercon_send_esc("[2J");
+}
+
+
+/****************************************************************
+ * Interface functions
+ ****************************************************************/
+
+void
+sercon_scroll(struct cursorpos win, struct cursorpos winsize
+              , int lines, struct carattr ca)
+{
+    if (winsize.x != GET_BDA(video_cols) || winsize.y != GET_BDA(video_rows)+1)
+        // XXX - handle window scrolling?
+        return;
+    sercon_set_attr(ca);
+    if (!lines) {
+        sercon_clear_screen();
+    } else if (lines > 0) {
+        // XXX - send '\n' if lines==1 and on last line
+        sercon_send_esc("[*S", lines);
+    } else {
+        sercon_send_esc("[*T", -lines);
+    }
+}
+
+void
+sercon_write_char(struct cursorpos cp, struct carattr ca)
+{
+    sercon_set_cursor(cp);
+    sercon_set_attr(ca);
+    u8 c = ca.car;
+    if (c <= 0x1f || c >= 0x7f)
+        // XXX - map value to UTF8 code
+        c = '?';
+    sercon_putc(c);
+}
+
+struct carattr
+sercon_read_char(struct cursorpos cp)
+{
+    return (struct carattr){0, 0, 0};
+}
+
+void
+sercon_write_pixel(u8 color, u16 x, u16 y)
+{
+}
+
+u8
+sercon_read_pixel(u16 x, u16 y)
+{
+    return 0;
+}
+
+#define SERCON_MODE 0x03
+static struct vgamode_s sercon_mode VAR16 = {
+    MM_TEXT, 80, 25, 4, 9, 16, 0
+};
+
+struct vgamode_s *sercon_find_mode(int mode)
+{
+    if (mode == SERCON_MODE)
+        return &sercon_mode;
+    return NULL;
+}
+
+void
+sercon_list_modes(u16 seg, u16 *dest, u16 *last)
+{
+    if (dest<last) {
+        SET_FARVAR(seg, *dest, SERCON_MODE);
+        dest++;
+    }
+    SET_FARVAR(seg, *dest, 0xffff);
+}
+
+int
+sercon_get_window(struct vgamode_s *vmode_g, int window)
+{
+    return -1;
+}
+
+int
+sercon_set_window(struct vgamode_s *vmode_g, int window, int val)
+{
+    return -1;
+}
+
+int
+sercon_get_linelength(struct vgamode_s *vmode_g)
+{
+    return GET_GLOBAL(sercon_mode.width) * 2;
+}
+
+int
+sercon_set_linelength(struct vgamode_s *vmode_g, int val)
+{
+    return -1;
+}
+
+int
+sercon_get_displaystart(struct vgamode_s *vmode_g)
+{
+    return 0;
+}
+
+int
+sercon_set_displaystart(struct vgamode_s *vmode_g, int val)
+{
+    return -1;
+}
+
+int
+sercon_get_dacformat(struct vgamode_s *vmode_g)
+{
+    return -1;
+}
+
+int
+sercon_set_dacformat(struct vgamode_s *vmode_g, int val)
+{
+    return -1;
+}
+
+int
+sercon_save_restore(int cmd, u16 seg, void *data)
+{
+    if (cmd & (SR_HARDWARE|SR_DAC|SR_REGISTERS))
+        return -1;
+    return bda_save_restore(cmd, seg, data);
+}
+
+int
+sercon_set_mode(struct vgamode_s *vmode_g, int flags)
+{
+    if (!(flags & MF_NOCLEARMEM))
+        sercon_clear_screen();
+    return 0;
+}
+
+int
+sercon_setup(void)
+{
+    outb(0x03, SERCON_PORT + SEROFF_LCR); // 8N1
+    outb(0x01, SERCON_PORT + 0x02);       // enable fifo
+    return 0;
+}
diff --git a/vgasrc/vgabios.c b/vgasrc/vgabios.c
index 3b9694c..5b08022 100644
--- a/vgasrc/vgabios.c
+++ b/vgasrc/vgabios.c
@@ -14,7 +14,6 @@
 #include "stdvga.h" // stdvga_set_cursor_shape
 #include "string.h" // memset_far
 #include "vgabios.h" // calc_page_size
-#include "vgafb.h" // vgafb_write_char
 #include "vgahw.h" // vgahw_set_mode
 #include "vgautil.h" // swcursor_pre_handle10
 
@@ -158,7 +157,7 @@ set_scan_lines(u8 lines)
 static void
 write_char(struct cursorpos *pcp, struct carattr ca)
 {
-    vgafb_write_char(*pcp, ca);
+    vgahw_write_char(*pcp, ca);
     pcp->x++;
     // Do we need to wrap ?
     if (pcp->x == GET_BDA(video_cols)) {
@@ -199,7 +198,7 @@ write_teletype(struct cursorpos *pcp, struct carattr ca)
         struct cursorpos win = {0, 0, pcp->page};
         struct cursorpos winsize = {GET_BDA(video_cols), nbrows+1};
         struct carattr attr = {' ', 0, 0};
-        vgafb_scroll(win, winsize, 1, attr);
+        vgahw_scroll(win, winsize, 1, attr);
     }
 }
 
@@ -411,7 +410,7 @@ verify_scroll(struct bregs *regs, int dir)
     struct cursorpos win = {ulx, uly, GET_BDA(video_page)};
     struct cursorpos winsize = {wincols, winrows};
     struct carattr attr = {' ', regs->bh, 1};
-    vgafb_scroll(win, winsize, lines, attr);
+    vgahw_scroll(win, winsize, lines, attr);
 }
 
 static void
@@ -429,7 +428,7 @@ handle_1007(struct bregs *regs)
 static void
 handle_1008(struct bregs *regs)
 {
-    struct carattr ca = vgafb_read_char(get_cursor_pos(regs->bh));
+    struct carattr ca = vgahw_read_char(get_cursor_pos(regs->bh));
     regs->al = ca.car;
     regs->ah = ca.attr;
 }
@@ -492,14 +491,14 @@ static void
 handle_100c(struct bregs *regs)
 {
     // XXX - page (regs->bh) is unused
-    vgafb_write_pixel(regs->al, regs->cx, regs->dx);
+    vgahw_write_pixel(regs->al, regs->cx, regs->dx);
 }
 
 static void
 handle_100d(struct bregs *regs)
 {
     // XXX - page (regs->bh) is unused
-    regs->al = vgafb_read_pixel(regs->cx, regs->dx);
+    regs->al = vgahw_read_pixel(regs->cx, regs->dx);
 }
 
 static void noinline
diff --git a/vgasrc/vgahw.h b/vgasrc/vgahw.h
index dab2b4d..5918f3f 100644
--- a/vgasrc/vgahw.h
+++ b/vgasrc/vgahw.h
@@ -1,12 +1,11 @@
 #ifndef __VGAHW_H
 #define __VGAHW_H
 
-#include "types.h" // u8
-#include "config.h" // CONFIG_*
-
 #include "bochsvga.h" // bochsvga_set_mode
-#include "stdvga.h" // stdvga_set_mode
+#include "config.h" // CONFIG_*
 #include "geodevga.h" // geodevga_setup
+#include "stdvga.h" // stdvga_set_mode
+#include "vgafb.h" // vgafb_scroll
 #include "vgautil.h" // stdvga_list_modes
 
 static inline struct vgamode_s *vgahw_find_mode(int mode) {
@@ -16,6 +15,8 @@ static inline struct vgamode_s *vgahw_find_mode(int mode) {
         return bochsvga_find_mode(mode);
     if (CONFIG_VGA_COREBOOT)
         return cbvga_find_mode(mode);
+    if (CONFIG_VGA_SERCON)
+        return sercon_find_mode(mode);
     return stdvga_find_mode(mode);
 }
 
@@ -26,6 +27,8 @@ static inline int vgahw_set_mode(struct vgamode_s *vmode_g, int flags) {
         return bochsvga_set_mode(vmode_g, flags);
     if (CONFIG_VGA_COREBOOT)
         return cbvga_set_mode(vmode_g, flags);
+    if (CONFIG_VGA_SERCON)
+        return sercon_set_mode(vmode_g, flags);
     return stdvga_set_mode(vmode_g, flags);
 }
 
@@ -36,6 +39,8 @@ static inline void vgahw_list_modes(u16 seg, u16 *dest, u16 *last) {
         bochsvga_list_modes(seg, dest, last);
     else if (CONFIG_VGA_COREBOOT)
         cbvga_list_modes(seg, dest, last);
+    else if (CONFIG_VGA_SERCON)
+        sercon_list_modes(seg, dest, last);
     else
         stdvga_list_modes(seg, dest, last);
 }
@@ -49,6 +54,8 @@ static inline int vgahw_setup(void) {
         return geodevga_setup();
     if (CONFIG_VGA_COREBOOT)
         return cbvga_setup();
+    if (CONFIG_VGA_SERCON)
+        return sercon_setup();
     return stdvga_setup();
 }
 
@@ -59,6 +66,8 @@ static inline int vgahw_get_window(struct vgamode_s *vmode_g, int window) {
         return bochsvga_get_window(vmode_g, window);
     if (CONFIG_VGA_COREBOOT)
         return cbvga_get_window(vmode_g, window);
+    if (CONFIG_VGA_SERCON)
+        return sercon_get_window(vmode_g, window);
     return stdvga_get_window(vmode_g, window);
 }
 
@@ -70,6 +79,8 @@ static inline int vgahw_set_window(struct vgamode_s *vmode_g, int window
         return bochsvga_set_window(vmode_g, window, val);
     if (CONFIG_VGA_COREBOOT)
         return cbvga_set_window(vmode_g, window, val);
+    if (CONFIG_VGA_SERCON)
+        return sercon_set_window(vmode_g, window, val);
     return stdvga_set_window(vmode_g, window, val);
 }
 
@@ -80,6 +91,8 @@ static inline int vgahw_get_linelength(struct vgamode_s *vmode_g) {
         return bochsvga_get_linelength(vmode_g);
     if (CONFIG_VGA_COREBOOT)
         return cbvga_get_linelength(vmode_g);
+    if (CONFIG_VGA_SERCON)
+        return sercon_get_linelength(vmode_g);
     return stdvga_get_linelength(vmode_g);
 }
 
@@ -90,6 +103,8 @@ static inline int vgahw_set_linelength(struct vgamode_s *vmode_g, int val) {
         return bochsvga_set_linelength(vmode_g, val);
     if (CONFIG_VGA_COREBOOT)
         return cbvga_set_linelength(vmode_g, val);
+    if (CONFIG_VGA_SERCON)
+        return sercon_set_linelength(vmode_g, val);
     return stdvga_set_linelength(vmode_g, val);
 }
 
@@ -100,6 +115,8 @@ static inline int vgahw_get_displaystart(struct vgamode_s *vmode_g) {
         return bochsvga_get_displaystart(vmode_g);
     if (CONFIG_VGA_COREBOOT)
         return cbvga_get_displaystart(vmode_g);
+    if (CONFIG_VGA_SERCON)
+        return sercon_get_displaystart(vmode_g);
     return stdvga_get_displaystart(vmode_g);
 }
 
@@ -110,6 +127,8 @@ static inline int vgahw_set_displaystart(struct vgamode_s *vmode_g, int val) {
         return bochsvga_set_displaystart(vmode_g, val);
     if (CONFIG_VGA_COREBOOT)
         return cbvga_set_displaystart(vmode_g, val);
+    if (CONFIG_VGA_SERCON)
+        return sercon_set_displaystart(vmode_g, val);
     return stdvga_set_displaystart(vmode_g, val);
 }
 
@@ -118,6 +137,8 @@ static inline int vgahw_get_dacformat(struct vgamode_s *vmode_g) {
         return bochsvga_get_dacformat(vmode_g);
     if (CONFIG_VGA_COREBOOT)
         return cbvga_get_dacformat(vmode_g);
+    if (CONFIG_VGA_SERCON)
+        return sercon_get_dacformat(vmode_g);
     return stdvga_get_dacformat(vmode_g);
 }
 
@@ -126,6 +147,8 @@ static inline int vgahw_set_dacformat(struct vgamode_s *vmode_g, int val) {
         return bochsvga_set_dacformat(vmode_g, val);
     if (CONFIG_VGA_COREBOOT)
         return cbvga_set_dacformat(vmode_g, val);
+    if (CONFIG_VGA_SERCON)
+        return sercon_set_dacformat(vmode_g, val);
     return stdvga_set_dacformat(vmode_g, val);
 }
 
@@ -136,7 +159,48 @@ static inline int vgahw_save_restore(int cmd, u16 seg, void *data) {
         return bochsvga_save_restore(cmd, seg, data);
     if (CONFIG_VGA_COREBOOT)
         return cbvga_save_restore(cmd, seg, data);
+    if (CONFIG_VGA_SERCON)
+        return sercon_save_restore(cmd, seg, data);
     return stdvga_save_restore(cmd, seg, data);
 }
 
+static inline void vgahw_scroll(struct cursorpos win, struct cursorpos winsize
+                                , int lines, struct carattr ca)
+{
+    if (CONFIG_VGA_SERCON)
+        sercon_scroll(win, winsize, lines, ca);
+    else
+        vgafb_scroll(win, winsize, lines, ca);
+}
+
+static inline void vgahw_write_char(struct cursorpos cp, struct carattr ca)
+{
+    if (CONFIG_VGA_SERCON)
+        sercon_write_char(cp, ca);
+    else
+        vgafb_write_char(cp, ca);
+}
+
+static inline struct carattr vgahw_read_char(struct cursorpos cp)
+{
+    if (CONFIG_VGA_SERCON)
+        return sercon_read_char(cp);
+    return vgafb_read_char(cp);
+}
+
+static inline void vgahw_write_pixel(u8 color, u16 x, u16 y)
+{
+    if (CONFIG_VGA_SERCON)
+        sercon_write_pixel(color, x, y);
+    else
+        vgafb_write_pixel(color, x, y);
+}
+
+static inline u8 vgahw_read_pixel(u16 x, u16 y)
+{
+    if (CONFIG_VGA_SERCON)
+        return sercon_read_pixel(x, y);
+    return vgafb_read_pixel(x, y);
+}
+
 #endif // vgahw.h
diff --git a/vgasrc/vgautil.h b/vgasrc/vgautil.h
index 08c4e8d..8436e60 100644
--- a/vgasrc/vgautil.h
+++ b/vgasrc/vgautil.h
@@ -34,6 +34,29 @@ struct bregs;
 void clext_1012(struct bregs *regs);
 int clext_setup(void);
 
+// sercon.c
+void sercon_write_pixel(u8 color, u16 x, u16 y);
+u8 sercon_read_pixel(u16 x, u16 y);
+struct cursorpos;
+struct carattr;
+void sercon_scroll(struct cursorpos win, struct cursorpos winsize
+                   , int lines, struct carattr ca);
+void sercon_write_char(struct cursorpos cp, struct carattr ca);
+struct carattr sercon_read_char(struct cursorpos cp);
+struct vgamode_s *sercon_find_mode(int mode);
+void sercon_list_modes(u16 seg, u16 *dest, u16 *last);
+int sercon_get_window(struct vgamode_s *vmode_g, int window);
+int sercon_set_window(struct vgamode_s *vmode_g, int window, int val);
+int sercon_get_linelength(struct vgamode_s *vmode_g);
+int sercon_set_linelength(struct vgamode_s *vmode_g, int val);
+int sercon_get_displaystart(struct vgamode_s *vmode_g);
+int sercon_set_displaystart(struct vgamode_s *vmode_g, int val);
+int sercon_get_dacformat(struct vgamode_s *vmode_g);
+int sercon_set_dacformat(struct vgamode_s *vmode_g, int val);
+int sercon_save_restore(int cmd, u16 seg, void *data);
+int sercon_set_mode(struct vgamode_s *vmode_g, int flags);
+int sercon_setup(void);
+
 // stdvgaio.c
 u8 stdvga_pelmask_read(void);
 void stdvga_pelmask_write(u8 val);
-- 
2.5.5




More information about the SeaBIOS mailing list