Skip to content
Permalink
Browse files
x86/boot: Allow to hook up alternative port I/O helpers
Port I/O instructions trigger #VE in the TDX environment. In response to
the exception, kernel emulates these instructions using hypercalls.

But during early boot, on the decompression stage, it is cumbersome to
deal with #VE. It is cleaner to go to hypercalls directly, bypassing #VE
handling.

Add a way to hook up alternative port I/O helpers in the boot stub.
All port I/O operations are routed via 'pio_ops'. By default 'pio_ops'
initialized with native port I/O implementations.

This is a preparation patch. The next patch will override 'pio_ops' if
the kernel booted in the TDX environment.

Signed-off-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
  • Loading branch information
kiryl committed Jan 20, 2022
1 parent 1233e81 commit d8bd4e38f74301c8e7c103e11fa302acbb97518c
Show file tree
Hide file tree
Showing 12 changed files with 93 additions and 47 deletions.
@@ -25,7 +25,7 @@ static int empty_8042(void)
while (loops--) {
io_delay();

status = inb(0x64);
status = pio_ops.inb(0x64);
if (status == 0xff) {
/* FF is a plausible, but very unlikely status */
if (!--ffs)
@@ -34,7 +34,7 @@ static int empty_8042(void)
if (status & 1) {
/* Read and discard input data */
io_delay();
(void)inb(0x60);
(void)pio_ops.inb(0x60);
} else if (!(status & 2)) {
/* Buffers empty, finished! */
return 0;
@@ -99,24 +99,24 @@ static void enable_a20_kbc(void)
{
empty_8042();

outb(0xd1, 0x64); /* Command write */
pio_ops.outb(0xd1, 0x64); /* Command write */
empty_8042();

outb(0xdf, 0x60); /* A20 on */
pio_ops.outb(0xdf, 0x60); /* A20 on */
empty_8042();

outb(0xff, 0x64); /* Null command, but UHCI wants it */
pio_ops.outb(0xff, 0x64); /* Null command, but UHCI wants it */
empty_8042();
}

static void enable_a20_fast(void)
{
u8 port_a;

port_a = inb(0x92); /* Configuration port A */
port_a = pio_ops.inb(0x92); /* Configuration port A */
port_a |= 0x02; /* Enable A20 */
port_a &= ~0x01; /* Do not reset machine */
outb(port_a, 0x92);
pio_ops.outb(port_a, 0x92);
}

/*
@@ -23,10 +23,10 @@
#include <linux/edd.h>
#include <asm/setup.h>
#include <asm/asm.h>
#include <asm/shared/io.h>
#include "bitops.h"
#include "ctype.h"
#include "cpuflags.h"
#include "io.h"

/* Useful macros */
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
@@ -47,6 +47,8 @@ void *memmove(void *dest, const void *src, size_t n);
*/
struct boot_params *boot_params;

struct port_io_ops pio_ops;

memptr free_mem_ptr;
memptr free_mem_end_ptr;

@@ -103,10 +105,12 @@ static void serial_putchar(int ch)
{
unsigned timeout = 0xffff;

while ((inb(early_serial_base + LSR) & XMTRDY) == 0 && --timeout)
while ((pio_ops.inb(early_serial_base + LSR) & XMTRDY) == 0 &&
--timeout) {
cpu_relax();
}

outb(ch, early_serial_base + TXR);
pio_ops.outb(ch, early_serial_base + TXR);
}

void __putstr(const char *s)
@@ -152,10 +156,10 @@ void __putstr(const char *s)
boot_params->screen_info.orig_y = y;

pos = (x + cols * y) * 2; /* Update cursor position */
outb(14, vidport);
outb(0xff & (pos >> 9), vidport+1);
outb(15, vidport);
outb(0xff & (pos >> 1), vidport+1);
pio_ops.outb(14, vidport);
pio_ops.outb(0xff & (pos >> 9), vidport+1);
pio_ops.outb(15, vidport);
pio_ops.outb(0xff & (pos >> 1), vidport+1);
}

void __puthex(unsigned long value)
@@ -370,6 +374,8 @@ asmlinkage __visible void *extract_kernel(void *rmode, memptr heap,
lines = boot_params->screen_info.orig_video_lines;
cols = boot_params->screen_info.orig_video_cols;

init_io_ops();

/*
* Detect TDX guest environment.
*
@@ -26,7 +26,6 @@
#include <asm/boot.h>
#include <asm/bootparam.h>
#include <asm/desc_defs.h>
#include <asm/shared/io.h>

#include "tdx.h"

@@ -35,6 +34,7 @@

#define BOOT_BOOT_H
#include "../ctype.h"
#include "../io.h"

#ifdef CONFIG_X86_64
#define memptr long
@@ -28,17 +28,17 @@ static void early_serial_init(int port, int baud)
unsigned char c;
unsigned divisor;

outb(0x3, port + LCR); /* 8n1 */
outb(0, port + IER); /* no interrupt */
outb(0, port + FCR); /* no fifo */
outb(0x3, port + MCR); /* DTR + RTS */
pio_ops.outb(0x3, port + LCR); /* 8n1 */
pio_ops.outb(0, port + IER); /* no interrupt */
pio_ops.outb(0, port + FCR); /* no fifo */
pio_ops.outb(0x3, port + MCR); /* DTR + RTS */

divisor = 115200 / baud;
c = inb(port + LCR);
outb(c | DLAB, port + LCR);
outb(divisor & 0xff, port + DLL);
outb((divisor >> 8) & 0xff, port + DLH);
outb(c & ~DLAB, port + LCR);
c = pio_ops.inb(port + LCR);
pio_ops.outb(c | DLAB, port + LCR);
pio_ops.outb(divisor & 0xff, port + DLL);
pio_ops.outb((divisor >> 8) & 0xff, port + DLH);
pio_ops.outb(c & ~DLAB, port + LCR);

early_serial_base = port;
}
@@ -104,11 +104,11 @@ static unsigned int probe_baud(int port)
unsigned char lcr, dll, dlh;
unsigned int quot;

lcr = inb(port + LCR);
outb(lcr | DLAB, port + LCR);
dll = inb(port + DLL);
dlh = inb(port + DLH);
outb(lcr, port + LCR);
lcr = pio_ops.inb(port + LCR);
pio_ops.outb(lcr | DLAB, port + LCR);
dll = pio_ops.inb(port + DLL);
dlh = pio_ops.inb(port + DLH);
pio_ops.outb(lcr, port + LCR);
quot = (dlh << 8) | dll;

return BASE_BAUD / quot;
@@ -0,0 +1,30 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef BOOT_IO_H
#define BOOT_IO_H

#include <asm/shared/io.h>

struct port_io_ops {
unsigned char (*inb)(int port);
unsigned short (*inw)(int port);
unsigned int (*inl)(int port);
void (*outb)(unsigned char v, int port);
void (*outw)(unsigned short v, int port);
void (*outl)(unsigned int v, int port);
};

extern struct port_io_ops pio_ops;

static inline void init_io_ops(void)
{
pio_ops = (struct port_io_ops){
.inb = inb,
.inw = inw,
.inl = inl,
.outb = outb,
.outw = outw,
.outl = outl,
};
}

#endif
@@ -17,6 +17,8 @@

struct boot_params boot_params __attribute__((aligned(16)));

struct port_io_ops pio_ops;

char *HEAP = _end;
char *heap_end = _end; /* Default end of heap = no heap */

@@ -133,6 +135,8 @@ static void init_heap(void)

void main(void)
{
init_io_ops();

/* First, copy the boot header into the "zeropage" */
copy_boot_params();

@@ -25,7 +25,7 @@ static void realmode_switch_hook(void)
: "eax", "ebx", "ecx", "edx");
} else {
asm volatile("cli");
outb(0x80, 0x70); /* Disable NMI */
pio_ops.outb(0x80, 0x70); /* Disable NMI */
io_delay();
}
}
@@ -35,9 +35,9 @@ static void realmode_switch_hook(void)
*/
static void mask_all_interrupts(void)
{
outb(0xff, 0xa1); /* Mask all interrupts on the secondary PIC */
pio_ops.outb(0xff, 0xa1); /* Mask all interrupts on the secondary PIC */
io_delay();
outb(0xfb, 0x21); /* Mask all but cascade on the primary PIC */
pio_ops.outb(0xfb, 0x21); /* Mask all but cascade on the primary PIC */
io_delay();
}

