2022-02-18 16:38:22

by Kirill A. Shutemov

[permalink] [raw]
Subject: [PATCHv3 16/32] 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.

Signed-off-by: Kirill A. Shutemov <[email protected]>
---
arch/x86/boot/a20.c | 14 +++++++-------
arch/x86/boot/boot.h | 2 +-
arch/x86/boot/compressed/misc.c | 18 ++++++++++++------
arch/x86/boot/compressed/misc.h | 2 +-
arch/x86/boot/early_serial_console.c | 28 ++++++++++++++--------------
arch/x86/boot/io.h | 28 ++++++++++++++++++++++++++++
arch/x86/boot/main.c | 4 ++++
arch/x86/boot/pm.c | 10 +++++-----
arch/x86/boot/tty.c | 4 ++--
arch/x86/boot/video-vga.c | 6 +++---
arch/x86/boot/video.h | 8 +++++---
arch/x86/realmode/rm/wakemain.c | 14 +++++++++-----
12 files changed, 91 insertions(+), 47 deletions(-)
create mode 100644 arch/x86/boot/io.h

diff --git a/arch/x86/boot/a20.c b/arch/x86/boot/a20.c
index a2b6b428922a..7f6dd5cc4670 100644
--- a/arch/x86/boot/a20.c
+++ b/arch/x86/boot/a20.c
@@ -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,13 +99,13 @@ 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();
}

@@ -113,10 +113,10 @@ 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);
}

/*
diff --git a/arch/x86/boot/boot.h b/arch/x86/boot/boot.h
index 22a474c5b3e8..bd8f640ca15f 100644
--- a/arch/x86/boot/boot.h
+++ b/arch/x86/boot/boot.h
@@ -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)))
diff --git a/arch/x86/boot/compressed/misc.c b/arch/x86/boot/compressed/misc.c
index 2b1169869b96..c0711b18086a 100644
--- a/arch/x86/boot/compressed/misc.c
+++ b/arch/x86/boot/compressed/misc.c
@@ -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.
*
diff --git a/arch/x86/boot/compressed/misc.h b/arch/x86/boot/compressed/misc.h
index 8a253e85f990..ea71cf3d64e1 100644
--- a/arch/x86/boot/compressed/misc.h
+++ b/arch/x86/boot/compressed/misc.h
@@ -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
diff --git a/arch/x86/boot/early_serial_console.c b/arch/x86/boot/early_serial_console.c
index 023bf1c3de8b..03e43d770571 100644
--- a/arch/x86/boot/early_serial_console.c
+++ b/arch/x86/boot/early_serial_console.c
@@ -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;
diff --git a/arch/x86/boot/io.h b/arch/x86/boot/io.h
new file mode 100644
index 000000000000..8a53947ef70e
--- /dev/null
+++ b/arch/x86/boot/io.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef BOOT_IO_H
+#define BOOT_IO_H
+
+#include <asm/shared/io.h>
+
+struct port_io_ops {
+ u8 (*inb)(u16 port);
+ u16 (*inw)(u16 port);
+ u32 (*inl)(u16 port);
+ void (*outb)(u8 v, u16 port);
+ void (*outw)(u16 v, u16 port);
+ void (*outl)(u32 v, u16 port);
+};
+
+extern struct port_io_ops pio_ops;
+
+static inline void init_io_ops(void)
+{
+ pio_ops.inb = inb;
+ pio_ops.inw = inw;
+ pio_ops.inl = inl;
+ pio_ops.outb = outb;
+ pio_ops.outw = outw;
+ pio_ops.outl = outl;
+}
+
+#endif
diff --git a/arch/x86/boot/main.c b/arch/x86/boot/main.c
index e3add857c2c9..447a797891be 100644
--- a/arch/x86/boot/main.c
+++ b/arch/x86/boot/main.c
@@ -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();

diff --git a/arch/x86/boot/pm.c b/arch/x86/boot/pm.c
index 40031a614712..4180b6a264c9 100644
--- a/arch/x86/boot/pm.c
+++ b/arch/x86/boot/pm.c
@@ -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();
}

diff --git a/arch/x86/boot/tty.c b/arch/x86/boot/tty.c
index f7eb976b0a4b..ee8700682801 100644
--- a/arch/x86/boot/tty.c
+++ b/arch/x86/boot/tty.c
@@ -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)
diff --git a/arch/x86/boot/video-vga.c b/arch/x86/boot/video-vga.c
index 4816cb9cf996..17baac542ee7 100644
--- a/arch/x86/boot/video-vga.c
+++ b/arch/x86/boot/video-vga.c
@@ -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)
diff --git a/arch/x86/boot/video.h b/arch/x86/boot/video.h
index 04bde0bb2003..87a5f726e731 100644
--- a/arch/x86/boot/video.h
+++ b/arch/x86/boot/video.h
@@ -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 */
diff --git a/arch/x86/realmode/rm/wakemain.c b/arch/x86/realmode/rm/wakemain.c
index 1d6437e6d2ba..b49404d0d63c 100644
--- a/arch/x86/realmode/rm/wakemain.c
+++ b/arch/x86/realmode/rm/wakemain.c
@@ -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)
--
2.34.1


