From: Gerd Hoffmann <kraxel@redhat.com>
To: seabios@seabios.org
Cc: qemu-devel@nongnu.org, Gerd Hoffmann <kraxel@redhat.com>
Subject: [Qemu-devel] [RFC PATCH 1/2] serial console, output
Date: Fri, 1 Jul 2016 12:54:30 +0200 [thread overview]
Message-ID: <1467370471-20554-2-git-send-email-kraxel@redhat.com> (raw)
In-Reply-To: <1467370471-20554-1-git-send-email-kraxel@redhat.com>
Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
---
src/misc.c | 2 +
src/optionroms.c | 4 +-
src/serial.c | 340 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/util.h | 2 +
4 files changed, 347 insertions(+), 1 deletion(-)
diff --git a/src/misc.c b/src/misc.c
index f02237c..f4b656d 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -11,6 +11,7 @@
#include "output.h" // debug_enter
#include "stacks.h" // call16_int
#include "string.h" // memset
+#include "util.h" // serial_10
#define PORT_MATH_CLEAR 0x00f0
@@ -57,6 +58,7 @@ handle_10(struct bregs *regs)
{
debug_enter(regs, DEBUG_HDL_10);
// don't do anything, since the VGA BIOS handles int10h requests
+ sercon_10(regs);
}
// NMI handler
diff --git a/src/optionroms.c b/src/optionroms.c
index 65f7fe0..e6b308c 100644
--- a/src/optionroms.c
+++ b/src/optionroms.c
@@ -432,9 +432,11 @@ vgarom_setup(void)
run_file_roms("vgaroms/", 1, NULL);
rom_reserve(0);
- if (rom_get_last() == BUILD_ROM_START)
+ if (rom_get_last() == BUILD_ROM_START) {
// No VGA rom found
+ sercon_enable();
return;
+ }
VgaROM = (void*)BUILD_ROM_START;
enable_vga_console();
diff --git a/src/serial.c b/src/serial.c
index 88349c8..74b91bb 100644
--- a/src/serial.c
+++ b/src/serial.c
@@ -315,3 +315,343 @@ handle_17(struct bregs *regs)
default: handle_17XX(regs); break;
}
}
+
+/****************************************************************
+ * serial console output
+ ****************************************************************/
+
+VARLOW u16 sercon_port;
+VARLOW u8 sercon_mode;
+VARLOW u8 sercon_cols;
+VARLOW u8 sercon_rows;
+
+VARLOW u8 sercon_row;
+VARLOW u8 sercon_col;
+
+/*
+ * We have a small output buffer here, for lazy output. That allows
+ * to avoid a whole bunch of control sequences for pointless cursor
+ * moves, so when logging the output it'll be *alot* less cluttered.
+ *
+ * sercon_char/attr is the actual output buffer.
+ * sercon_col_lazy is the column of the terminal's cursor, typically
+ * a few positions left of sercon_col.
+ * sercon_attr_last is the most recent attribute sent to the terminal.
+ */
+VARLOW u8 sercon_attr_last;
+VARLOW u8 sercon_col_lazy;
+VARLOW u8 sercon_char[8];
+VARLOW u8 sercon_attr[8];
+
+VARLOW u8 sercon_cmap[8] = { '0', '4', '2', '6', '1', '5', '3', '7' };
+
+static void sercon_putchar(u8 chr)
+{
+ u16 addr = GET_LOW(sercon_port);
+ u32 end = irqtimer_calc_ticks(0x0a);
+
+ for (;;) {
+ u8 lsr = inb(addr+SEROFF_LSR);
+ if ((lsr & 0x60) == 0x60) {
+ // Success - can write data
+ outb(chr, addr+SEROFF_DATA);
+ break;
+ }
+ if (irqtimer_check(end)) {
+ break;
+ }
+ yield();
+ }
+}
+
+static void sercon_init(void)
+{
+ /* reset */
+ sercon_putchar('\x1b');
+ sercon_putchar('c');
+ /* clear screen */
+ sercon_putchar('\x1b');
+ sercon_putchar('[');
+ sercon_putchar('2');
+ sercon_putchar('J');
+}
+
+static void sercon_set_color(u8 fg, u8 bg, u8 bold)
+{
+ sercon_putchar('\x1b');
+ sercon_putchar('[');
+ sercon_putchar('0');
+ if (fg != 7) {
+ sercon_putchar(';');
+ sercon_putchar('3');
+ sercon_putchar(GET_LOW(sercon_cmap[fg & 7]));
+ }
+ if (bg != 0) {
+ sercon_putchar(';');
+ sercon_putchar('4');
+ sercon_putchar(GET_LOW(sercon_cmap[bg & 7]));
+ }
+ if (bold) {
+ sercon_putchar(';');
+ sercon_putchar('1');
+ }
+ sercon_putchar('m');
+}
+
+static void sercon_set_attr(u8 attr)
+{
+ if (attr == GET_LOW(sercon_attr_last))
+ return;
+
+ SET_LOW(sercon_attr_last, attr);
+ sercon_set_color((attr >> 0) & 7,
+ (attr >> 4) & 7,
+ attr & 0x08);
+}
+
+static void sercon_cursor_goto(u8 row, u8 col)
+{
+ row++; col++;
+ sercon_putchar('\x1b');
+ sercon_putchar('[');
+ sercon_putchar('0' + row / 10);
+ sercon_putchar('0' + row % 10);
+ sercon_putchar(';');
+ sercon_putchar('0' + col / 10);
+ sercon_putchar('0' + col % 10);
+ sercon_putchar('H');
+}
+
+static void sercon_print_char(u8 chr, u8 attr)
+{
+ if (chr != 0x00) {
+ sercon_set_attr(attr);
+ sercon_putchar(chr);
+ } else {
+ /* move cursor right */
+ sercon_putchar('\x1b');
+ sercon_putchar('[');
+ sercon_putchar('C');
+ }
+}
+
+static void sercon_flush_lazy(void)
+{
+ u8 count = GET_LOW(sercon_col) - GET_LOW(sercon_col_lazy);
+ u8 pos = 0;
+
+ if (!count && !GET_LOW(sercon_attr[0]))
+ return;
+
+ while (count) {
+ sercon_print_char(GET_LOW(sercon_char[pos]),
+ GET_LOW(sercon_attr[pos]));
+ count--;
+ pos++;
+ }
+
+ if (pos < ARRAY_SIZE(sercon_char) && GET_LOW(sercon_char[pos])) {
+ sercon_print_char(GET_LOW(sercon_char[pos]),
+ GET_LOW(sercon_attr[pos]));
+ /* move cursor left */
+ sercon_putchar('\x1b');
+ sercon_putchar('[');
+ sercon_putchar('D');
+ }
+
+ SET_LOW(sercon_col_lazy, GET_LOW(sercon_col));
+ for (pos = 0; pos < ARRAY_SIZE(sercon_char); pos++) {
+ SET_LOW(sercon_attr[pos], 0x07);
+ SET_LOW(sercon_char[pos], 0x00);
+ }
+}
+
+/* Set video mode */
+static void sercon_1000(struct bregs *regs)
+{
+ SET_LOW(sercon_mode, regs->al);
+ switch (regs->al) {
+ case 0x03:
+ default:
+ SET_LOW(sercon_cols, 80);
+ SET_LOW(sercon_rows, 25);
+ regs->al = 0x30;
+ }
+ SET_LOW(sercon_row, 0);
+ SET_LOW(sercon_col, 0);
+ SET_LOW(sercon_col_lazy, 0);
+ sercon_init();
+}
+
+/* Set cursor position */
+static void sercon_1002(struct bregs *regs)
+{
+ u8 row = regs->dh;
+ u8 col = regs->dl;
+
+ if (row == GET_LOW(sercon_row) &&
+ col >= GET_LOW(sercon_col_lazy) &&
+ col < GET_LOW(sercon_col_lazy) + ARRAY_SIZE(sercon_char)) {
+ SET_LOW(sercon_col, col);
+ if (col+1 == GET_LOW(sercon_col_lazy) + ARRAY_SIZE(sercon_char))
+ sercon_flush_lazy();
+ } else {
+ sercon_flush_lazy();
+ if (row == GET_LOW(sercon_row) && col == 0) {
+ sercon_putchar('\r');
+ } else {
+ sercon_cursor_goto(row, col);
+ }
+ SET_LOW(sercon_row, row);
+ SET_LOW(sercon_col, col);
+ SET_LOW(sercon_col_lazy, col);
+ }
+}
+
+/* Get cursor position */
+static void sercon_1003(struct bregs *regs)
+{
+ regs->ax = 0;
+ regs->ch = 0;
+ regs->cl = 7;
+ regs->dh = GET_LOW(sercon_row);
+ regs->dl = GET_LOW(sercon_col);
+}
+
+/* Scroll up window */
+static void sercon_1006(struct bregs *regs)
+{
+ sercon_flush_lazy();
+ sercon_putchar('\r');
+ sercon_putchar('\n');
+}
+
+/* Read character and attribute at cursor position */
+static void sercon_1008(struct bregs *regs)
+{
+ regs->ah = 0x07;
+ regs->bh = ' ';
+}
+
+/* Write character and attribute at cursor position */
+static void sercon_1009(struct bregs *regs)
+{
+ u16 count = regs->cx;
+ u8 pos;
+
+ if (count == 1) {
+ pos = GET_LOW(sercon_col) - GET_LOW(sercon_col_lazy);
+ if (pos < ARRAY_SIZE(sercon_char)) {
+ sercon_char[pos] = regs->al;
+ sercon_attr[pos] = regs->bl;
+ }
+ } else if (regs->al == 0x20 &&
+ GET_LOW(sercon_rows) * GET_LOW(sercon_cols) == count &&
+ GET_LOW(sercon_row) == 0 &&
+ GET_LOW(sercon_col) == 0) {
+ /* override everything with spaces -> this is clear screen */
+ sercon_flush_lazy();
+ sercon_putchar('\x1b');
+ sercon_putchar('[');
+ sercon_putchar('2');
+ sercon_putchar('J');
+ } else {
+ sercon_flush_lazy();
+ sercon_set_attr(regs->bl);
+ while (count) {
+ sercon_putchar(regs->al);
+ count--;
+ }
+ sercon_cursor_goto(GET_LOW(sercon_row),
+ GET_LOW(sercon_col));
+ }
+}
+
+/* Teletype output */
+static void sercon_100e(struct bregs *regs)
+{
+ u8 pos, row, col;
+
+ switch (regs->al) {
+ case 7:
+ // beep
+ break;
+ case 8:
+ sercon_flush_lazy();
+ sercon_putchar(regs->al);
+ col = GET_LOW(sercon_col);
+ if (col > 0) {
+ col--;
+ SET_LOW(sercon_col, col);
+ SET_LOW(sercon_col_lazy, col);
+ }
+ break;
+ case '\r':
+ sercon_flush_lazy();
+ sercon_putchar(regs->al);
+ SET_LOW(sercon_col, 0);
+ SET_LOW(sercon_col_lazy, 0);
+ break;
+ case '\n':
+ sercon_flush_lazy();
+ sercon_putchar(regs->al);
+ row = GET_LOW(sercon_row);
+ row++;
+ if (row >= GET_LOW(sercon_rows))
+ row = GET_LOW(sercon_rows)-1;
+ SET_LOW(sercon_row, row);
+ break;
+ default:
+ pos = GET_LOW(sercon_col) - GET_LOW(sercon_col_lazy);
+ sercon_char[pos] = regs->al;
+ SET_LOW(sercon_col, GET_LOW(sercon_col) + 1);
+ if (pos+1 == ARRAY_SIZE(sercon_char))
+ sercon_flush_lazy();
+ break;
+ }
+}
+
+/* Get current video mode */
+static void sercon_100f(struct bregs *regs)
+{
+ regs->al = GET_LOW(sercon_mode);
+ regs->ah = GET_LOW(sercon_cols);
+}
+
+/* VBE 2.0 */
+static void sercon_104f(struct bregs *regs)
+{
+ regs->ax = 0x0100;
+}
+
+void VISIBLE16
+sercon_10(struct bregs *regs)
+{
+ if (!GET_LOW(sercon_port))
+ return;
+
+ switch (regs->ah) {
+ case 0x00: sercon_1000(regs); break;
+ case 0x02: sercon_1002(regs); break;
+ case 0x03: sercon_1003(regs); break;
+ case 0x06: sercon_1006(regs); break;
+ case 0x08: sercon_1008(regs); break;
+ case 0x09: sercon_1009(regs); break;
+ case 0x0e: sercon_100e(regs); break;
+ case 0x0f: sercon_100f(regs); break;
+ case 0x4f: sercon_104f(regs); break;
+ default:
+ dprintf(1, "%s: ah 0x%02x, not implemented\n",
+ __func__, regs->ah);
+ }
+}
+
+void sercon_enable(void)
+{
+ u16 addr = PORT_SERIAL1;
+
+ SET_LOW(sercon_port, addr);
+ outb(0x03, addr + SEROFF_LCR); // 8N1
+ outb(0x01, addr + 0x02); // enable fifo
+ enable_vga_console();
+}
diff --git a/src/util.h b/src/util.h
index 7b41207..29f17be 100644
--- a/src/util.h
+++ b/src/util.h
@@ -230,6 +230,8 @@ void code_mutable_preinit(void);
// serial.c
void serial_setup(void);
void lpt_setup(void);
+void sercon_10(struct bregs *regs);
+void sercon_enable(void);
// vgahooks.c
void handle_155f(struct bregs *regs);
--
1.8.3.1
next prev parent reply other threads:[~2016-07-01 10:54 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-07-01 10:54 [Qemu-devel] [RFC PATCH 0/2] seabios: add serial console support Gerd Hoffmann
2016-07-01 10:54 ` Gerd Hoffmann [this message]
2016-07-01 15:47 ` [Qemu-devel] [SeaBIOS] [RFC PATCH 1/2] serial console, output Kevin O'Connor
2016-07-04 8:16 ` Gerd Hoffmann
2016-07-04 9:11 ` [Qemu-devel] " Paolo Bonzini
2016-07-04 12:46 ` Gerd Hoffmann
2016-07-04 12:48 ` Paolo Bonzini
2016-07-04 15:26 ` Kevin O'Connor
2016-07-04 15:45 ` Paolo Bonzini
2016-07-04 20:10 ` Gerd Hoffmann
2016-07-04 16:00 ` Kevin O'Connor
2016-07-04 16:03 ` Paolo Bonzini
2016-07-04 17:28 ` Kevin O'Connor
2016-07-04 20:18 ` Gerd Hoffmann
2016-07-04 20:23 ` Gerd Hoffmann
2016-07-01 10:54 ` [Qemu-devel] [RFC PATCH 2/2] serial console, input Gerd Hoffmann
2016-07-01 17:07 ` [Qemu-devel] [SeaBIOS] " Kevin O'Connor
2016-07-01 18:07 ` Kevin O'Connor
2016-07-04 9:16 ` Gerd Hoffmann
2016-07-04 15:34 ` Kevin O'Connor
2016-07-04 20:03 ` Gerd Hoffmann
2016-07-01 15:47 ` [Qemu-devel] [RFC PATCH 0/2] seabios: add serial console support Gerd Hoffmann
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1467370471-20554-2-git-send-email-kraxel@redhat.com \
--to=kraxel@redhat.com \
--cc=qemu-devel@nongnu.org \
--cc=seabios@seabios.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.