All Projects → pbatard → ubrx

pbatard / ubrx

Licence: GPL-3.0 license
Universal BIOS Recovery console for x86 PCs

Programming Languages

assembly
5116 projects
Makefile
30231 projects
*******************************************************************************
*                                  UBRX README                                *
*******************************************************************************

UBRX is a Universal Bootblock Recovery serial console utility, targeted at x86
platform BIOSes, such as the one found on most AMD or Intel based PCs.

It aims at providing a universal way of performing system recovery operations
such as flashing a BIOS or running a low level debugger, for x86 systems,
through the provision of a serial console. This mode of operation is also
called "panic room" mode.

The main goal of UBRX is to provide a base that is hardware agnostic, meaning
for instance that the same UBRX binary can provide a recovery console regardless
of the CPU or chipset being used. In practice, this means that you should be
able to pick an UBRX BIOS from one machine, put it in another, and be able to
enter the UBRX console to reflash the BIOS payload without having to change
anything, even if the CPUs, chipsets and Super I/O of the 2 computers are
entirely different.

The advantage we see in such a feature, besides the advantage of having a single
codebase, is that it can help with coreboot development for platforms that are
not yet supported by the project, through the provision of a recovery and
development console that should work out of the box, and where a development
BIOS can easily be tested.
another advantage has of course to do with in-situ BIOS recovery for any system
relying on an UBRX enabled BIOS.


Current Status
--------------

UBRX is currently alpha software.

At this stage to be fully useful, UBRX is missing parts such as CAR (Cache As
RAM) script initialization examples as as well as Y-modem upload.


Limitations
-----------

Because of the diversity and history of the x86 PC platform, it should be fairly
obvious that there exists some restrictions with regards to our "universal"
qualifier. They are currently as follow:

* The motherboard must have a physical serial port, provided by a Super I/O chip.
  Console over USB-RS232 adapters or legacy-free hardware is not supported.
* Only AMD and Intel x86 CPUs are supported for the time being.
* The CPU must support the MMX instruction set (Intel Pentium MMX or
  later, AMD K6 or later). This is because we use MMX for stack operations.
* Only Super I/O chipsets conforming to the ISA PnP specifications, and
  starting in PnP mode on reset are supported (except if VMware compatible)
* Some non widespread PnP Super I/O chips with complex config mode (eg. IT8671)
  have voluntarily been dropped.
* Powering up of Super I/O Logical Devices is not currently conducted. We assert
  that UARTs are powered up by default (but may review that assertion).
* With regards to Super I/O chips that require extra configuration from the
  Southbridge for LPC bus access, only Intel ICH# (any version that requires it)
  and AMD SB6x0-SB9x0 are supported.

The limitations above are unlikely to be much of an impact, unless you have a
an old system. Moreover, these limitations are mostly a result of a design
choice (or lack of datasheets in the case of nVidia chipset support), and can
be addressed in future versions of UBRX.


Build Instructions
------------------

To build UBRX you need a GNU C compiler toolchain that can produce x86 code:
 * On x86 Linux (32 or 64 bit), the standard toolchain should do. Cross
   compilation should also work for non x86 architectures.
 * On Windows, either of cygwin, MinGW32 or MinGW-w64 will do.
 * On OSX, you will need to install as proper GNU toolchain as XCode cannot be
   used.

To compile, simply invoke:

  make

The above will build a 512 KB BIOS ROM that only contains the UBRX bootblock.
To build a BIOS ROM of a different size, you can either invoke 'make' with one
of the 128k, 256k, 512k, 1m or 2m options. Or you can define a ROM_SIZE
environment variable to one of these parameters.
The size of a bootblock, which currently defaults to 8K, can also be set
through a BB_SIZE environment variable or by editing the Makefile directly.

The generated file is called 'bios.rom', which you can then flash directly
onto a BIOS chip. In case this needs to be pointed out, remember that this
BIOS does not provide anything, not even an actually functional console, so you
MUST have means to restore your BIOS, with an external programmer, before you
attempt to flash an UBRX one.

