]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[uaccess] Generalise librm's virt_offset mechanism for RISC-V
authorMichael Brown <mcb30@ipxe.org>
Wed, 7 May 2025 22:02:40 +0000 (23:02 +0100)
committerMichael Brown <mcb30@ipxe.org>
Wed, 7 May 2025 23:12:33 +0000 (00:12 +0100)
The virtual offset memory model used for i386-pcbios and x86_64-pcbios
can be generalised to also cover riscv32-sbi and riscv64-sbi.  In both
architectures, the 32-bit builds will use a circular map of the 32-bit
address space, and the 64-bit builds will use an identity map for the
relevant portion of the physical address space, with iPXE itself
placed in the negative (kernel) address space.

Generalise and document the virt_offset mechanism, and set it as the
default for both PCBIOS and SBI platforms.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
12 files changed:
src/arch/riscv/prefix/libprefix.S
src/arch/x86/image/elfboot.c
src/arch/x86/include/bits/uaccess.h [deleted file]
src/arch/x86/include/librm.h
src/arch/x86/include/realmode.h
src/arch/x86/interface/pcbios/bios_mp.c
src/arch/x86/transitions/librm_mgmt.c
src/config/defaults/pcbios.h
src/config/defaults/sbi.h
src/core/virt_offset.c [new file with mode: 0644]
src/include/ipxe/uaccess.h
src/include/ipxe/virt_offset.h [new file with mode: 0644]

index 7f4e2bc98710d4c9689b4fd9413a5218cd3627b9..c43a2b1b2696c54599d1e431c2a7367a12815c32 100644 (file)
@@ -41,6 +41,13 @@ prefix_virt:
        .dword  _prefix
        .size   prefix_virt, . - prefix_virt
 
+       /* Current virtual address offset */
+       .section ".data.virt_offset", "aw", @progbits
+       .globl  virt_offset
+virt_offset:
+       .space  ( __riscv_xlen / 8 )
+       .size   virt_offset, . - virt_offset
+
 /*****************************************************************************
  *
  * Print message to debug console
index 7f89e8b657064f3bed0dd07d0a3370870e04f83e..d0f91d1c03ce53a3b0e739d2626684b8ad083395 100644 (file)
@@ -26,11 +26,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 #include <string.h>
 #include <errno.h>
 #include <elf.h>
+#include <librm.h>
 #include <ipxe/image.h>
 #include <ipxe/elf.h>
 #include <ipxe/features.h>
 #include <ipxe/init.h>
-#include <ipxe/uaccess.h>
 
 /**
  * @file
diff --git a/src/arch/x86/include/bits/uaccess.h b/src/arch/x86/include/bits/uaccess.h
deleted file mode 100644 (file)
index e9e7e5a..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef _BITS_UACCESS_H
-#define _BITS_UACCESS_H
-
-/** @file
- *
- * x86-specific user access API implementations
- *
- */
-
-FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
-
-#include <librm.h>
-
-#endif /* _BITS_UACCESS_H */
index 4fce7e8c801ea24594dfc3a6cbfa4f6d415e62ed..cbe7d7134909904b256722bf927f3794fe435c73 100644 (file)
@@ -64,12 +64,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 
 #else /* ASSEMBLY */
 
-#ifdef UACCESS_LIBRM
-#define UACCESS_PREFIX_librm
-#else
-#define UACCESS_PREFIX_librm __librm_
-#endif
-
 /**
  * Call C function from real-mode code
  *
@@ -79,53 +73,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
        "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
index 5cf644a2386081a8fd34b9dea58a855b467d1920..7baec56caf5e5f992827b3b760b5a2d9247ed4c6 100644 (file)
@@ -4,6 +4,7 @@
 #include <stdint.h>
 #include <string.h>
 #include <registers.h>
+#include <librm.h>
 #include <ipxe/uaccess.h>
 
 /*
index c903a81fc804e5aee1b46c5ac8001b4022f5a9ca..4525a60cf0a3d07272025abba2ef8ee6dec87704 100644 (file)
@@ -30,6 +30,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
  */
 
 #include <registers.h>
+#include <librm.h>
 #include <ipxe/uaccess.h>
 #include <ipxe/timer.h>
 #include <ipxe/msr.h>
index 14e00eda87c9b0135af261ecf162b2a241f66309..0328cb2a5111f980ba41c86f28278ff009e60aa0 100644 (file)
@@ -429,8 +429,6 @@ void setup_sipi ( unsigned int vector, uint32_t handler,
        copy_to_real ( ( vector << 8 ), 0, sipi, ( ( size_t ) sipi_len ) );
 }
 
-PROVIDE_UACCESS_INLINE ( librm, phys_to_virt );
-PROVIDE_UACCESS_INLINE ( librm, virt_to_phys );
 PROVIDE_IOMAP_INLINE ( pages, io_to_bus );
 PROVIDE_IOMAP ( pages, ioremap, ioremap_pages );
 PROVIDE_IOMAP ( pages, iounmap, iounmap_pages );
index fa12a1005842f32d04d96716b85b22a3dd9fc697..13532e429f51655c70ec17d9e21e884f33366c9f 100644 (file)
@@ -9,7 +9,7 @@
 
 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 
-#define UACCESS_LIBRM
+#define UACCESS_OFFSET
 #define IOAPI_X86
 #define PCIAPI_PCBIOS
 #define DMAAPI_FLAT
index 27fbe0e80404b662c455bbb2da1ce54d993fe4c6..268bf0afee10a4ec7b607f13576b4d7e9e2c99a3 100644 (file)
@@ -12,7 +12,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 #define IOAPI_RISCV
 #define IOMAP_VIRT
 #define DMAAPI_FLAT
-#define UACCESS_FLAT
+#define UACCESS_OFFSET
 #define TIMER_ZICNTR
 #define ENTROPY_ZKR
 
diff --git a/src/core/virt_offset.c b/src/core/virt_offset.c
new file mode 100644 (file)
index 0000000..23862d9
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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 );
index 8d93cbf4fba2d8106daacb496bcf51f021036fa9..d97db95be1ed15b383879af0e959b5ede4e559d3 100644 (file)
@@ -60,6 +60,7 @@ UACCESS_INLINE ( flat, virt_to_phys ) ( volatile const void *virt ) {
 }
 
 /* Include all architecture-independent user access API headers */
+#include <ipxe/virt_offset.h>
 #include <ipxe/linux/linux_uaccess.h>
 
 /* Include all architecture-dependent user access API headers */
diff --git a/src/include/ipxe/virt_offset.h b/src/include/ipxe/virt_offset.h
new file mode 100644 (file)
index 0000000..f8f9027
--- /dev/null
@@ -0,0 +1,140 @@
+#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 */