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