#else /* ASSEMBLY */
-#ifdef UACCESS_LIBRM
-#define UACCESS_PREFIX_librm
-#else
-#define UACCESS_PREFIX_librm __librm_
-#endif
-
/**
* Call C function from real-mode code
*
"pushl $( " _S2 ( VIRTUAL ( function ) ) " )\n\t" \
"call virt_call\n\t"
-/* Variables in librm.S */
-extern const unsigned long virt_offset;
-
-/**
- * Convert physical address to user pointer
- *
- * @v phys Physical address
- * @ret virt Virtual address
- */
-static inline __always_inline void *
-UACCESS_INLINE ( librm, phys_to_virt ) ( unsigned long phys ) {
-
- /* In a 64-bit build, any valid physical address is directly
- * usable as a virtual address, since the low 4GB is
- * identity-mapped.
- */
- if ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) )
- return ( ( void * ) phys );
-
- /* In a 32-bit build, subtract virt_offset */
- return ( ( void * ) ( phys - virt_offset ) );
-}
-
-/**
- * Convert virtual address to physical address
- *
- * @v virt Virtual address
- * @ret phys Physical address
- */
-static inline __always_inline physaddr_t
-UACCESS_INLINE ( librm, virt_to_phys ) ( volatile const void *virt ) {
- physaddr_t addr = ( ( physaddr_t ) virt );
-
- /* In a 64-bit build, any virtual address in the low 4GB is
- * directly usable as a physical address, since the low 4GB is
- * identity-mapped.
- */
- if ( ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) ) &&
- ( addr <= 0xffffffffUL ) )
- return addr;
-
- /* In a 32-bit build or in a 64-bit build with a virtual
- * address above 4GB: add virt_offset
- */
- return ( addr + virt_offset );
-}
-
/******************************************************************************
*
* Access to variables in .data16 and .text16
--- /dev/null
+/*
+ * Copyright (C) 2025 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/uaccess.h>
+
+/**
+ * @file
+ *
+ * Virtual offset memory model
+ *
+ */
+
+PROVIDE_UACCESS_INLINE ( offset, phys_to_virt );
+PROVIDE_UACCESS_INLINE ( offset, virt_to_phys );
--- /dev/null
+#ifndef _IPXE_VIRT_OFFSET_H
+#define _IPXE_VIRT_OFFSET_H
+
+/**
+ * @file
+ *
+ * Virtual offset memory model
+ *
+ * No currently supported machine provides a full 64 bits of physical
+ * address space. When we have ownership of the page tables (or
+ * segmentation mechanism), we can therefore use the following model:
+ *
+ * - For 32-bit builds: set up a circular map so that all 32-bit
+ * virtual addresses are at a fixed offset from the 32-bit
+ * physical addresses.
+ *
+ * - For 64-bit builds: identity-map the required portion of the
+ * physical address space, then map iPXE itself using virtual
+ * addresses in the negative (kernel) address space.
+ *
+ * In both cases, we can define "virt_offset" as "the value to be
+ * added to an address within iPXE's own image in order to obtain its
+ * physical address". With this definition:
+ *
+ * - For 32-bit builds: conversion between physical and virtual
+ * addresses is a straightforward addition or subtraction of
+ * virt_offset, since the whole 32-bit address space is circular.
+ *
+ * - For 64-bit builds: conversion from any valid physical address
+ * is a no-op (since all physical addresses are identity-mapped),
+ * and conversion from a virtual address to a physical address
+ * requires an addition of virt_offset if and only if the virtual
+ * address lies in the negative portion of the address space
+ * (i.e. has the MSB set).
+ *
+ * For x86_64-pcbios, we identity-map the low 4GB of address space
+ * since the only accesses required above 4GB are for MMIO (typically
+ * PCI devices with large memory BARs).
+ *
+ * For riscv64-sbi, we identity-map as much of the physical address
+ * space as can be mapped by the paging model (Sv39, Sv48, or Sv57).
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#ifdef UACCESS_OFFSET
+#define UACCESS_PREFIX_offset
+#else
+#define UACCESS_PREFIX_offset __offset_
+#endif
+
+/** Virtual address offset
+ *
+ * This is defined to be the value to be added to an address within
+ * iPXE's own image in order to obtain its physical address, as
+ * described above.
+ *
+ * Note that if iPXE's image is not yet writable (i.e. during early
+ * startup, prior to physical relocation), then this value may not yet
+ * be valid. Under these circumstances, callers must use
+ * offset_phys_to_virt() and offset_virt_to_phys() instead (and so
+ * provide the virtual address offset as a function parameter).
+ */
+extern const unsigned long virt_offset;
+
+/**
+ * Convert physical address to virtual address
+ *
+ * @v phys Physical address
+ * @v offset Virtual address offset
+ * @ret virt Virtual address
+ */
+static inline __always_inline void *
+offset_phys_to_virt ( unsigned long phys, unsigned long offset ) {
+
+ /* In a 64-bit build, any valid physical address is directly
+ * usable as a virtual address, since physical addresses are
+ * identity-mapped.
+ */
+ if ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) )
+ return ( ( void * ) phys );
+
+ /* In a 32-bit build: subtract virt_offset */
+ return ( ( void * ) ( phys - offset ) );
+}
+
+/**
+ * Convert virtual address to physical address
+ *
+ * @v virt Virtual address
+ * @v offset Virtual address offset
+ * @ret phys Physical address
+ */
+static inline __always_inline physaddr_t
+offset_virt_to_phys ( volatile const void *virt, unsigned long offset ) {
+ physaddr_t addr = ( ( physaddr_t ) virt );
+
+ /* In a 64-bit build, any valid virtual address with the MSB
+ * clear is directly usable as a physical address, since it
+ * must lie within the identity-mapped portion.
+ *
+ * This test will typically reduce to a single "branch if less
+ * than zero" instruction.
+ */
+ if ( ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) ) &&
+ ( ! ( addr & ( 1ULL << ( 8 * sizeof ( physaddr_t ) - 1 ) ) ) ) ) {
+ return addr;
+ }
+
+ /* In a 32-bit build or in a 64-bit build with a virtual
+ * address with the MSB set: add virt_offset
+ */
+ return ( addr + offset );
+}
+
+/**
+ * Convert physical address to virtual address
+ *
+ * @v phys Physical address
+ * @ret virt Virtual address
+ */
+static inline __always_inline void *
+UACCESS_INLINE ( offset, phys_to_virt ) ( unsigned long phys ) {
+
+ return offset_phys_to_virt ( phys, virt_offset );
+}
+
+/**
+ * Convert virtual address to physical address
+ *
+ * @v virt Virtual address
+ * @ret phys Physical address
+ */
+static inline __always_inline physaddr_t
+UACCESS_INLINE ( offset, virt_to_phys ) ( volatile const void *virt ) {
+
+ return offset_virt_to_phys ( virt, virt_offset );
+}
+
+#endif /* _IPXE_VIRT_OFFSET_H */