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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
|
/** @file
Helper library to map mmio memory regions.
Copyright (c) 2026, Arm Ltd. All rights reserved.<BR>
SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Base.h>
#include <Uefi.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/DxeServicesTableLib.h>
#include <Library/UefiBootServicesTableLib.h>
STATIC EFI_EVENT mExitBootServicesEvent;
STATIC BOOLEAN mAtRuntime = FALSE;
/**
Ensure a range is present in the GCD memory space map as MMIO.
The input range must already be page-aligned. The function walks the current
GCD memory space map and adds every overlapping EfiGcdMemoryTypeNonExistent
descriptor as EfiGcdMemoryTypeMemoryMappedIo with the requested attributes.
Existing EfiGcdMemoryTypeMemoryMappedIo descriptors are accepted only when
their capabilities contain all requested attributes. Existing descriptors of
any other type, or MMIO descriptors without the requested attributes, are
treated as conflicts.
This function only ensures that MMIO GCD descriptors exist. It does not set
memory space attributes.
@param[in] Base The page-aligned base address of the MMIO range.
@param[in] Length The page-aligned size of the MMIO range, in bytes.
@param[in] Attributes The GCD memory space attributes required for the MMIO
range.
@retval EFI_SUCCESS The range is backed by compatible MMIO descriptors.
@retval EFI_UNSUPPORTED The range overlaps an existing non-MMIO descriptor,
or an MMIO descriptor without the requested
attributes.
@retval EFI_ABORTED An existing GCD descriptor is malformed.
@retval Others The GCD memory services returned an error.
**/
STATIC
EFI_STATUS
AddMmioMemorySpace (
IN UINT64 Base,
IN UINT64 Length,
IN UINT64 Attributes
)
{
EFI_STATUS Status;
UINTN Index;
UINTN NumberOfDescriptors;
EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap;
EFI_GCD_MEMORY_SPACE_DESCRIPTOR *Descriptor;
UINT64 IntersectionBase;
UINT64 IntersectionEnd;
Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap);
if (EFI_ERROR (Status)) {
return Status;
}
for (Index = 0; Index < NumberOfDescriptors; Index++) {
Descriptor = &MemorySpaceMap[Index];
if (Descriptor->BaseAddress > (MAX_UINT64 - Descriptor->Length)) {
Status = EFI_ABORTED;
break;
}
IntersectionBase = MAX (Base, Descriptor->BaseAddress);
IntersectionEnd = MIN (
Base + Length,
Descriptor->BaseAddress + Descriptor->Length
);
if (IntersectionBase >= IntersectionEnd) {
//
// The descriptor and the aperture don't overlap.
//
continue;
}
if (Descriptor->GcdMemoryType == EfiGcdMemoryTypeNonExistent) {
Status = gDS->AddMemorySpace (
EfiGcdMemoryTypeMemoryMappedIo,
IntersectionBase,
IntersectionEnd - IntersectionBase,
Attributes
);
DEBUG ((
EFI_ERROR (Status) ? DEBUG_ERROR : DEBUG_VERBOSE,
"%a: %a: add [%Lx, %Lx): %r\n",
gEfiCallerBaseName,
__func__,
IntersectionBase,
IntersectionEnd,
Status
));
if (EFI_ERROR (Status)) {
break;
}
continue;
}
if ((Descriptor->GcdMemoryType != EfiGcdMemoryTypeMemoryMappedIo) ||
((Descriptor->Capabilities & Attributes) != Attributes))
{
Status = EFI_UNSUPPORTED;
break;
}
} // for
FreePool (MemorySpaceMap);
return Status;
}
/**
Map a range as MMIO in the GCD memory map.
The requested range is expanded to page boundaries before it is processed.
Missing GCD memory space descriptors are added as
EfiGcdMemoryTypeMemoryMappedIo. Existing MMIO descriptors are accepted only
when their capabilities contain the requested attributes. Existing descriptors
of any other type are treated as conflicts.
After the range is backed by compatible MMIO descriptors, the requested GCD
memory space attributes are applied to the normalized full range.
If this function fails after adding new GCD MMIO descriptors, the descriptors
are not rolled back. Callers are expected to treat failures from this function
as fatal to the current boot path.
@param[in] Base The base address of the requested MMIO range.
@param[in] Length The size of the requested MMIO range, in bytes.
@param[in] Attributes The GCD memory space attributes to apply to the MMIO
range.
@retval EFI_SUCCESS The full range was mapped as MMIO and
configured with the requested attributes.
@retval EFI_INVALID_PARAMETER Length is zero, or the normalized range
overflows the physical address space.
@retval EFI_UNSUPPORTED The range overlaps an existing non-MMIO
descriptor, or an MMIO descriptor without the
requested attributes.
@retval EFI_ABORTED An existing GCD descriptor is malformed.
@retval EFI_ACCESS_DENIED DXE Services are no longer available.
@retval Others The GCD memory services returned an error.
**/
EFI_STATUS
EFIAPI
MapMmioMemory (
IN EFI_PHYSICAL_ADDRESS Base,
IN UINT64 Length,
IN UINT64 Attributes
)
{
EFI_STATUS Status;
EFI_PHYSICAL_ADDRESS RegionEnd;
if (mAtRuntime) {
//
// DXE Services are no longer available.
//
return EFI_ACCESS_DENIED;
}
DEBUG ((
DEBUG_INFO,
"Map MMIO Memory: 0x%08lx - 0x%08lx : 0x%08lx\n",
Base,
Length,
Attributes
));
if (Length == 0) {
return EFI_INVALID_PARAMETER;
}
// Check if RegionsBase + Length would overflow
if ((Base > (MAX_UINT64 - Length))) {
return EFI_INVALID_PARAMETER;
}
RegionEnd = Base + Length;
// Check if aligning RegionEnd would overflow
if (RegionEnd > MAX_UINT64 - ALIGN_VALUE_ADDEND (RegionEnd, EFI_PAGE_SIZE)) {
return EFI_INVALID_PARAMETER;
}
RegionEnd = ALIGN_VALUE (RegionEnd, EFI_PAGE_SIZE);
// Align down Base to page boundary
Base = Base & ~(EFI_PAGE_SIZE - 1);
// Calculate the total region size.
Length = RegionEnd - Base;
Status = AddMmioMemorySpace (Base, Length, Attributes);
if (EFI_ERROR (Status)) {
return Status;
}
return gDS->SetMemorySpaceAttributes (Base, Length, Attributes);
}
/**
Notification function signaled when ExitBootServices() is called.
Record that DXE services are no longer available. MapMmioMemory() uses this
state to reject calls after ExitBootServices().
@param[in] Event Event whose notification function is being invoked.
@param[in] Context Pointer to the notification function's context.
**/
STATIC
VOID
EFIAPI
MapMmioLibExitBootServicesNotify (
IN EFI_EVENT Event,
IN VOID *Context
)
{
mAtRuntime = TRUE;
}
/**
Library instance destructor.
Close the ExitBootServices event created by the constructor.
@param[in] ImageHandle The firmware allocated handle for the EFI image.
@param[in] SystemTable A pointer to the EFI System Table.
@retval EFI_SUCCESS The ExitBootServices event was closed.
@retval Others Failed to close the ExitBootServices event.
**/
EFI_STATUS
EFIAPI
MapMmioLibDestructor (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
return gBS->CloseEvent (mExitBootServicesEvent);
}
/**
Library instance constructor.
Register for ExitBootServices() notification so MapMmioLib can detect when
DXE services are no longer available.
@param[in] ImageHandle The firmware allocated handle for the EFI image.
@param[in] SystemTable A pointer to the EFI System Table.
@retval EFI_SUCCESS The ExitBootServices event was registered.
@retval Others Failed to register the ExitBootServices event.
**/
EFI_STATUS
EFIAPI
MapMmioLibConstructor (
IN EFI_HANDLE ImageHandle,
IN EFI_SYSTEM_TABLE *SystemTable
)
{
return gBS->CreateEventEx (
EVT_NOTIFY_SIGNAL,
TPL_CALLBACK,
MapMmioLibExitBootServicesNotify,
NULL,
&gEfiEventExitBootServicesGuid,
&mExitBootServicesEvent
);
}
|