GLM 4.5 Air

UEFI Print Characters Fix: QEMU & Serial Output Guide

Fix UEFI printing issues showing non-printable characters in QEMU and serial output. Complete guide to resolve character encoding problems and proper console initialization.

Question

UEFI printing issue: Print statements showing as non-printable characters in QEMU and serial output

I’m developing my first EFI application with a simple wait for input loop, but I’m encountering an issue where all my print statements display as non-printable characters instead of the expected text. This problem occurs in both QEMU and serial output.

The function I’m using is:

c
uefi_call_wrapper(SystemTable->ConOut->OutputString, 2, SystemTable->ConOut, L"..")

The Print() function exhibits the same behavior.

I’ve confirmed that I’m compiling with the -fshort-wchar flag. My linking commands are:

ld -nostdlib -znocombreloc -T ${EFI_LIB_DIR}/elf_x86_64_efi.lds -shared -Bsymbolic -L ${EFI_LIB_DIR} ${EFI_LIB_DIR}/crt0-efi-x86_64.o $<TARGET_OBJECTS:efi_obj> -o efi.so -lefi -lgnuefi     
objcopy -j .text -j .sdata -j .data -j .dynamic -j .dynsym -j .rel -j .rela -j .reloc --target=efi-app-x86_64 efi.so bootx64.efi

What could be causing this printing issue in my UEFI application, and how can I resolve it?

GLM 4.5 Air

Brief Answer

The issue with non-printable characters in your UEFI print statements is likely related to string encoding or console initialization problems. Even with -fshort-wchar flag set, the problem may stem from incorrect string formatting, improper console setup, or mismatched character encodings between your code and the UEFI environment.

Contents

Common Causes of UEFI Printing Issues

The display of non-printable characters instead of expected text in UEFI applications typically stems from one of these primary issues:

  1. Character encoding mismatch: UEFI expects UTF-16 encoded strings, but your code might be generating UTF-8 or other encodings
  2. Improper string initialization: Missing null terminators or incorrect wide character string definitions
  3. Console not properly initialized: Attempting to print before the console output protocol is ready
  4. Linking issues: Missing or incorrectly ordered EFI libraries in your linking command

Important Note: Even though you’re using -fshort-wchar, which is correct for UEFI development, this doesn’t automatically solve all string-related problems. The flag only ensures that wchar_t is 2 bytes, but string handling still requires proper attention.

String Encoding and Format Solutions

Correct String Format

When working with UEFI, all string literals must be properly defined as wide character strings:

c
// Correct way to define wide character strings in UEFI
uefi_call_wrapper(SystemTable->ConOut->OutputString, 2, SystemTable->ConOut, L"Hello, UEFI World!");

// Instead of:
// uefi_call_wrapper(SystemTable->ConOut->OutputString, 2, SystemTable->ConOut, "Hello"); // Wrong

String Termination

Ensure all strings are properly null-terminated:

c
// Good practice: explicitly null-terminate your strings
CHAR16 greeting[] = L"Hello, UEFI!\0";
uefi_call_wrapper(SystemTable->ConOut->OutputString, 2, SystemTable->ConOut, greeting);

Character Selection

Start with simple ASCII characters before moving to Unicode:

c
// Begin with simple ASCII characters
uefi_call_wrapper(SystemTable->ConOut->OutputString, 2, SystemTable->ConOut, L"System Ready");

// Avoid complex Unicode characters initially

Console Initialization and Configuration

Proper Console Setup

Before attempting to print, ensure your console is properly initialized:

c
// Initialize console output
InitializeLib(SystemTable, ImageHandle);

// Reset console to clean state
uefi_call_wrapper(SystemTable->ConOut->Reset, 2, SystemTable->ConOut, TRUE);

Console Mode Configuration

Set the console mode to ensure proper text display:

