297 lines
14 KiB
Text
297 lines
14 KiB
Text
|
=============================================
|
||
|
Google Serial Graphics Adapter BIOS (SGABIOS)
|
||
|
|
||
|
Copyright 2007 Google Inc.
|
||
|
|
||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
you may not use this file except in compliance with the License.
|
||
|
You may obtain a copy of the License at
|
||
|
|
||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||
|
|
||
|
Unless required by applicable law or agreed to in writing, software
|
||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
See the License for the specific language governing permissions and
|
||
|
limitations under the License.
|
||
|
=============================================
|
||
|
Status: Implemented (as of 2007-08-08)
|
||
|
|
||
|
Nathan Laredo <nil@google.com>
|
||
|
Modified: 2008-02-14 13:45 PDT
|
||
|
|
||
|
|
||
|
Objective
|
||
|
---------
|
||
|
|
||
|
The Google Serial Graphics Adapter BIOS or SGABIOS provides a means
|
||
|
for legacy pc software to communicate with an attached serial console
|
||
|
as if a vga card is attached.
|
||
|
|
||
|
Background
|
||
|
----------
|
||
|
|
||
|
The headless server problem
|
||
|
|
||
|
When building a lot of systems for data center use, it makes
|
||
|
no sense to install hardware that will rarely if ever be used.
|
||
|
Graphics adapters are not very useful even if they are installed
|
||
|
in a data center environment since often the person interested in
|
||
|
seeing the output is separated from the device by tens to thousands
|
||
|
of miles.
|
||
|
|
||
|
While it's possible to use remote management hardware that provides
|
||
|
a remotely accessible display and keyboard, this hardware is much
|
||
|
more expensive than the hardware that it replaces, and often this
|
||
|
hardware sends only images of the display rather than something
|
||
|
suitable for logging.
|
||
|
|
||
|
Since most systems already have a serial port, it's an obvious
|
||
|
target as a replacement for the primary display and keyboard.
|
||
|
The problem is that while an operating system like Linux can
|
||
|
support this arrangement, all of the output that would normally
|
||
|
appear on a graphics adapter before Linux boots is lost on modern
|
||
|
x86 hardware without modifications to the system firmware.
|
||
|
|
||
|
While some vendors provide firmware that enables the serial port to
|
||
|
be used as the primary display, this is usually a "premium" option
|
||
|
and isn't universally available for all x86 platforms. Often such
|
||
|
services aren't implemented in a way that is friendly to saving logs
|
||
|
of boot activity. One particularly ugly implementation might send
|
||
|
the same text hundreds of times as it tries to refresh the entire
|
||
|
display each timer tick. Others have ansi control sequences
|
||
|
between every single character output which, while readable in a
|
||
|
terminal, is almost unusable when referring to serial log files.
|
||
|
Behavior like this slows down the serial output by up to fifteen
|
||
|
times in some cases, using sometimes that many extra characters
|
||
|
of control sequences for each character output.
|
||
|
|
||
|
The need for detailed system logs
|
||
|
|
||
|
None of the vendor-supplied serial redirection implementations
|
||
|
include facilities for logging boot message for later capture by
|
||
|
an operating system. Being able to refer to the boot messages
|
||
|
after an operating system has loaded, or having a history of such
|
||
|
messages can be a useful debug, analysis, and management feature.
|
||
|
|
||
|
Even on systems with graphics adapters attached, once the display
|
||
|
is scrolled or refreshed with enough new text, the old messages
|
||
|
are only available in the user's own brain, which often isn't
|
||
|
very good at accurately recalling more than two or three items
|
||
|
that aren't grammatically meaningful in the user's native language.
|
||
|
|
||
|
Overview
|
||
|
---------
|
||
|
SGABIOS is designed to be inserted into a bios as an option rom
|
||
|
to provide over a serial port the display and input capabilites
|
||
|
normally handled by a VGA adapter and a keyboard, and additionally
|
||
|
provide hooks for logging displayed characters for later collection
|
||
|
after an operating system boots.
|
||
|
|
||
|
It is designed to handle all text mode output sent to the legacy
|
||
|
bios int 10h service routine. Int 10h is the most common method
|
||
|
for displaying characters in 16-bit legacy x86 code.
|
||
|
|
||
|
Occasionally some code may write directly to the vga memory in
|
||
|
the interest of "speed," and this output will be missed, but
|
||
|
it's rather uncommon for any code involved in booting a system
|
||
|
to be concerned with the speed of display output. SGABIOS is not
|
||
|
designed to handle these cases since those applications that make
|
||
|
such assumptions generally write to an area of memory that typically
|
||
|
already in use for system management mode and unusable outside of
|
||
|
that mode. Paging tricks could be used to capture such output,
|
||
|
but enabling paging requires protected mode to be enabled which
|
||
|
instantly breaks all segment loads in legacy 16-bit real- mode code
|
||
|
(which is the traditional boot environment).
|
||
|
|
||
|
Detailed Design
|
||
|
----------------
|
||
|
|
||
|
VGA BIOS int 10h is hooked and chained to any existing handler or
|
||
|
the default handler that the BIOS previously setup.
|
||
|
|
||
|
During initialization, the option rom also probes the serial port
|
||
|
for reply from an attached terminal. If the terminal replies to
|
||
|
a specific sequence, the terminal size is recorded and used for
|
||
|
all future display calculations. If a VGA card is attached at
|
||
|
the same time, the width of the terminal is limited to 80 columns
|
||
|
in order to have sensible output on both the VGA card and on the
|
||
|
serial console. If no reply comes from the serial terminal within
|
||
|
a very short timeout of about 8 milliseconds (or more accurately,
|
||
|
65536 reads of the serial status port), a default size of 80x24
|
||
|
is used. The detected size is displayed at the end of option rom
|
||
|
init to the serial console.
|
||
|
|
||
|
Because of the way the cursor is updated, if the cursor is never
|
||
|
moved upwards or more than one line down by int 10h calls, output
|
||
|
will still be appear completely appropriate for whatever sized
|
||
|
terminal is attached but failed to get detected.
|
||
|
|
||
|
Whenever int 10h is invoked, SGABIOS gets control first and decides
|
||
|
whether to act based on register state. With the exception of
|
||
|
functions for getting current mode info or the current cursor
|
||
|
position, whether it acts or not, register state is ultimately
|
||
|
restored to the state on entry and a far jump is made to the
|
||
|
original handler.
|
||
|
|
||
|
SGABIOS maintains two active cursor positions. One contains the
|
||
|
traditional VGA cursor position at the traditional location in
|
||
|
the BIOS Data Area, while the other maintains the position the
|
||
|
serial console's cursor is located. The serial cursor position
|
||
|
is located in a BDA location that traditionally contains the
|
||
|
base io port address for LPT3, but since builtin printer ports are
|
||
|
disappearing over time, this location is reused. These two values
|
||
|
will often differ since serial terminal output will always move
|
||
|
the cursor to the next position on the screen while many VGA
|
||
|
operations don't update the cursor position at all, or some only
|
||
|
at the start of the string, but leave the old value at the end.
|
||
|
Keeping track of two active cursor positions means that SGABIOS
|
||
|
can collapse a string of "set cursor" calls into perhaps a single
|
||
|
one or none if the serial console cursor already happens to be at
|
||
|
the target location. Cursor movements are further optimized
|
||
|
by sending newline characters to move the cursor down one row,
|
||
|
carriage return characters to move the cursor back to column 0,
|
||
|
and backspace characters to send the cursor back one or two spaces.
|
||
|
|
||
|
To avoid problems when a video card is connected, any Bios Data
|
||
|
Area location that would be updated by a VGA card is left alone
|
||
|
to be updated by the VGA card. SGABIOS will update the cursor
|
||
|
position as usual, but just before chaining to an existing vga
|
||
|
card's handler, it will restore the values to those on entry,
|
||
|
and for those functions that return data, it will defer completely
|
||
|
to the chained routines rather than taking those over as it does
|
||
|
when no video card is detected.
|
||
|
|
||
|
Cursor position updates to serial console are deferred until the
|
||
|
next character of terminal output is available. This collapses
|
||
|
the cases where the cursor is updated more than one time between
|
||
|
each character output (this is surprisingly common).
|
||
|
|
||
|
The goal of tracking the cursor so closely and minimizing the number
|
||
|
of characters required to update the cursor position is to both to
|
||
|
make the display of output as efficient and fast as possible and
|
||
|
to allow one to grep a raw log of serial console output for text
|
||
|
(which without such optimization may be impossible or extremely
|
||
|
difficult with movement escape sequences between every character).
|
||
|
|
||
|
In the same way cursor position is tracked, vga character attributes
|
||
|
are tracked so that it's possible to minimize the number of times
|
||
|
an attribute change escape sequence is sent to the serial console.
|
||
|
|
||
|
A BIOS Data Area location traditionally used for storing the
|
||
|
current palette value is used to store the last attribute sent to
|
||
|
the serial console. As SGABIOS processes new calls, if the value
|
||
|
is the same, after masking off bright background colors which
|
||
|
aren't supported in ansi escape codes, then no attribute update
|
||
|
is sent to the serial console, else an escape sequence is sent
|
||
|
that gives the new background and foreground colors and whether
|
||
|
the foreground is bold or not.
|
||
|
|
||
|
Data communication
|
||
|
|
||
|
Whenever the call is made to output text, SGABIOS first updates
|
||
|
the serial terminal cursor to match the current position of
|
||
|
the vga cursor (if necessary), outputs any attribute change if
|
||
|
applicable to the particular int 10h call made, and finally sends
|
||
|
the text character (or characters) out to the serial port, and then
|
||
|
updates its own view of where the serial console cursor is located.
|
||
|
After the text is sent, a logging routine is called to store that
|
||
|
text in a private area of memory allocated at option rom init.
|
||
|
|
||
|
For keyboard/terminal input, SGABIOS hooks bios int 16h which is
|
||
|
typically called to poll for a keypress. Before passing the call
|
||
|
along, SGABIOS looks for any pending input on the serial port and
|
||
|
stuffs the keyboard buffer with any pending byte after translating
|
||
|
it to a compatible keyboard scancode. If the character received
|
||
|
is an escape, SGABIOS will continue to poll for up to four extra
|
||
|
characters of input for several milliseconds in order to detect
|
||
|
ANSI/VT100/xterm/etc cursor keys and function keys, looking up
|
||
|
appropriate scancodes in a table of escape sequences for all
|
||
|
known non-conflicting terminal types.
|
||
|
|
||
|
SGABIOS also hooks the serial port interrupts, and on receiving
|
||
|
an interrupt blocks out interrupts, calls the same polling
|
||
|
routines as above, following the same processing of multi-byte
|
||
|
input as well, stuffing the keyboard buffer as appropriate,
|
||
|
and finally acknowledging the interrupt and returning from the
|
||
|
handler. [ serial port interrupts are now DISABLED ]
|
||
|
|
||
|
Optionally the serial port input/output can be replaced with
|
||
|
a SMI trigger that calls into an EFI BIOS in order to tie into
|
||
|
its own console input and output routines rather than directly
|
||
|
hitting the serial port. In this particular case it's assumed
|
||
|
that all logging is handled in the EFI module that will be called.
|
||
|
BIOS int 15h, ax = 0d042h is used to trigger SMI. The parameters
|
||
|
passed will need to be changed to be specific to the EFI or SMI
|
||
|
handler put in place. In the example in SMBIOS, for output,
|
||
|
ebx = 0xf00d0000 | (char << 8), and for input, ebx = 0xfeed0000,
|
||
|
with the character, if any, returned in the eax register with ZF
|
||
|
set and eax=0 if no character was available.
|
||
|
|
||
|
Summary of new enhancements
|
||
|
---------------------------
|
||
|
SGABIOS now keeps a log of the last 256 characters written to
|
||
|
the screen and where they were written in the event an application
|
||
|
like lilo asks for the current character under the cursor. These
|
||
|
are currently stored in a 1KB EBDA allocation which can be expanded
|
||
|
as needed. This method avoids having to store a 64KB buffer for
|
||
|
the largest possible serial terminal supported (255x255).
|
||
|
|
||
|
When lilo 22.6 is detected, SGABIOS now knows how to disable
|
||
|
lilo's serial output in favor of its own. This avoids having
|
||
|
double character output from both serial and VGABIOS interleaved.
|
||
|
|
||
|
Possible future enhancements
|
||
|
----------------------------
|
||
|
Previous future ideas have now been implemented.
|
||
|
|
||
|
Known Bugs
|
||
|
----------
|
||
|
With some versions of DOS, only the last character of every line
|
||
|
is displayed once dos boots since DOS will use direct access to
|
||
|
the VGA framebuffer until the end of line is reached, at which
|
||
|
point it will start using int 10h. Dual cursor tracking might
|
||
|
fix this issue by maintaining positions for dos that look like
|
||
|
the end of line and another for internal use to know where to
|
||
|
output next.
|
||
|
|
||
|
Caveats
|
||
|
-------
|
||
|
It may be possible for someone to construct a terminal reply for
|
||
|
the terminal sizing code that is completely invalid and attempts
|
||
|
to either setup variables to overrun buffers or else overruns
|
||
|
the input buffer itself. This situation is currently handled
|
||
|
by limiting the reply to between eight and fourteen characters
|
||
|
and ignoring any values outside the range from ten to two hundred
|
||
|
fifty-five for both the number of rows and the number of columns.
|
||
|
In these situations a default size of 80x24 is used (unless a
|
||
|
video card is present, in which case its size is used). If the
|
||
|
resize code detects several unexpected characters during the
|
||
|
terminal size detection, it currently assumes that someone has
|
||
|
left a loopback device plugged into the serial port and redirects
|
||
|
the serial input and output to the fourth serial port at 0x2e8.
|
||
|
|
||
|
|
||
|
Security considerations
|
||
|
-----------------------
|
||
|
None. This is already 16-bit real-mode x86 code. The entire
|
||
|
system may be crashed or bent to do anyone's bidding at any time
|
||
|
by any other running code outside of SGABIOS.
|
||
|
|
||
|
|
||
|
Opensource Plan
|
||
|
---------------
|
||
|
This source code was approved for release to the public for use under
|
||
|
the Apache License, Version 2.0 on http://code.google.com/p/sgabios
|
||
|
|
||
|
|
||
|
Document History
|
||
|
----------------
|
||
|
Date Author Description
|
||
|
2008-02-14 nil fix for release
|
||
|
2007-10-04 nil new features
|
||
|
2007-08-31 nil sga+vga fixes
|
||
|
2007-08-08 nil Initial version
|
||
|
|
||
|
$Id$
|