2022-02-22 04:06:43

by Josh Poimboeuf

[permalink] [raw]
Subject: Re: [PATCHv3 16/32] x86/boot: Allow to hook up alternative port I/O helpers

On Tue, Feb 22, 2022 at 01:02:33AM +0300, Kirill A. Shutemov wrote:
> On Mon, Feb 21, 2022 at 02:04:33PM -0600, Tom Lendacky wrote:
> > On 2/18/22 10:17, Kirill A. Shutemov wrote:
> > > 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.
> >
> > This seems like a lot of churn in order to get this all working without
> > taking a #VE.
>
> Well, it evolved from more concise (but also more hacky) implementation:
>
> https://lore.kernel.org/all/[email protected]
>
> > How cumbersome is it to get #VE handling working in the decompression
> > stage? Can you build on any of the support that was added to handle #VC?
>
> We definitely can.
>
> But I still think exception-based implementation is inherently more
> fragile. I would rather stick with this.

At least from reading the commit message it's not self-evident why #VE
handling would be worse, especially since there's already #VC support in
boot. It would help to give more info about that in the commit message.

The current approach also seems fragile, doesn't it require all future
code to remember to not do i/o directly? How do we make sure that
doesn't happen going forward?

How does it fail if some code accidentally does i/o directly? Or
triggers #VE some other way? Is the error understandable and
actionable?

--
Josh

2022-02-22 04:35:04

by Kirill A. Shutemov

[permalink] [raw]
Subject: Re: [PATCHv3 16/32] x86/boot: Allow to hook up alternative port I/O helpers

On Mon, Feb 21, 2022 at 02:04:33PM -0600, Tom Lendacky wrote:
> On 2/18/22 10:17, Kirill A. Shutemov wrote:
> > 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.
>
> This seems like a lot of churn in order to get this all working without
> taking a #VE.

Well, it evolved from more concise (but also more hacky) implementation:

https://lore.kernel.org/all/[email protected]

> How cumbersome is it to get #VE handling working in the decompression
> stage? Can you build on any of the support that was added to handle #VC?

We definitely can.

But I still think exception-based implementation is inherently more
fragile. I would rather stick with this.

--
Kirill A. Shutemov

2022-02-22 05:05:06

by Tom Lendacky

[permalink] [raw]
Subject: Re: [PATCHv3 16/32] x86/boot: Allow to hook up alternative port I/O helpers

On 2/18/22 10:17, Kirill A. Shutemov wrote:
> 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.

This seems like a lot of churn in order to get this all working without
taking a #VE. How cumbersome is it to get #VE handling working in the
decompression stage? Can you build on any of the support that was added to
handle #VC?

Thanks,
Tom

