diff --git a/arch/arm/include/arch/reg.h b/arch/arm/include/arch/reg.h new file mode 100644 index 000000000..14abe2fb8 --- /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 000000000..bb1a76b1b --- /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 000000000..f1f77967e --- /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 000000000..f1f77967e --- /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 000000000..f1f77967e --- /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 000000000..f1f77967e --- /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 000000000..f1f77967e --- /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 000000000..9151b00e2 --- /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 c67b5c6d8..24d189d72 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))