ANDROID: drivers: misc: Add an example pl011 driver for pKVM

This is an example pl011 driver that has been tuned for the Google Pixel
devices only! Its main purpose is to demonstrate how to use the newly
introduce pKVM module loading feature.

Bug: 357781595
Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
Signed-off-by: Quentin Perret <qperret@google.com>
Change-Id: Id56bd3f39e91da490e97e8683a8cc7454497035b
This commit is contained in:
Vincent Donnefort
2022-09-16 11:47:07 +01:00
committed by Keir Fraser
parent 8903638740
commit 1a8bda12e0
8 changed files with 127 additions and 0 deletions
+2
View File
@@ -1603,3 +1603,5 @@ endmenu
config SERIAL_MCTRL_GPIO
tristate
source "drivers/tty/serial/pkvm-pl011/Kconfig"
+2
View File
@@ -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/
+31
View File
@@ -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
+8
View File
@@ -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
@@ -0,0 +1,2 @@
hyp-reloc.S
hyp.lds
@@ -0,0 +1,2 @@
hyp-obj-y := pl011-hyp.o
include $(srctree)/arch/arm64/kvm/hyp/nvhe/Makefile.module
@@ -0,0 +1,54 @@
#include <asm/alternative-macros.h>
#include <asm/barrier.h>
#include <asm/kvm_mmu.h>
#include <asm/kvm_pkvm_module.h>
#include <asm/io.h>
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 <<CONFIG_SERIAL_PKVM_PL011_FULL));
dmb(sy);
__hyp_writew(c, base + CONFIG_SERIAL_PKVM_PL011_UARTTX);
do {
val = __hyp_readw(base + CONFIG_SERIAL_PKVM_PL011_UARTFR);
} while (val & (1U << CONFIG_SERIAL_PKVM_PL011_BUSY));
dmb(sy);
}
int pl011_hyp_init(const struct pkvm_module_ops *ops)
{
int ret;
ret = ops->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;
}
@@ -0,0 +1,26 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <asm/kvm_pkvm_module.h>
#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");