>
> Signed-off-by: Kirill A. Shutemov <[email protected]>
> ---
> arch/x86/boot/a20.c | 14 +++++++-------
> arch/x86/boot/boot.h | 2 +-
> arch/x86/boot/compressed/misc.c | 18 ++++++++++++------
> arch/x86/boot/compressed/misc.h | 2 +-
> arch/x86/boot/early_serial_console.c | 28 ++++++++++++++--------------
> arch/x86/boot/io.h | 28 ++++++++++++++++++++++++++++
> arch/x86/boot/main.c | 4 ++++
> arch/x86/boot/pm.c | 10 +++++-----
> arch/x86/boot/tty.c | 4 ++--
> arch/x86/boot/video-vga.c | 6 +++---
> arch/x86/boot/video.h | 8 +++++---
> arch/x86/realmode/rm/wakemain.c | 14 +++++++++-----
> 12 files changed, 91 insertions(+), 47 deletions(-)
> create mode 100644 arch/x86/boot/io.h
>
> diff --git a/arch/x86/boot/a20.c b/arch/x86/boot/a20.c
> index a2b6b428922a..7f6dd5cc4670 100644
> --- a/arch/x86/boot/a20.c
> +++ b/arch/x86/boot/a20.c
> @@ -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,13 +99,13 @@ 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();
> }
>
> @@ -113,10 +113,10 @@ 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);
> }
>
> /*
> diff --git a/arch/x86/boot/boot.h b/arch/x86/boot/boot.h
> index 22a474c5b3e8..bd8f640ca15f 100644
> --- a/arch/x86/boot/boot.h
> +++ b/arch/x86/boot/boot.h
> @@ -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)))
> diff --git a/arch/x86/boot/compressed/misc.c b/arch/x86/boot/compressed/misc.c
> index 2b1169869b96..c0711b18086a 100644
> --- a/arch/x86/boot/compressed/misc.c
> +++ b/arch/x86/boot/compressed/misc.c
> @@ -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.
> *
> diff --git a/arch/x86/boot/compressed/misc.h b/arch/x86/boot/compressed/misc.h
> index 8a253e85f990..ea71cf3d64e1 100644
> --- a/arch/x86/boot/compressed/misc.h
> +++ b/arch/x86/boot/compressed/misc.h
> @@ -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
> diff --git a/arch/x86/boot/early_serial_console.c b/arch/x86/boot/early_serial_console.c
> index 023bf1c3de8b..03e43d770571 100644
> --- a/arch/x86/boot/early_serial_console.c
> +++ b/arch/x86/boot/early_serial_console.c
> @@ -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;
> diff --git a/arch/x86/boot/io.h b/arch/x86/boot/io.h
> new file mode 100644
> index 000000000000..8a53947ef70e
> --- /dev/null
> +++ b/arch/x86/boot/io.h
> @@ -0,0 +1,28 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef BOOT_IO_H
> +#define BOOT_IO_H
> +
> +#include <asm/shared/io.h>
> +
> +struct port_io_ops {
> + u8 (*inb)(u16 port);
> + u16 (*inw)(u16 port);
> + u32 (*inl)(u16 port);
> + void (*outb)(u8 v, u16 port);
> + void (*outw)(u16 v, u16 port);
> + void (*outl)(u32 v, u16 port);
> +};
> +
> +extern struct port_io_ops pio_ops;
> +
> +static inline void init_io_ops(void)
> +{
> + pio_ops.inb = inb;
> + pio_ops.inw = inw;
> + pio_ops.inl = inl;
> + pio_ops.outb = outb;
> + pio_ops.outw = outw;
> + pio_ops.outl = outl;
> +}
> +
> +#endif
> diff --git a/arch/x86/boot/main.c b/arch/x86/boot/main.c
> index e3add857c2c9..447a797891be 100644
> --- a/arch/x86/boot/main.c
> +++ b/arch/x86/boot/main.c
> @@ -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();
>
> diff --git a/arch/x86/boot/pm.c b/arch/x86/boot/pm.c
> index 40031a614712..4180b6a264c9 100644
> --- a/arch/x86/boot/pm.c
> +++ b/arch/x86/boot/pm.c
> @@ -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();
> }
>
> diff --git a/arch/x86/boot/tty.c b/arch/x86/boot/tty.c
> index f7eb976b0a4b..ee8700682801 100644
> --- a/arch/x86/boot/tty.c
> +++ b/arch/x86/boot/tty.c
> @@ -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)
> diff --git a/arch/x86/boot/video-vga.c b/arch/x86/boot/video-vga.c
> index 4816cb9cf996..17baac542ee7 100644
> --- a/arch/x86/boot/video-vga.c
> +++ b/arch/x86/boot/video-vga.c
> @@ -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)
> diff --git a/arch/x86/boot/video.h b/arch/x86/boot/video.h
> index 04bde0bb2003..87a5f726e731 100644
> --- a/arch/x86/boot/video.h
> +++ b/arch/x86/boot/video.h
> @@ -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 */
> diff --git a/arch/x86/realmode/rm/wakemain.c b/arch/x86/realmode/rm/wakemain.c
> index 1d6437e6d2ba..b49404d0d63c 100644
> --- a/arch/x86/realmode/rm/wakemain.c
> +++ b/arch/x86/realmode/rm/wakemain.c
> @@ -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)