@@ -46,9 +46,9 @@ static void mask_all_interrupts(void)
*/
static void reset_coprocessor(void)
{
outb(0, 0xf0);
pio_ops.outb(0, 0xf0);
io_delay();
outb(0, 0xf1);
pio_ops.outb(0, 0xf1);
io_delay();
}

@@ -29,10 +29,10 @@ static void __section(".inittext") serial_putchar(int ch)
{
unsigned timeout = 0xffff;

while ((inb(early_serial_base + LSR) & XMTRDY) == 0 && --timeout)
while ((pio_ops.inb(early_serial_base + LSR) & XMTRDY) == 0 && --timeout)
cpu_relax();

outb(ch, early_serial_base + TXR);
pio_ops.outb(ch, early_serial_base + TXR);
}

static void __section(".inittext") bios_putchar(int ch)
@@ -131,7 +131,7 @@ static void vga_set_80x43(void)
/* I/O address of the VGA CRTC */
u16 vga_crtc(void)
{
return (inb(0x3cc) & 1) ? 0x3d4 : 0x3b4;
return (pio_ops.inb(0x3cc) & 1) ? 0x3d4 : 0x3b4;
}

static void vga_set_480_scanlines(void)
@@ -148,10 +148,10 @@ static void vga_set_480_scanlines(void)
out_idx(0xdf, crtc, 0x12); /* Vertical display end */
out_idx(0xe7, crtc, 0x15); /* Vertical blank start */
out_idx(0x04, crtc, 0x16); /* Vertical blank end */
csel = inb(0x3cc);
csel = pio_ops.inb(0x3cc);
csel &= 0x0d;
csel |= 0xe2;
outb(csel, 0x3c2);
pio_ops.outb(csel, 0x3c2);
}

