diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 72f2318dec86..8007c0a84c1a 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1603,3 +1603,5 @@ endmenu config SERIAL_MCTRL_GPIO tristate + +source "drivers/tty/serial/pkvm-pl011/Kconfig" diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 6ff74f0a9530..e0ecdc2e7e14 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -99,3 +99,5 @@ obj-$(CONFIG_SERIAL_MCTRL_GPIO) += serial_mctrl_gpio.o obj-$(CONFIG_SERIAL_KGDB_NMI) += kgdb_nmi.o obj-$(CONFIG_KGDB_SERIAL_CONSOLE) += kgdboc.o obj-$(CONFIG_SERIAL_NUVOTON_MA35D1) += ma35d1_serial.o + +obj-$(CONFIG_SERIAL_PKVM_PL011) += pkvm-pl011/ diff --git a/drivers/tty/serial/pkvm-pl011/Kconfig b/drivers/tty/serial/pkvm-pl011/Kconfig new file mode 100644 index 000000000000..772825a57cda --- /dev/null +++ b/drivers/tty/serial/pkvm-pl011/Kconfig @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: GPL-2.0 + +config SERIAL_PKVM_PL011 + tristate "Protected KVM PL011 UART" + depends on ARM64 + depends on KVM + +config SERIAL_PKVM_PL011_BASE_PHYS + hex + depends on SERIAL_PKVM_PL011 + default 0x09000000 + +config SERIAL_PKVM_PL011_UARTFR + hex + depends on SERIAL_PKVM_PL011 + default 0x18 + +config SERIAL_PKVM_PL011_UARTTX + hex + depends on SERIAL_PKVM_PL011 + default 0x0 + +config SERIAL_PKVM_PL011_BUSY + hex + depends on SERIAL_PKVM_PL011 + default 0x2 + +config SERIAL_PKVM_PL011_FULL + hex + depends on SERIAL_PKVM_PL011 + default 0x6 diff --git a/drivers/tty/serial/pkvm-pl011/Makefile b/drivers/tty/serial/pkvm-pl011/Makefile new file mode 100644 index 000000000000..be22f1348539 --- /dev/null +++ b/drivers/tty/serial/pkvm-pl011/Makefile @@ -0,0 +1,8 @@ +obj-m += pkvm_pl011.o + +$(obj)/hyp/kvm_nvhe.o: FORCE + $(Q)$(MAKE) $(build)=$(obj)/hyp $(obj)/hyp/kvm_nvhe.o + +clean-files := hyp/hyp.lds hyp/hyp-reloc.S + +pkvm_pl011-y := pl011-host.o hyp/kvm_nvhe.o diff --git a/drivers/tty/serial/pkvm-pl011/hyp/.gitignore b/drivers/tty/serial/pkvm-pl011/hyp/.gitignore new file mode 100644 index 000000000000..185e3ad69ca2 --- /dev/null +++ b/drivers/tty/serial/pkvm-pl011/hyp/.gitignore @@ -0,0 +1,2 @@ +hyp-reloc.S +hyp.lds diff --git a/drivers/tty/serial/pkvm-pl011/hyp/Makefile b/drivers/tty/serial/pkvm-pl011/hyp/Makefile new file mode 100644 index 000000000000..82765177d962 --- /dev/null +++ b/drivers/tty/serial/pkvm-pl011/hyp/Makefile @@ -0,0 +1,2 @@ +hyp-obj-y := pl011-hyp.o +include $(srctree)/arch/arm64/kvm/hyp/nvhe/Makefile.module diff --git a/drivers/tty/serial/pkvm-pl011/hyp/pl011-hyp.c b/drivers/tty/serial/pkvm-pl011/hyp/pl011-hyp.c new file mode 100644 index 000000000000..30090c4b07f4 --- /dev/null +++ b/drivers/tty/serial/pkvm-pl011/hyp/pl011-hyp.c @@ -0,0 +1,54 @@ +#include +#include +#include +#include +#include + +static unsigned long uart_addr; + +static inline unsigned int __hyp_readw(void *ioaddr) +{ + unsigned int val; + asm volatile("ldr %w0, [%1]" : "=r" (val) : "r" (ioaddr)); + return val; +} +static inline void __hyp_writew(unsigned int val, void *ioaddr) +{ + asm volatile("str %w0, [%1]" : : "r" (val), "r" (ioaddr)); +} + +static void pl011_hyp_putc(char c) +{ + void *base = (void *)uart_addr; + unsigned int val; + + do { + val = __hyp_readw(base + CONFIG_SERIAL_PKVM_PL011_UARTFR); + } while (val & (1U <create_private_mapping(CONFIG_SERIAL_PKVM_PL011_BASE_PHYS, PAGE_SIZE, + PAGE_HYP_DEVICE, &uart_addr); + if (ret) + return ret; + + ret = ops->register_serial_driver(pl011_hyp_putc); + if (ret) + return ret; + + ops->puts("pKVM pl011 UART driver loaded"); + + return 0; +} diff --git a/drivers/tty/serial/pkvm-pl011/pl011-host.c b/drivers/tty/serial/pkvm-pl011/pl011-host.c new file mode 100644 index 000000000000..797a6abbb1f9 --- /dev/null +++ b/drivers/tty/serial/pkvm-pl011/pl011-host.c @@ -0,0 +1,26 @@ +#include +#include +#include + +#include + +#ifndef MODULE +BUILD_BUG("pKVM pl011 UART must be compiled as a module"); +#endif + +int __kvm_nvhe_pl011_hyp_init(const struct pkvm_module_ops *ops); + +static int __init pl011_nvhe_init(void) +{ + unsigned long token; + int ret; + + ret = pkvm_load_el2_module(__kvm_nvhe_pl011_hyp_init, &token); + if (ret) + return ret; + + return 0; +} +module_init(pl011_nvhe_init); + +MODULE_LICENSE("GPL");