| #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 */ |