From 86267ca23cb371c034307b6d1139bc1b009840bb Mon Sep 17 00:00:00 2001 From: Travis Geiselbrecht Date: Mon, 13 May 2024 00:28:27 -0700 Subject: [PATCH] [include][reg.h] define new mmio_read/write accessors To work properly with some hypervisors on various architectures (ARM, ARM64, x86), add global routines to allow access to MMIO registers via architecturally defined accessors. Add accessors for ARM, ARM64, and x86-32/64. Have the other arches default to just using whatever the compiler emits. Will need to generally move things off the legacy REG*() accessors since they're really not safe going forward with what compilers emit. --- arch/arm/include/arch/reg.h | 43 ++++++++++++++ arch/arm64/include/arch/reg.h | 49 ++++++++++++++++ arch/m68k/include/arch/reg.h | 9 +++ arch/microblaze/include/arch/reg.h | 9 +++ arch/mips/include/arch/reg.h | 9 +++ arch/or1k/include/arch/reg.h | 9 +++ arch/riscv/include/arch/reg.h | 9 +++ arch/x86/include/arch/reg.h | 55 ++++++++++++++++++ top/include/lk/reg.h | 92 +++++++++++++++++++++++++++--- 9 files changed, 275 insertions(+), 9 deletions(-) create mode 100644 arch/arm/include/arch/reg.h create mode 100644 arch/arm64/include/arch/reg.h create mode 100644 arch/m68k/include/arch/reg.h create mode 100644 arch/microblaze/include/arch/reg.h create mode 100644 arch/mips/include/arch/reg.h create mode 100644 arch/or1k/include/arch/reg.h create mode 100644 arch/riscv/include/arch/reg.h create mode 100644 arch/x86/include/arch/reg.h diff --git a/arch/arm/include/arch/reg.h b/arch/arm/include/arch/reg.h new file mode 100644 index 0000000000..14abe2fb8b --- /dev/null +++ b/arch/arm/include/arch/reg.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2024 Travis Geiselbrecht + * + * Use of this source code is governed by a MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT + */ +#pragma once + +#include + +// Accessor routines for this architecture, used in include/reg.h +// +// Simple load/store instructions with a basic addressing mode of either +// [reg], [reg, #imm] or [reg, reg], with no writeback are guaranteed to be +// fully decoded into ESR_EL2 for hypervisor assist. + +#define ARCH_MMIO_READ_WRITE_OVERRIDE 1 + +#define _ARCH_MMIO_READ8(addr) ({ \ + uint8_t val; \ + __asm__ volatile("ldrb %0, %1" : "=r"(val) : "m"(*(addr)) : "memory"); \ + val; \ +}) +#define _ARCH_MMIO_READ16(addr) ({ \ + uint16_t val; \ + __asm__ volatile("ldrh %0, %1" : "=r"(val) : "m"(*(addr)) : "memory"); \ + val; \ +}) +#define _ARCH_MMIO_READ32(addr) ({ \ + uint32_t val; \ + __asm__ volatile("ldr %0, %1" : "=r"(val) : "m"(*(addr)) : "memory"); \ + val; \ +}) + +#define _ARCH_MMIO_WRITE8(addr, val) \ + __asm__ volatile("strb %1, %0" : "=m"(*(addr)) : "r"(val) : "memory") +#define _ARCH_MMIO_WRITE16(addr, val) \ + __asm__ volatile("strh %1, %0" : "=m"(*(addr)) : "r"(val) : "memory") +#define _ARCH_MMIO_WRITE32(addr, val) \ + __asm__ volatile("str %1, %0" : "=m"(*(addr)) : "r"(val) : "memory") + + diff --git a/arch/arm64/include/arch/reg.h b/arch/arm64/include/arch/reg.h new file mode 100644 index 0000000000..bb1a76b1bc --- /dev/null +++ b/arch/arm64/include/arch/reg.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024 Travis Geiselbrecht + * + * Use of this source code is governed by a MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT + */ +#pragma once + +#include + +// Accessor routines for this architecture, used in include/reg.h +// +// Simple load/store instructions with a basic addressing mode of either +// [reg], [reg, #imm] or [reg, reg], with no writeback are guaranteed to be +// fully decoded into ESR_EL2 for hypervisor assist. + +#define ARCH_MMIO_READ_WRITE_OVERRIDE 1 + +#define _ARCH_MMIO_READ8(addr) ({ \ + uint8_t val; \ + __asm__ volatile("ldrb %w0, %1" : "=r"(val) : "m"(*(addr)) : "memory"); \ + val; \ +}) +#define _ARCH_MMIO_READ16(addr) ({ \ + uint16_t val; \ + __asm__ volatile("ldrh %w0, %1" : "=r"(val) : "m"(*(addr)) : "memory"); \ + val; \ +}) +#define _ARCH_MMIO_READ32(addr) ({ \ + uint32_t val; \ + __asm__ volatile("ldr %w0, %1" : "=r"(val) : "m"(*(addr)) : "memory"); \ + val; \ +}) +#define _ARCH_MMIO_READ64(addr) ({ \ + uint64_t val; \ + __asm__ volatile("ldr %0, %1" : "=r"(val) : "m"(*(addr)) : "memory"); \ + val; \ +}) + +#define _ARCH_MMIO_WRITE8(addr, val) \ + __asm__ volatile("strb %w1, %0" : "=m"(*(addr)) : "r"(val) : "memory") +#define _ARCH_MMIO_WRITE16(addr, val) \ + __asm__ volatile("strh %w1, %0" : "=m"(*(addr)): "r"(val) : "memory") +#define _ARCH_MMIO_WRITE32(addr, val) \ + __asm__ volatile("str %w1, %0" : "=m"(*(addr)) : "r"(val) : "memory") +#define _ARCH_MMIO_WRITE64(addr, val) \ + __asm__ volatile("str %1, %0" : "=m"(*(addr)) : "r"(val) : "memory") + diff --git a/arch/m68k/include/arch/reg.h b/arch/m68k/include/arch/reg.h new file mode 100644 index 0000000000..f1f77967e8 --- /dev/null +++ b/arch/m68k/include/arch/reg.h @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2024 Travis Geiselbrecht + * + * Use of this source code is governed by a MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT + */ +#pragma once + diff --git a/arch/microblaze/include/arch/reg.h b/arch/microblaze/include/arch/reg.h new file mode 100644 index 0000000000..f1f77967e8 --- /dev/null +++ b/arch/microblaze/include/arch/reg.h @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2024 Travis Geiselbrecht + * + * Use of this source code is governed by a MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT + */ +#pragma once + diff --git a/arch/mips/include/arch/reg.h b/arch/mips/include/arch/reg.h new file mode 100644 index 0000000000..f1f77967e8 --- /dev/null +++ b/arch/mips/include/arch/reg.h @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2024 Travis Geiselbrecht + * + * Use of this source code is governed by a MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT + */ +#pragma once + diff --git a/arch/or1k/include/arch/reg.h b/arch/or1k/include/arch/reg.h new file mode 100644 index 0000000000..f1f77967e8 --- /dev/null +++ b/arch/or1k/include/arch/reg.h @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2024 Travis Geiselbrecht + * + * Use of this source code is governed by a MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT + */ +#pragma once + diff --git a/arch/riscv/include/arch/reg.h b/arch/riscv/include/arch/reg.h new file mode 100644 index 0000000000..f1f77967e8 --- /dev/null +++ b/arch/riscv/include/arch/reg.h @@ -0,0 +1,9 @@ +/* + * Copyright (c) 2024 Travis Geiselbrecht + * + * Use of this source code is governed by a MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT + */ +#pragma once + diff --git a/arch/x86/include/arch/reg.h b/arch/x86/include/arch/reg.h new file mode 100644 index 0000000000..9151b00e21 --- /dev/null +++ b/arch/x86/include/arch/reg.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024 Travis Geiselbrecht + * + * Use of this source code is governed by a MIT-style + * license that can be found in the LICENSE file or at + * https://opensource.org/licenses/MIT + */ +#pragma once + +#include + +// Accessor routines for this architecture, used in include/reg.h +// +// Simple mov instructions that directly access the mmio address using the +// precise width needed in one instruction. +// +// NOTE: this may not be strict enough, and it may be necessary to load the +// address into a register and use a simple (reg) dereference, instead of a more +// complicated #imm(reg, reg, #imm) addressing mode. + +#define ARCH_MMIO_READ_WRITE_OVERRIDE 1 + +#define _ARCH_MMIO_READ8(addr) ({ \ + uint8_t val; \ + __asm__ volatile("movb %1, %0" : "=q"(val) : "m"(*(addr)) : "memory"); \ + val; \ +}) +#define _ARCH_MMIO_READ16(addr) ({ \ + uint16_t val; \ + __asm__ volatile("movw %1, %0" : "=r"(val) : "m"(*(addr)) : "memory"); \ + val; \ +}) +#define _ARCH_MMIO_READ32(addr) ({ \ + uint32_t val; \ + __asm__ volatile("movl %1, %0" : "=r"(val) : "m"(*(addr)) : "memory"); \ + val; \ +}) +#if _LP64 +#define _ARCH_MMIO_READ64(addr) ({ \ + uint64_t val; \ + __asm__ volatile("movq %1, %0" : "=r"(val) : "m"(*(addr)) : "memory"); \ + val; \ +}) +#endif + +#define _ARCH_MMIO_WRITE8(addr, val) \ + __asm__ volatile("movb %1, %0" : "=m"(*(addr)) : "iq"(val) : "memory") +#define _ARCH_MMIO_WRITE16(addr, val) \ + __asm__ volatile("movw %1, %0" : "=m"(*(addr)) : "ir"(val) : "memory") +#define _ARCH_MMIO_WRITE32(addr, val) \ + __asm__ volatile("movl %1, %0" : "=m"(*(addr)) : "ir"(val) : "memory") +#if _LP64 +#define _ARCH_MMIO_WRITE64(addr, val) \ + __asm__ volatile("movq %1, %0" : "=m"(*(addr)) : "er"(val) : "memory") +#endif diff --git a/top/include/lk/reg.h b/top/include/lk/reg.h index c67b5c6d89..24d189d72a 100644 --- a/top/include/lk/reg.h +++ b/top/include/lk/reg.h @@ -9,18 +9,92 @@ #include -/* low level macros for accessing memory mapped hardware registers */ +// Low level macros for accessing memory mapped hardware registers + +// Each architecture has an ability to provide their own specific accessors, or use the default +#include + +// Newer style accessors, generally preferred due to virtual machine virtualization. +// +// If the arch didn't override it, use the default +#if !ARCH_MMIO_READ_WRITE_OVERRIDE +#define _MMIO_READ_DEFAULT(ptr) *ptr +#define _MMIO_WRITE_DEFAULT(ptr, val) *ptr = val + +#define _ARCH_MMIO_READ8 _MMIO_READ_DEFAULT +#define _ARCH_MMIO_READ16 _MMIO_READ_DEFAULT +#define _ARCH_MMIO_READ32 _MMIO_READ_DEFAULT +#define _ARCH_MMIO_READ64 _MMIO_READ_DEFAULT +#define _ARCH_MMIO_WRITE8 _MMIO_WRITE_DEFAULT +#define _ARCH_MMIO_WRITE16 _MMIO_WRITE_DEFAULT +#define _ARCH_MMIO_WRITE32 _MMIO_WRITE_DEFAULT +#define _ARCH_MMIO_WRITE64 _MMIO_WRITE_DEFAULT +#endif + +static inline uint8_t mmio_read8(volatile uint8_t *ptr) { + return _ARCH_MMIO_READ8(ptr); +} + +static inline uint16_t mmio_read16(volatile uint16_t *ptr) { + return _ARCH_MMIO_READ16(ptr); +} + +static inline uint32_t mmio_read32(volatile uint32_t *ptr) { + return _ARCH_MMIO_READ32(ptr); +} + +#if _LP64 +static inline uint64_t mmio_read64(volatile uint64_t *ptr) { + return _ARCH_MMIO_READ64(ptr); +} +#endif + +// For architectures that do not need stricter accessor instructions +static inline void mmio_write8(volatile uint8_t *ptr, uint8_t val) { + _ARCH_MMIO_WRITE8(ptr, val); +} + +static inline void mmio_write16(volatile uint16_t *ptr, uint16_t val) { + _ARCH_MMIO_WRITE16(ptr, val); +} + +static inline void mmio_write32(volatile uint32_t *ptr, uint32_t val) { + _ARCH_MMIO_WRITE32(ptr, val); +} + +#if _LP64 +static inline void mmio_write64(volatile uint64_t *ptr, uint64_t val) { + _ARCH_MMIO_WRITE64(ptr, val); +} +#endif + +// TODO: define C++ accessors here + +// Older style accessors: +// NOTE: These are not generally VM safe, since some architectures require specific instruction +// forms to work properly in a trap-and-emulate situation. #define REG64(addr) ((volatile uint64_t *)(uintptr_t)(addr)) #define REG32(addr) ((volatile uint32_t *)(uintptr_t)(addr)) #define REG16(addr) ((volatile uint16_t *)(uintptr_t)(addr)) #define REG8(addr) ((volatile uint8_t *)(uintptr_t)(addr)) -#define RMWREG64(addr, startbit, width, val) *REG64(addr) = (*REG64(addr) & ~(((1<<(width)) - 1) << (startbit))) | ((val) << (startbit)) -#define RMWREG32(addr, startbit, width, val) *REG32(addr) = (*REG32(addr) & ~(((1<<(width)) - 1) << (startbit))) | ((val) << (startbit)) -#define RMWREG16(addr, startbit, width, val) *REG16(addr) = (*REG16(addr) & ~(((1<<(width)) - 1) << (startbit))) | ((val) << (startbit)) -#define RMWREG8(addr, startbit, width, val) *REG8(addr) = (*REG8(addr) & ~(((1<<(width)) - 1) << (startbit))) | ((val) << (startbit)) +#define RMWREG64(addr, startbit, width, val) mmio_write64((volatile void *)(addr), (mmio_read64((volatile void *)(addr)) & ~(((1<<(width)) - 1) << (startbit))) | ((val) << (startbit))) +#define RMWREG32(addr, startbit, width, val) mmio_write32((volatile void *)(addr), (mmio_read32((volatile void *)(addr)) & ~(((1<<(width)) - 1) << (startbit))) | ((val) << (startbit))) +#define RMWREG16(addr, startbit, width, val) mmio_write16((volatile void *)(addr), (mmio_read16((volatile void *)(addr)) & ~(((1<<(width)) - 1) << (startbit))) | ((val) << (startbit))) +#define RMWREG8(addr, startbit, width, val) mmio_write8((volatile void *)(addr), (mmio_read8((volatile void *)(addr)) & ~(((1<<(width)) - 1) << (startbit))) | ((val) << (startbit))) + +// Linux-style accessors +#define readb(a) mmio_read8((volatile uint8_t *)(a)) +#define readw(a) mmio_read16((volatile uint16_t *)(a)) +#define readl(a) mmio_read32((volatile uint32_t *)(a)) +#if _LP64 +#define readq(a) mmio_read64((volatile uint64_t *)(a)) +#endif +#define writeb(v, a) mmio_write8((volatile uint8_t *)(a), v) +#define writew(v, a) mmio_write16((volatile uint16_t *)(a), v) +#define writel(v, a) mmio_write32((volatile uint32_t *)(a), v) +#if _LP64 +#define writeq(v, a) mmio_write64((volatile uint64_t *)(a), v) +#endif + -#define writel(v, a) (*REG32(a) = (v)) -#define readl(a) (*REG32(a)) -#define writeb(v, a) (*REG8(a) = (v)) -#define readb(a) (*REG8(a))