summaryrefslogtreecommitdiffstats
path: root/src/include/ipxe/virt_offset.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/include/ipxe/virt_offset.h')
-rw-r--r--src/include/ipxe/virt_offset.h132
1 files changed, 132 insertions, 0 deletions
diff --git a/src/include/ipxe/virt_offset.h b/src/include/ipxe/virt_offset.h
new file mode 100644
index 000000000..31c434fc5
--- /dev/null
+++ b/src/include/ipxe/virt_offset.h
@@ -0,0 +1,132 @@
+#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, place iPXE within the 32-bit physical
+ * address space, map iPXE using virtual addresses in the top part
+ * of the negative (kernel) address space, and optionally map the
+ * 32-bit physical address space with attributes suitable for
+ * coherent DMA accesses.
+ *
+ * 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 high negative portion of the address space
+ * (i.e. has the MSB set, but has the MSB clear after adding
+ * virt_offset).
+ *
+ * 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)
+ * and create a coherent DMA mapping of the low 4GB.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+FILE_SECBOOT ( PERMITTED );
+
+#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.
+ */
+extern const unsigned long virt_offset;
+
+/** Allow for architecture-specific overrides of virt_offset */
+#include <bits/virt_offset.h>
+
+/**
+ * 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 ) {
+
+ /* 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 - 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 ) {
+ const physaddr_t msb = ( 1ULL << ( 8 * sizeof ( physaddr_t ) - 1 ) );
+ 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 & msb ) ) ) {
+ return addr;
+ }
+
+ /* In a 32-bit build or in a 64-bit build with a virtual
+ * address with the MSB set: add virt_offset
+ */
+ addr += virt_offset;
+
+ /* In a 64-bit build with an address that still has the MSB
+ * set after adding virt_offset: truncate the original virtual
+ * address to form a 32-bit physical address.
+ *
+ * This test will also typically reduce to a single "branch if
+ * less than zero" instruction.
+ */
+ if ( ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) ) &&
+ ( addr & msb ) ) {
+ return ( ( uint32_t ) ( physaddr_t ) virt );
+ }
+
+ return addr;
+}
+
+#endif /* _IPXE_VIRT_OFFSET_H */