[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