Printing over serial on x86

Tonight I set myself the task of printing some text over a serial port in a tiny kernel mode program that boots with the BOOTBOOT bootloader. I didn’t start from scratch, but from some example code in the BOOTBOOT reference implementation.

The only reason it wasn’t trivial is that I’m targeting a UEFI machine, and between the BOOTBOOT spec and the osdev wiki page about serial ports or UEFI, it’s not clear whether the “old fashioned” way of interacting with serial ports is supported on UEFI. All the information I could find about how to print via serial console on UEFI systems implied that I should use a library which abstracts access to hardware via UEFI.

Instead I elected to try doing it the “old fashioned” way, based on the debug printing code from the seL4 microkernel. Here’s how it looks.

I added a small assembly file defining a com1_putc function that sends its argument to I/O port 0x3F8, which conventionally addresses the COM1 serial port.

; ioports.asm

global com1_putc

%define COM1 0x3F8

section .text
bits 64

com1_putc:
    mov rax, rdi
    mov dx, COM1
    out dx, al
    ret

Assemble the new file:

# Makefile

mykernel.x86_64.elf: kernel.c
	nasm -f elf64 ioports.asm -o ioports.o
	... (add ioports.o to the linker arguments)

And call the new function from _start:

// kernel.c

void com1_putc(char c);
void com1_puts(char* s);

void _start()
{
    com1_puts("Hello, COM1\n");
}

void com1_puts(char* s) {
    do {
        com1_putc(*(s++));
    } while (*s);
}

Despite its simplicity, I’m not convinced that this code is correct in all cases. I rushed this because I just wanted to get something working, and now that it works for me, I’ll slow down and understand this code as deeply as I can before moving on.