Please consult the Makefile to find out how you can also invoke the building
and flashing of target specific UBRX BIOS ROMs.
Build options can be modified in "config.inc".


Testing
-------

Once UBRX has been flashed onto your BIOS chip, you should connect one of its
serial port to another computer with a Null Modem cable, open a console (115200
bauds, 8N1, XON/XOFF a.k.a. "Software" handshaking) and then power up the 
machine with the UBRX BIOS while pressing the Space Bar in the console window.
If detection of the serial port was successful, you should end up with a prompt
where recovery commands are available.

Since UBRX can easily be tested on VMware, we encourage first running a test
there, before trying on real hardware. For more information on VMware testing
you can check http://pete.akeo.ie/2011/06/crafting-bios-from-scratch.html.


Usage
-----

Once in console mode, the typical usage scenario is as follows:

1. User enters script mode ('s') to set up XIP (Execute in Place) on the ROM
   flash. See the ####_xip.txt for an example of such a script. This is done
   by mapping the UBRX BIOS region (FE000-FFFFF for an 8 KB bootblock) as 
   WriteBack cache in the MTRRs, enabling caching, and then triggering XIP
   with a far ret instruction (which is done transparently with '%')
   The reason one wants to have XIP early is that, even with a very aggressive
   XON/XOFF policy and a 16 byte 16550 buffer, uncached code running from a slow
   flash chip will result in serial characters being dropped, even at lower
   baudrates, which wrecks havoc on the ability to copy/paste scripts. Serial
   transfers are also much *slower* without XIP.

2. User continues cache initialization by enabling WriteBack caching for
   L2-Unified as well as L1-Data, respectively for executables and stack. 
   The reason L2 cache needs to be enabled is that the L1-Instruction cache is
   separate from L1-Data and non writable, therefore any code that is uploaded
   must reside in L2-Unified (which serves for both instruction and data).
   Once L1 and L2 cache are initialized (which would tipically include a memory
   fill instruction such as '<' or '>' so that subsequent attempts to read/write
   to RAM are not carried out), the user provides the base address for uploads,
   in EAX and the bade address for the stack in EBX.
   
3. With cache and stack setup, the user can now invoke the Y-modem upload 
   command ('u') which transfers a binary executable into the L2-Unified cache
   for execution. Note that the Y-modem upload requires a working stack, and of
   course a working base address to upload to.

4. Once the executable has been transferred, simply invoke the 'r' command to
   run it. The uploaded executable can of course make use of the stack and should
   end with a far ret instruction to return to the UBRX console. Be mindful that
   the program starts in real address rather than protected mode.

Both steps #1 and #2 are CPU specific, and therefore must be carried out in
accordance with the CPU specifications with regards to MTRR access, total L1 and
L2 cache available, etc.

For more details on the available UBRX commands, see the USAGE text file.


Known issues
------------

* As long as you have not enabled XIP (see above), you are likely to lose 
  characters when copy/pasting scripts. This is due to uncached flash ROM being
  very slow to access, and is resolved as soon as XIP is setup.
* When using com0com with VMWare, the baudrate you select doesn't seem to apply
  and 115200 bauds is being used regardless. 

Detection primer
----------------

The paragraphs below highlight the generic Super I/O and 16550 UART detection
process as performed by UBRX. We believe that this process is safe to be
executed at every boot, irrespective of the hardware and without side effects.

The 2 main components UBRX needs to detect are:
1. a potential PnP Super I/O candidate.
2. a potential 16650 UART Logical Device (LD) on the Super I/O chip.

As we start with absolutely no knowledge of the hardware, and must avoid writing
data at random, since doing so can damage the hardware, our detection process
has been designed with safety in mind from the ground up, by ensuring that write
operations were limited to the bare minimum, and only executed after we had some
assurance that the destination for the write would match the expectation.
Below is a detailed description of how we ensure that our generic detection
process is as innocuous to the hardware as possible.

