Signed-off-by: Kevin O'Connor kevin@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@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);