summaryrefslogtreecommitdiffstats
path: root/src/include/ipxe/virt_offset.h
blob: 31c434fc596eaf40c7706faad5fd9862ad404071 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
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 */