ADR: SOL Terminal Emulation & OEM Capabilities Encoding Support - device-management-toolkit/deployment GitHub Wiki
Proposed
Serial-over-LAN (SOL) redirection on Intel vPro and ISM platforms transmits console text and terminal control sequences between the remote target's firmware/operating system and the management console. Currently, ui-toolkit acts as the intermediary processor between the redirection WebSocket stream and the terminal display component (xterm.js), which is consumed by frontend component libraries (ui-toolkit-angular, ui-toolkit-react) and applications (sample-web-ui, console).
As tracked in ui-toolkit Issue #1855 and ui-toolkit Issue #1768, character corruption occurs under two primary operational scenarios:
-
Chunk-Boundary Multi-Byte Fragmentation: Network packets and WebSocket frames fragment multi-byte UTF-8 sequences (2 to 4 bytes). If an incoming frame cuts off mid-character, standard stateless decoders treat trailing bytes as illegal sequences, emitting
\uFFFDreplacement symbols. -
Terminal Emulation and Character Set Mismatches: Legacy BIOS POST and configuration setup utilities output single-byte extended ASCII (Code Page 437) to render menus, dialog boundaries, and drop shadows (e.g.,
0xDA,0xC4,0xBF). When parsed as UTF-8, these single bytes violate UTF-8 leading-byte rules, corrupting screen layout (Console Issue #530).
The Intel CSME/AMT Redirection specification establishes that the host communicates its expected terminal format through the 2nd Byte of OEM Capabilities (Terminal Emulation):
-
Bit 0:3 β Emulation Profile:
-
1β VT52 (Standard monochrome, 2-byte escape grammar) -
2β VT100+ (ANSI/VT100 baseline with function keys F5βF14; Note: Deprecated and unsupported in Intel CSME 14.0 and later) -
3β VT-UTF8 (Standard streaming UTF-8 Unicode stream with OEM function key F8 mapping) -
4β PC-ANSI (IBM PC-compatible ANSI sequence set with Code Page 437 box-drawing glyphs) -
5β15β Reserved for future architecture definitions
-
Console operators must have access to accurate default rendering negotiated from hardware capability bits, paired with runtime UI controls to override the terminal mode when legacy firmware communicates invalid or incomplete capability descriptors.
- Spike Objectives (Issue #1855): Define an end-to-end decoding and sequence translation flow for Intel vPro and ISM devices that fixes multi-byte chunk corruption while preserving legacy BIOS display rendering.
-
Preserve Component Boundaries: Keep decoding logic inside
TerminalDataProcessorinui-toolkitso downstream consumers (ui-toolkit-angular,ui-toolkit-react,sample-web-ui) remain clean wrappers. - Avoid Screen Distortion in Legacy BIOS: Ensure DOS-style box-drawing characters in BIOS setup menus render correctly as crisp Unicode lines instead of broken question marks or replacement symbols.
- Handle Network Chunks Safely: Ensure trailing bytes cut across network packets do not crash the decoder or leak memory.
- Operator Flexibility: Allow users to manually change the emulation mode in the UI if the device reports incorrect capabilities.
TerminalDataProcessor in ui-toolkit will manage both a stateful byte decoder and an emulation profile based on the active terminal mode:
-
VT-UTF8 Mode (OEM Bit 0:3 = 3):
-
Why it is needed: UTF-8 characters can take between 1 and 4 bytes. In network streaming, a packet or WebSocket chunk can end right in the middle of a multi-byte character (for example, receiving byte 1 of a 2-byte character in Chunk
$A$ , and byte 2 in Chunk$B$ ). A standard decoder likeBuffer.toString('utf-8')fails on Chunk$A$ because the trailing byte looks like an invalid character, producing replacement symbols like (\uFFFD). -
How it works: Employs a stateful stream decoder using native Web APIs:
Setting
this.decoder = new TextDecoder('utf-8', { fatal: false }); const text = this.decoder.decode(incomingChunk, { stream: true });
{ stream: true }instructsTextDecoderto hold any incomplete 1- to 3-byte sequence in memory at the end of the chunk instead of erroring out. When the subsequent chunk arrives,TextDecoderautomatically prepends those held bytes to complete the character cleanly. -
Sequence behavior: Standard ANSI/ECMA-48 escape codes pass directly to
xterm.js. For keyboard input, function key F8 is mapped to the OEM sequence expected by pre-boot menus (such as<ESC>Oq).
-
Why it is needed: UTF-8 characters can take between 1 and 4 bytes. In network streaming, a packet or WebSocket chunk can end right in the middle of a multi-byte character (for example, receiving byte 1 of a 2-byte character in Chunk
-
PC-ANSI / CP437 Mode (OEM Bit 0:3 = 4):
-
Why it is needed: Legacy BIOS configuration utilities (such as Aptio, Phoenix, or Insyde BIOS setup screens) do not use multi-byte UTF-8. Instead, they use IBM Code Page 437 (DOS character set), where single bytes between
0x80and0xFFrepresent box borders, lines, corners, and blocks. If the decoder treats these bytes as UTF-8, they violate UTF-8 encoding rules and fail to display, collapsing menus and dialog borders (Console Issue #530). -
How it works: Instead of UTF-8 decoding, incoming bytes are processed byte-by-byte:
- Standard ASCII bytes (
0x00β0x7F): Converted to standard characters. - High-ASCII bytes (
0x80β0xFF): Mapped directly to their equivalent Unicode box-drawing characters using a static lookup table before sending the string toxterm.js:-
0xDA$\rightarrow$ β(Top-left corner,\u250C) -
0xBF$\rightarrow$ β(Top-right corner,\u2510) -
0xC0$\rightarrow$ β(Bottom-left corner,\u2514) -
0xD9$\rightarrow$ β(Bottom-right corner,\u2518) -
0xC4$\rightarrow$ β(Horizontal border,\u2500) -
0xB3$\rightarrow$ β(Vertical border,\u2502) -
0xC5$\rightarrow$ βΌ(Cross intersection,\u253C) -
0xCD$\rightarrow$ β(Double horizontal border,\u2550) -
0xBA$\rightarrow$ β(Double vertical border,\u2551) -
0xDB$\rightarrow$ β(Full solid block,\u2588)
-
- Standard ASCII bytes (
-
Sequence behavior: PC-ANSI color attributes (
<ESC>[3Xm,<ESC>[4Xm) and standard cursor movements are already recognized byxterm.jsand pass through without modification.
-
Why it is needed: Legacy BIOS configuration utilities (such as Aptio, Phoenix, or Insyde BIOS setup screens) do not use multi-byte UTF-8. Instead, they use IBM Code Page 437 (DOS character set), where single bytes between
-
VT52 Mode (OEM Bit 0:3 = 1):
-
Why it is needed: VT52 is a vintage terminal standard that predates modern ANSI (
xterm.js). It uses compact 2-byte escape codes (like<ESC>Ainstead of ANSI<ESC>[A) and direct character-offset cursor coordinates (<ESC>Y<row><col>). Becausexterm.jsonly understands ANSI/ECMA-48 escape sequences, raw VT52 streams result in unhandled text and failed cursor jumps. -
How it works: Bytes are decoded as standard 7-bit ASCII (
byte & 0x7F).TerminalDataProcessorruns a lightweight translator that intercepts VT52 commands and converts them into the ANSI equivalents thatxterm.jsexpects:-
<ESC>A(Cursor Up)$\rightarrow$ <ESC>[A -
<ESC>B(Cursor Down)$\rightarrow$ <ESC>[B -
<ESC>C(Cursor Right)$\rightarrow$ <ESC>[C -
<ESC>D(Cursor Left)$\rightarrow$ <ESC>[D -
<ESC>H(Cursor Home)$\rightarrow$ <ESC>[H -
<ESC>I(Reverse Line Feed)$\rightarrow$ <ESC>M -
<ESC>J(Clear to End of Screen)$\rightarrow$ <ESC>[J -
<ESC>K(Clear to End of Line)$\rightarrow$ <ESC>[K -
<ESC>Y<row><col>(Direct Cursor Jump)$\rightarrow$ <ESC>[{row - 31};{col - 31}H
-
-
Keyboard input: Translates local arrow keys back into VT52 sequences (
<ESC>Aβ<ESC>D) when sent upstream to the host.
-
Why it is needed: VT52 is a vintage terminal standard that predates modern ANSI (
-
VT100+ Mode (OEM Bit 0:3 = 2):
- Why it is needed: Provided for backward compatibility on older firmware (Intel CSME < 14.0). It behaves identically to standard VT100/ANSI with extended function keys F5βF14.
-
Behavior: On modern platforms (Intel CSME
$\ge 14.0$ ), this mode is deprecated by firmware. If encountered,TerminalDataProcessorlogs a deprecation warning and operates with standard ANSI handling.
The UI architecture provides automatic capability negotiation alongside real-time manual operator controls across consumer applications (sample-web-ui, console):
-
Capability Handshake & Auto-Negotiation:
- Backend Transparency (MPS / Console): MPS operates as a transparent TCP/WebSocket proxy and does not inspect redirection payload bytes. The OEM Capabilities structure is already transmitted over the wire by the AMT/CSME firmware today; no backend additions or API modifications are required on MPS.
-
Client Extraction in
ui-toolkit: During the Intel AMT Redirection handshake (receivingStart Channel Reply0x21for SOL),AMTRedirectorinspects the 2nd Byte of OEM Capabilities. -
Bitmask Parsing: The lower 4 bits are isolated via bitmasking:
const oemTerminalCode = oemCapabilitiesByte & 0x0F;
-
Initial Mapping:
-
0x01$\rightarrow$ InitializesSolTerminalType.VT52 -
0x02$\rightarrow$ InitializesSolTerminalType.VT100Plus(if firmware CSME < 14.0) -
0x03$\rightarrow$ InitializesSolTerminalType.VTUTF8(Modern default) -
0x04$\rightarrow$ InitializesSolTerminalType.PCANSI(BIOS default)
-
-
Fallback Heuristic: If Bit 0:3 is
0(unspecified) or$\ge 5$ (reserved for future definitions), the client defaults safely toSolTerminalType.VTUTF8and logs a debug notice.
-
UI Selector & Toolbar Presentation:
-
Placement: Exposed as a compact
<select>or dropdown component in the SOL header/action bar directly adjacent to terminal power controls and connection status indicators. -
Display Items & Hints:
-
VT-UTF8β "Modern Linux/Windows, UEFI Shell (Default)" -
PC-ANSIβ "Legacy BIOS Setup Menus (CP437 Box-Drawing)" -
VT52β "Basic Monochrome Serial" -
VT100+β "Legacy only (< CSME 14)" (Visually flagged or disabled when connected to CSME$\ge 14.0$ )
-
- Negotiation Indicator: Displays an "Auto" badge when the active selection matches the hardware-negotiated OEM capability, switching to "Manual Override" if an operator changes the dropdown.
-
Placement: Exposed as a compact
-
Runtime Non-Destructive Switching:
-
Component Flow: Dropdown change event
$\rightarrow$ UI wrapper binding ((terminalTypeChange)in Angular /onTerminalTypeChangein React)$\rightarrow$ callsterminalDataProcessor.setTerminalType(selectedType). - Zero Socket Disruption: The underlying WebSocket and TCP redirection session are not disconnected, restarted, or interrupted.
-
State Buffer Flush: Calling
setTerminalType()immediately triggers internal cleanup (this.reset()):- Empties any pending 1- to 3-byte partial UTF-8 cache to prevent stale byte contamination.
- Re-binds the active parser without wiping the rendered screen buffer in
xterm.js.
- Upstream Keyboard Switching: The keyboard input handler instantaneously updates its keymap (e.g., swapping VT52 2-byte arrow keys or VT-UTF8 OEM function key mappings) for all subsequent keystrokes sent to the remote host.
-
Component Flow: Dropdown change event
To establish a clear contract across the toolkit and consumer layers, terminal modes are declared via an explicit enum matching the OEM capability values:
export enum SolTerminalType {
VT52 = 1,
VT100Plus = 2, // Deprecated in Intel CSME >= 14.0
VTUTF8 = 3,
PCANSI = 4
}To prevent stale byte bleed, memory leaks, or corrupted decoding states across stream lifecycles:
-
Hard 3-Byte Buffer Ceiling: Because valid UTF-8 sequences are at most 4 bytes long, incomplete fragments stored across chunks can never exceed 3 bytes. If a buffer reaches 4 bytes without resolving a valid sequence, the processor flushes
\uFFFD, discards the invalid sequence, and resets the cache. -
Deterministic Session Reset (
reset()):TerminalDataProcessorexposes an explicit.reset()method invoked whenever the SOL WebSocket disconnects, reconnects, or when the operator switches the terminal emulation mode in the UI.
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Consumer UI / Sample Web UI β
β [ Emulation Selector: VT-UTF8 | PC-ANSI | VT52 ] β
ββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββ
β (Input / User Selection)
ββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββ
β ui-toolkit-angular / ui-toolkit-react Wrappers β
ββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββ
β setTerminalType(type)
ββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββ
β ui-toolkit Core β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β TerminalDataProcessor β β
β β β β
β β Incoming Chunks βββΊ [ Stateful Stream Buffer ] β β
β β β β β
β β Active Emulation Mode β β
β β βββββββββββββββββββββΌββββββββββββββββ β β
β β βΌ βΌ βΌ β β
β β [ VT-UTF8 ] [ PC-ANSI ] [ VT52 ]ββ
β β TextDecoder Stream CP437 Table Map ANSI Transββ
β ββββββββββββββ¬ββββββββββββββββββββ¬ββββββββββββββββ¬ββ β
βββββββββββββββββΌββββββββββββββββββββΌββββββββββββββββΌβββββ
βΌ βΌ βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββ
β xterm.js Terminal Display β
βββββββββββββββββββββββββββββββββββββββββββββββββββββ
- Resolves Issue #1855: Stateful streaming prevents multi-byte character corruption across split network packets.
- Restores BIOS Setup Screen Fidelity: Direct CP437 mapping ensures legacy setup menus, borders, and dialogs render as clean box lines instead of corrupt symbols (Console Issue #530).
- Standard Compliant: Adheres directly to Intel AMT/CSME OEM Capabilities specifications.
- Operator Flexibility: If firmware negotiates an incorrect terminal mode, the operator can switch modes instantly via the UI dropdown.
-
CP437 Lookup Table: Requires embedding a static 128-entry Unicode character lookup array in
ui-toolkit. -
Multi-Package Release Dependency: Changes must be released in order:
ui-toolkit$\rightarrow$ ui-toolkit-angular/ui-toolkit-react$\rightarrow$ sample-web-ui.
| Step | Repository | Scope of Work |
|---|---|---|
| Step 1 | ui-toolkit |
Add SolTerminalType enum, implement TerminalDataProcessor modes (VT-UTF8 streaming, CP437 lookup map, VT52 sequence translation), add buffer guardrails and .reset(). |
| Step 2 | ui-toolkit-angular |
Update ui-toolkit dependency; expose @Input() terminalType and change emitter on the SOL component. |
| Step 3 | ui-toolkit-react |
Update ui-toolkit dependency; expose terminalType prop and change handlers. |
| Step 4 |
sample-web-ui & console
|
Update toolkit dependencies; add terminal selection dropdown in the SOL header/toolbar; bind selection to the terminal session. |