1. The Super I/O chip is accessed through the LPC bus, which is not always
   accessible after reset, so we may have to enable LPC/SuperIO access first.
   Currently we only support Intel ICH# (all versions) and AMD SB6x0-9x0.
   The detection and initialisation of the chipset for LPC access is safe, since
   it is PCI based, and the PCI VID:PID of the South Bridge can be read
   beforehand to unconditionally identify a supported chip before we proceed with
   LPC initiation.
   You will notice that we use a blanket LPC initialization, as we don't
   distinguish between versions of the SouthBridge (eg: ICH6 is initialized the
   same as ICH9), but this is the result of a *thorough* review of all the Intel
   ICH# and AMD SBxx0 datasheets, to confirm that LPC init could indeed be
   factorized. Even for the chips that don't require LPC initialization(such as
   Intel ICH5 or earlier) and for which we do send the LPC initialization command,
   we have confirmed from the datasheet that we can simply let the PCI
   transaction fail as no register conflict in the destination config space.
   As such our LPC bus access initialization is deemed safe.

2. With the LPC bus accessible, we must probe a few common Super I/O ports.
   Currently, these are 0x2E, 0x4E, 0x370, 0x3f0, as well as their +1 data port.
   The last two I/O ports are commonly assigned to Tape and FDC so we expect any
   chip there to withstand unintended writes (plus these can be disabled through
   the bios.S build options). But even then, the extra checks we apply to the
   0x2E and 0x4E ports ensure safe access. With regards to 0x2E and 0x4E, these
   are more problematic as a non PnP Super I/Os are expected to reside there on
   older machines, and unchecked write access (such as trying to configure PnP
   access on a non PnP aware chip) could very well have unintended consequences.
   To alleviate this problem we:
   a) always keep a copy of the original value at base and base+1
   b) perform PnP enter conf (write to base only) and attempt to read the Super
      I/O ID (at base +1). If the id is either 0x00 of 0xff, we consider that
      the address is not one of a PnP Super I/O chip and restore the base data
      => only the base register will have been accessed, then restored.
   c) attempt to write 8 LDN values, read them back and check that at least 2 of
      them stick, indicating that the potential PnP Super I/O chip has at least
      2 LDs. If this isn't the case, we also restore the content from base+1 and
      declare the PnP access to have failed. At most, since we are only writing
      LDNs, this modifies the 3 lowest bits of base +1.
   Considering that both superiotool and sensors-detect have let user perform
   similar Super I/O probing (without the extra restore step), and we are not
   aware of problems, as well as the fact that any recent PC from our targeted
   audience would have a Super I/O running in PnP mode at either 0x2e or 0x4e,
   we consider this approach safe to be executed at every boot.

3. Even with a possible PnP Super I/O chip accessible (and with the current POC
   assertion that the UART LD we want to access is powered up by default on
   reset), we do not have any knowledge of the LDN of the potential UART. With
   other LDNs being set up for GPIO or hardware monitoring or control, trying
   to access each LDN as an UART, without exerting any form of caution, is not
   a viable option. To alleviate this issue, we perform an extensive yet non
   intrusive detection of a 16550 UART LD by first making sure, through read-
   only accesses, that the registers match the reset value of a 16550 compliant
   unit. Then, we try to flip the furthest 'safe' bit in the I/O range
   (register 7, bit 6, with backup), to eliminate any LD that has less than 7
   registers. Then we check a significant unflippable bit from the 16550
   register range (which we also restore in case of failure), and finish our
   testing with a complete UART loopback test. In all, we perform no less than
   16 tests to confirm that an LD is indeed a 16550 UART, with more than 30
   bits being tested in read-only mode, before we even start trying to flip a
   single bit. As such, we seem this form of detection both safe and conclusive.
   At the moment, we test up to 32 LDNs per potential PnP Super I/O for UART
   access. The 16550 tests are heavily documented in bios.S/check_16550.

Only once we have successfully identified a 16650 UART do we attempt to fully
configure it and read the console request key. If multiple UARTs have been
identified, these will be checked in sequence, meaning that any serial port
available on the motherboard can be used for console access.
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].