c
// Check and set console mode
UINT64 Mode;
uefi_call_wrapper(SystemTable->ConOut->QueryMode, 4, SystemTable->ConOut, 
                  SystemTable->ConOut->Mode->Mode, &Mode, NULL);

// Set to standard text mode if needed
uefi_call_wrapper(SystemTable->ConOut->SetMode, 2, SystemTable->ConOut, 1);

QEMU and Serial Output Specific Fixes

QEMU Configuration

When running your UEFI application in QEMU, ensure you’re using the correct parameters to display output:

bash
# Command to run QEMU with UEFI output visible
qemu-system-x86_64 -bios OVMF.fd -serial stdio -monitor none -display none

# Or for graphical output
qemu-system-x86_64 -bios OVMF.fd -serial stdio

Serial Output Configuration

If using serial output specifically, configure your terminal properly:

bash
# For Linux/macOS
stty -F /dev/ttyS0 115200 cs8 -cstopb -parenb

# For Windows, use a terminal program like PuTTY or Tera Term
# Configure for 115200 baud, 8 data bits, no parity, 1 stop bit

Alternative Printing Methods

Using the EFI_PRINT Macro

Many UEFI development environments provide a PRINT macro that handles string encoding automatically:

c
// If available in your development environment
PRINT(L"Hello, UEFI World!\n");

Direct Simple Text Output Protocol Usage

Try using the protocol directly instead of through the wrapper:

c
EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut = SystemTable->ConOut;
ConOut->OutputString(ConOut, L"Direct output test\n");

Custom Print Function

Create a simple wrapper function that handles string formatting:

c
VOID Print(IN EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL *ConOut, IN CHAR16 *String) {
    uefi_call_wrapper(ConOut->OutputString, 2, ConOut, String);
}

// Usage
Print(SystemTable->ConOut, L"Custom print function works!\n");

Complete Working Example

Here’s a minimal UEFI application with proper printing setup:

c
#include <efi.h>
#include <efilib.h>

EFI_STATUS EFIAPI UefiMain(IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable) {
    // Initialize the library
    InitializeLib(ImageHandle, SystemTable);
    
    // Reset console
    uefi_call_wrapper(SystemTable->ConOut->Reset, 2, SystemTable->ConOut, TRUE);
    
    // Test with simple ASCII string
    Print(L"UEFI Application Starting...\n");
    
    // Test with more complex output
    CHAR16 message[] = L"System initialized successfully!\0";
    uefi_call_wrapper(SystemTable->ConOut->OutputString, 2, SystemTable->ConOut, message);
    
    // Simple wait loop
    while(1) {
        gBS->Stall(1000000); // 1 second delay
    }
    
    return EFI_SUCCESS;
}

Compilation and Linking Commands

Ensure your compilation and linking process includes these flags:

bash
# Compilation
gcc -fshort-wchar -fno-stack-protector -fno-stack-check -mno-red-zone -m64 -c your_code.c -o your_code.o

# Linking
ld -nostdlib -znocombreloc -T ${EFI_LIB_DIR}/elf_x86_64_efi.lds -shared -Bsymbolic -L ${EFI_LIB_DIR} \
   ${EFI_LIB_DIR}/crt0-efi-x86_64.o your_code.o -o your_code.so -lefi -lgnuefi

# Object copy
objcopy -j .text -j .sdata -j .data -j .dynamic -j .dynsym -j .rel -j .rela -j .reloc \
   --target=efi-app-x86_64 your_code.so bootx64.efi

Conclusion

To resolve your UEFI printing issue, focus on these key points:

  1. Verify all string literals are properly defined with the L prefix for wide characters
  2. Ensure your console is properly initialized before attempting to print
  3. Start with simple ASCII characters to validate basic functionality
  4. Check your QEMU configuration to ensure output is properly displayed
  5. Consider using alternative printing methods if the standard approach continues to fail

The issue is most likely related to string encoding or console initialization rather than a fundamental problem with your approach. With these adjustments, your print statements should display correctly in both QEMU and serial output.