Skip to content

Commit

Permalink
[include][reg.h] define new mmio_read/write accessors
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
travisg committed May 13, 2024
1 parent 356e9ad commit 86267ca
Show file tree
Hide file tree
Showing 9 changed files with 275 additions and 9 deletions.
43 changes: 43 additions & 0 deletions arch/arm/include/arch/reg.h
Original file line number Diff line number Diff line change
@@ -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 <stdint.h>

// 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")


49 changes: 49 additions & 0 deletions arch/arm64/include/arch/reg.h
Original file line number Diff line number Diff line change
@@ -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 <stdint.h>

// 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")

9 changes: 9 additions & 0 deletions arch/m68k/include/arch/reg.h
Original file line number Diff line number Diff line change
@@ -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

9 changes: 9 additions & 0 deletions arch/microblaze/include/arch/reg.h
Original file line number Diff line number Diff line change
@@ -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

9 changes: 9 additions & 0 deletions arch/mips/include/arch/reg.h
Original file line number Diff line number Diff line change
@@ -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

9 changes: 9 additions & 0 deletions arch/or1k/include/arch/reg.h
Original file line number Diff line number Diff line change
@@ -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

9 changes: 9 additions & 0 deletions arch/riscv/include/arch/reg.h
Original file line number Diff line number Diff line change
@@ -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

55 changes: 55 additions & 0 deletions arch/x86/include/arch/reg.h
Original file line number Diff line number Diff line change
@@ -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 <stdint.h>

// 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
92 changes: 83 additions & 9 deletions top/include/lk/reg.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,92 @@

#include <stdint.h>

/* 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 <arch/reg.h>

// 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))

0 comments on commit 86267ca

Please sign in to comment.