static void vga_set_vertical_end(int lines)
@@ -15,6 +15,8 @@

#include <linux/types.h>

#include "boot.h"

/*
* This code uses an extended set of video mode numbers. These include:
* Aliases for standard modes
@@ -96,13 +98,13 @@ extern int graphic_mode; /* Graphics mode with linear frame buffer */
/* Accessing VGA indexed registers */
static inline u8 in_idx(u16 port, u8 index)
{
outb(index, port);
return inb(port+1);
pio_ops.outb(index, port);
return pio_ops.inb(port+1);
}

static inline void out_idx(u8 v, u16 port, u8 index)
{
outw(index+(v << 8), port);
pio_ops.outw(index+(v << 8), port);
}

/* Writes a value to an indexed port and then reads the port again */
@@ -17,18 +17,18 @@ static void beep(unsigned int hz)
} else {
u16 div = 1193181/hz;

outb(0xb6, 0x43); /* Ctr 2, squarewave, load, binary */
pio_ops.outb(0xb6, 0x43); /* Ctr 2, squarewave, load, binary */
io_delay();
outb(div, 0x42); /* LSB of counter */
pio_ops.outb(div, 0x42); /* LSB of counter */
io_delay();
outb(div >> 8, 0x42); /* MSB of counter */
pio_ops.outb(div >> 8, 0x42); /* MSB of counter */
io_delay();

enable = 0x03; /* Turn on speaker */
}
inb(0x61); /* Dummy read of System Control Port B */
pio_ops.inb(0x61); /* Dummy read of System Control Port B */
io_delay();
outb(enable, 0x61); /* Enable timer 2 output to speaker */
pio_ops.outb(enable, 0x61); /* Enable timer 2 output to speaker */
io_delay();
}

@@ -62,8 +62,12 @@ static void send_morse(const char *pattern)
}
}

struct port_io_ops pio_ops;

void main(void)
{
init_io_ops();

/* Kill machine if structures are wrong */
if (wakeup_header.real_magic != 0x12345678)
while (1)

0 comments on commit d8bd4e3

Please sign in to comment.