From patchwork Fri Jul 1 10:54:30 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gerd Hoffmann X-Patchwork-Id: 9209715 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id EDDA260752 for ; Fri, 1 Jul 2016 10:55:06 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id DD52E28527 for ; Fri, 1 Jul 2016 10:55:06 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id D1E7728690; Fri, 1 Jul 2016 10:55:06 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 22DAF28527 for ; Fri, 1 Jul 2016 10:55:06 +0000 (UTC) Received: from localhost ([::1]:60919 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bIw6D-0002sR-99 for patchwork-qemu-devel@patchwork.kernel.org; Fri, 01 Jul 2016 06:55:05 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:55320) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bIw5o-0002rZ-Gv for qemu-devel@nongnu.org; Fri, 01 Jul 2016 06:54:42 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1bIw5l-00009Y-HP for qemu-devel@nongnu.org; Fri, 01 Jul 2016 06:54:40 -0400 Received: from mx1.redhat.com ([209.132.183.28]:49976) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bIw5l-000095-9R for qemu-devel@nongnu.org; Fri, 01 Jul 2016 06:54:37 -0400 Received: from int-mx11.intmail.prod.int.phx2.redhat.com (int-mx11.intmail.prod.int.phx2.redhat.com [10.5.11.24]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 9D11681117; Fri, 1 Jul 2016 10:54:36 +0000 (UTC) Received: from nilsson.home.kraxel.org (ovpn-116-19.ams2.redhat.com [10.36.116.19]) by int-mx11.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id u61AsZTH016436; Fri, 1 Jul 2016 06:54:35 -0400 Received: by nilsson.home.kraxel.org (Postfix, from userid 500) id 2340B82FE3; Fri, 1 Jul 2016 12:54:35 +0200 (CEST) From: Gerd Hoffmann To: seabios@seabios.org Date: Fri, 1 Jul 2016 12:54:30 +0200 Message-Id: <1467370471-20554-2-git-send-email-kraxel@redhat.com> In-Reply-To: <1467370471-20554-1-git-send-email-kraxel@redhat.com> References: <1467370471-20554-1-git-send-email-kraxel@redhat.com> X-Scanned-By: MIMEDefang 2.68 on 10.5.11.24 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.27]); Fri, 01 Jul 2016 10:54:36 +0000 (UTC) X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 Subject: [Qemu-devel] [RFC PATCH 1/2] serial console, output X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: qemu-devel@nongnu.org, Gerd Hoffmann Errors-To: qemu-devel-bounces+patchwork-qemu-devel=patchwork.kernel.org@nongnu.org Sender: "Qemu-devel" X-Virus-Scanned: ClamAV using ClamSMTP Signed-off-by: Gerd Hoffmann --- 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);