Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/lib/libefivar/uefi-dputil.c
39475 views
1
/*-
2
* Copyright (c) 2017 Netflix, Inc.
3
*
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions
6
* are met:
7
* 1. Redistributions of source code must retain the above copyright
8
* notice, this list of conditions and the following disclaimer.
9
* 2. Redistributions in binary form must reproduce the above copyright
10
* notice, this list of conditions and the following disclaimer in the
11
* documentation and/or other materials provided with the distribution.
12
*
13
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23
* SUCH DAMAGE.
24
*/
25
26
/*
27
* Routines to format EFI_DEVICE_PATHs from the UEFI standard. Much of
28
* this file is taken from EDK2 and rototilled.
29
*/
30
31
#include <efivar.h>
32
#include <limits.h>
33
#include <stdio.h>
34
#include <string.h>
35
#include <sys/endian.h>
36
37
#include "efi-osdep.h"
38
39
#include "uefi-dplib.h"
40
41
/* XXX maybe I should include the entire DevicePathUtiltiies.c and ifdef out what we don't use */
42
43
/*
44
* Taken from MdePkg/Library/UefiDevicePathLib/DevicePathUtilities.c
45
* hash a11928f3310518ab1c6fd34e8d0fdbb72de9602c 2017-Mar-01
46
*/
47
48
/** @file
49
Device Path services. The thing to remember is device paths are built out of
50
nodes. The device path is terminated by an end node that is length
51
sizeof(EFI_DEVICE_PATH_PROTOCOL). That would be why there is sizeof(EFI_DEVICE_PATH_PROTOCOL)
52
all over this file.
53
54
The only place where multi-instance device paths are supported is in
55
environment varibles. Multi-instance device paths should never be placed
56
on a Handle.
57
58
Copyright (c) 2006 - 2016, Intel Corporation. All rights reserved.<BR>
59
This program and the accompanying materials
60
are licensed and made available under the terms and conditions of the BSD License
61
which accompanies this distribution. The full text of the license may be found at
62
http://opensource.org/licenses/bsd-license.php.
63
64
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
65
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
66
67
**/
68
69
//
70
// Template for an end-of-device path node.
71
//
72
static CONST EFI_DEVICE_PATH_PROTOCOL mUefiDevicePathLibEndDevicePath = {
73
END_DEVICE_PATH_TYPE,
74
END_ENTIRE_DEVICE_PATH_SUBTYPE,
75
{
76
END_DEVICE_PATH_LENGTH,
77
0
78
}
79
};
80
81
82
/**
83
Returns the size of a device path in bytes.
84
85
This function returns the size, in bytes, of the device path data structure
86
specified by DevicePath including the end of device path node.
87
If DevicePath is NULL or invalid, then 0 is returned.
88
89
@param DevicePath A pointer to a device path data structure.
90
91
@retval 0 If DevicePath is NULL or invalid.
92
@retval Others The size of a device path in bytes.
93
94
**/
95
UINTN
96
EFIAPI
97
GetDevicePathSize (
98
IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath
99
)
100
{
101
CONST EFI_DEVICE_PATH_PROTOCOL *Start;
102
103
if (DevicePath == NULL) {
104
return 0;
105
}
106
107
if (!IsDevicePathValid (DevicePath, 0)) {
108
return 0;
109
}
110
111
//
112
// Search for the end of the device path structure
113
//
114
Start = DevicePath;
115
while (!IsDevicePathEnd (DevicePath)) {
116
DevicePath = NextDevicePathNode (DevicePath);
117
}
118
119
//
120
// Compute the size and add back in the size of the end device path structure
121
//
122
return ((UINTN) DevicePath - (UINTN) Start) + DevicePathNodeLength (DevicePath);
123
}
124
125
/**
126
Determine whether a given device path is valid.
127
If DevicePath is NULL, then ASSERT().
128
129
@param DevicePath A pointer to a device path data structure.
130
@param MaxSize The maximum size of the device path data structure.
131
132
@retval TRUE DevicePath is valid.
133
@retval FALSE The length of any node in the DevicePath is less
134
than sizeof (EFI_DEVICE_PATH_PROTOCOL).
135
@retval FALSE If MaxSize is not zero, the size of the DevicePath
136
exceeds MaxSize.
137
@retval FALSE If PcdMaximumDevicePathNodeCount is not zero, the node
138
count of the DevicePath exceeds PcdMaximumDevicePathNodeCount.
139
**/
140
BOOLEAN
141
EFIAPI
142
IsDevicePathValid (
143
IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath,
144
IN UINTN MaxSize
145
)
146
{
147
UINTN Count;
148
UINTN Size;
149
UINTN NodeLength;
150
151
ASSERT (DevicePath != NULL);
152
153
if (MaxSize == 0) {
154
MaxSize = MAX_UINTN;
155
}
156
157
//
158
// Validate the input size big enough to touch the first node.
159
//
160
if (MaxSize < sizeof (EFI_DEVICE_PATH_PROTOCOL)) {
161
return FALSE;
162
}
163
164
for (Count = 0, Size = 0; !IsDevicePathEnd (DevicePath); DevicePath = NextDevicePathNode (DevicePath)) {
165
NodeLength = DevicePathNodeLength (DevicePath);
166
if (NodeLength < sizeof (EFI_DEVICE_PATH_PROTOCOL)) {
167
return FALSE;
168
}
169
170
if (NodeLength > MAX_UINTN - Size) {
171
return FALSE;
172
}
173
Size += NodeLength;
174
175
//
176
// Validate next node before touch it.
177
//
178
if (Size > MaxSize - END_DEVICE_PATH_LENGTH ) {
179
return FALSE;
180
}
181
182
if (PcdGet32 (PcdMaximumDevicePathNodeCount) > 0) {
183
Count++;
184
if (Count >= PcdGet32 (PcdMaximumDevicePathNodeCount)) {
185
return FALSE;
186
}
187
}
188
}
189
190
//
191
// Only return TRUE when the End Device Path node is valid.
192
//
193
return (BOOLEAN) (DevicePathNodeLength (DevicePath) == END_DEVICE_PATH_LENGTH);
194
}
195
196
/**
197
Returns the Type field of a device path node.
198
199
Returns the Type field of the device path node specified by Node.
200
201
If Node is NULL, then ASSERT().
202
203
@param Node A pointer to a device path node data structure.
204
205
@return The Type field of the device path node specified by Node.
206
207
**/
208
UINT8
209
EFIAPI
210
DevicePathType (
211
IN CONST VOID *Node
212
)
213
{
214
ASSERT (Node != NULL);
215
return ((const EFI_DEVICE_PATH_PROTOCOL *)(Node))->Type;
216
}
217
218
219
/**
220
Returns the SubType field of a device path node.
221
222
Returns the SubType field of the device path node specified by Node.
223
224
If Node is NULL, then ASSERT().
225
226
@param Node A pointer to a device path node data structure.
227
228
@return The SubType field of the device path node specified by Node.
229
230
**/
231
UINT8
232
EFIAPI
233
DevicePathSubType (
234
IN CONST VOID *Node
235
)
236
{
237
ASSERT (Node != NULL);
238
return ((const EFI_DEVICE_PATH_PROTOCOL *)(Node))->SubType;
239
}
240
241
/**
242
Returns the 16-bit Length field of a device path node.
243
244
Returns the 16-bit Length field of the device path node specified by Node.
245
Node is not required to be aligned on a 16-bit boundary, so it is recommended
246
that a function such as ReadUnaligned16() be used to extract the contents of
247
the Length field.
248
249
If Node is NULL, then ASSERT().
250
251
@param Node A pointer to a device path node data structure.
252
253
@return The 16-bit Length field of the device path node specified by Node.
254
255
**/
256
UINTN
257
EFIAPI
258
DevicePathNodeLength (
259
IN CONST VOID *Node
260
)
261
{
262
ASSERT (Node != NULL);
263
return ((const EFI_DEVICE_PATH_PROTOCOL *)Node)->Length[0] |
264
(((const EFI_DEVICE_PATH_PROTOCOL *)Node)->Length[1] << 8);
265
}
266
267
/**
268
Returns a pointer to the next node in a device path.
269
270
Returns a pointer to the device path node that follows the device path node
271
specified by Node.
272
273
If Node is NULL, then ASSERT().
274
275
@param Node A pointer to a device path node data structure.
276
277
@return a pointer to the device path node that follows the device path node
278
specified by Node.
279
280
**/
281
EFI_DEVICE_PATH_PROTOCOL *
282
EFIAPI
283
NextDevicePathNode (
284
IN CONST VOID *Node
285
)
286
{
287
ASSERT (Node != NULL);
288
return ((EFI_DEVICE_PATH_PROTOCOL *)(__DECONST(UINT8 *, Node) + DevicePathNodeLength(Node)));
289
}
290
291
/**
292
Determines if a device path node is an end node of a device path.
293
This includes nodes that are the end of a device path instance and nodes that
294
are the end of an entire device path.
295
296
Determines if the device path node specified by Node is an end node of a device path.
297
This includes nodes that are the end of a device path instance and nodes that are the
298
end of an entire device path. If Node represents an end node of a device path,
299
then TRUE is returned. Otherwise, FALSE is returned.
300
301
If Node is NULL, then ASSERT().
302
303
@param Node A pointer to a device path node data structure.
304
305
@retval TRUE The device path node specified by Node is an end node of a
306
device path.
307
@retval FALSE The device path node specified by Node is not an end node of
308
a device path.
309
310
**/
311
BOOLEAN
312
EFIAPI
313
IsDevicePathEndType (
314
IN CONST VOID *Node
315
)
316
{
317
ASSERT (Node != NULL);
318
return (BOOLEAN) (DevicePathType (Node) == END_DEVICE_PATH_TYPE);
319
}
320
321
/**
322
Determines if a device path node is an end node of an entire device path.
323
324
Determines if a device path node specified by Node is an end node of an entire
325
device path. If Node represents the end of an entire device path, then TRUE is
326
returned. Otherwise, FALSE is returned.
327
328
If Node is NULL, then ASSERT().
329
330
@param Node A pointer to a device path node data structure.
331
332
@retval TRUE The device path node specified by Node is the end of an entire
333
device path.
334
@retval FALSE The device path node specified by Node is not the end of an
335
entire device path.
336
337
**/
338
BOOLEAN
339
EFIAPI
340
IsDevicePathEnd (
341
IN CONST VOID *Node
342
)
343
{
344
ASSERT (Node != NULL);
345
return (BOOLEAN) (IsDevicePathEndType (Node) && DevicePathSubType(Node) == END_ENTIRE_DEVICE_PATH_SUBTYPE);
346
}
347
348
/**
349
Fills in all the fields of a device path node that is the end of an entire device path.
350
351
Fills in all the fields of a device path node specified by Node so Node represents
352
the end of an entire device path. The Type field of Node is set to
353
END_DEVICE_PATH_TYPE, the SubType field of Node is set to
354
END_ENTIRE_DEVICE_PATH_SUBTYPE, and the Length field of Node is set to
355
END_DEVICE_PATH_LENGTH. Node is not required to be aligned on a 16-bit boundary,
356
so it is recommended that a function such as WriteUnaligned16() be used to set
357
the contents of the Length field.
358
359
If Node is NULL, then ASSERT().
360
361
@param Node A pointer to a device path node data structure.
362
363
**/
364
VOID
365
EFIAPI
366
SetDevicePathEndNode (
367
OUT VOID *Node
368
)
369
{
370
ASSERT (Node != NULL);
371
memcpy (Node, &mUefiDevicePathLibEndDevicePath, sizeof (mUefiDevicePathLibEndDevicePath));
372
}
373
374
/**
375
Sets the length, in bytes, of a device path node.
376
377
Sets the length of the device path node specified by Node to the value specified
378
by NodeLength. NodeLength is returned. Node is not required to be aligned on
379
a 16-bit boundary, so it is recommended that a function such as WriteUnaligned16()
380
be used to set the contents of the Length field.
381
382
If Node is NULL, then ASSERT().
383
If NodeLength >= SIZE_64KB, then ASSERT().
384
If NodeLength < sizeof (EFI_DEVICE_PATH_PROTOCOL), then ASSERT().
385
386
@param Node A pointer to a device path node data structure.
387
@param Length The length, in bytes, of the device path node.
388
389
@return Length
390
391
**/
392
UINT16
393
EFIAPI
394
SetDevicePathNodeLength (
395
IN OUT VOID *Node,
396
IN UINTN Length
397
)
398
{
399
ASSERT (Node != NULL);
400
ASSERT ((Length >= sizeof (EFI_DEVICE_PATH_PROTOCOL)) && (Length < SIZE_64KB));
401
// return WriteUnaligned16 ((UINT16 *)&((EFI_DEVICE_PATH_PROTOCOL *)(Node))->Length[0], (UINT16)(Length));
402
le16enc(&((EFI_DEVICE_PATH_PROTOCOL *)(Node))->Length[0], (UINT16)(Length));
403
return Length;
404
}
405
406
/**
407
Creates a device node.
408
409
This function creates a new device node in a newly allocated buffer of size
410
NodeLength and initializes the device path node header with NodeType and NodeSubType.
411
The new device path node is returned.
412
If NodeLength is smaller than a device path header, then NULL is returned.
413
If there is not enough memory to allocate space for the new device path, then
414
NULL is returned.
415
The memory is allocated from EFI boot services memory. It is the responsibility
416
of the caller to free the memory allocated.
417
418
@param NodeType The device node type for the new device node.
419
@param NodeSubType The device node sub-type for the new device node.
420
@param NodeLength The length of the new device node.
421
422
@return The new device path.
423
424
**/
425
EFI_DEVICE_PATH_PROTOCOL *
426
EFIAPI
427
CreateDeviceNode (
428
IN UINT8 NodeType,
429
IN UINT8 NodeSubType,
430
IN UINT16 NodeLength
431
)
432
{
433
EFI_DEVICE_PATH_PROTOCOL *DevicePath;
434
435
if (NodeLength < sizeof (EFI_DEVICE_PATH_PROTOCOL)) {
436
//
437
// NodeLength is less than the size of the header.
438
//
439
return NULL;
440
}
441
442
DevicePath = AllocateZeroPool (NodeLength);
443
if (DevicePath != NULL) {
444
DevicePath->Type = NodeType;
445
DevicePath->SubType = NodeSubType;
446
SetDevicePathNodeLength (DevicePath, NodeLength);
447
}
448
449
return DevicePath;
450
}
451
452
/**
453
Creates a new copy of an existing device path.
454
455
This function allocates space for a new copy of the device path specified by DevicePath.
456
If DevicePath is NULL, then NULL is returned. If the memory is successfully
457
allocated, then the contents of DevicePath are copied to the newly allocated
458
buffer, and a pointer to that buffer is returned. Otherwise, NULL is returned.
459
The memory for the new device path is allocated from EFI boot services memory.
460
It is the responsibility of the caller to free the memory allocated.
461
462
@param DevicePath A pointer to a device path data structure.
463
464
@retval NULL DevicePath is NULL or invalid.
465
@retval Others A pointer to the duplicated device path.
466
467
**/
468
EFI_DEVICE_PATH_PROTOCOL *
469
EFIAPI
470
DuplicateDevicePath (
471
IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath
472
)
473
{
474
UINTN Size;
475
476
//
477
// Compute the size
478
//
479
Size = GetDevicePathSize (DevicePath);
480
if (Size == 0) {
481
return NULL;
482
}
483
484
//
485
// Allocate space for duplicate device path
486
//
487
488
return AllocateCopyPool (Size, DevicePath);
489
}
490
491
/**
492
Creates a new device path by appending a second device path to a first device path.
493
494
This function creates a new device path by appending a copy of SecondDevicePath
495
to a copy of FirstDevicePath in a newly allocated buffer. Only the end-of-device-path
496
device node from SecondDevicePath is retained. The newly created device path is
497
returned. If FirstDevicePath is NULL, then it is ignored, and a duplicate of
498
SecondDevicePath is returned. If SecondDevicePath is NULL, then it is ignored,
499
and a duplicate of FirstDevicePath is returned. If both FirstDevicePath and
500
SecondDevicePath are NULL, then a copy of an end-of-device-path is returned.
501
502
If there is not enough memory for the newly allocated buffer, then NULL is returned.
503
The memory for the new device path is allocated from EFI boot services memory.
504
It is the responsibility of the caller to free the memory allocated.
505
506
@param FirstDevicePath A pointer to a device path data structure.
507
@param SecondDevicePath A pointer to a device path data structure.
508
509
@retval NULL If there is not enough memory for the newly allocated buffer.
510
@retval NULL If FirstDevicePath or SecondDevicePath is invalid.
511
@retval Others A pointer to the new device path if success.
512
Or a copy an end-of-device-path if both FirstDevicePath and SecondDevicePath are NULL.
513
514
**/
515
EFI_DEVICE_PATH_PROTOCOL *
516
EFIAPI
517
AppendDevicePath (
518
IN CONST EFI_DEVICE_PATH_PROTOCOL *FirstDevicePath, OPTIONAL
519
IN CONST EFI_DEVICE_PATH_PROTOCOL *SecondDevicePath OPTIONAL
520
)
521
{
522
UINTN Size;
523
UINTN Size1;
524
UINTN Size2;
525
EFI_DEVICE_PATH_PROTOCOL *NewDevicePath;
526
EFI_DEVICE_PATH_PROTOCOL *DevicePath2;
527
528
//
529
// If there's only 1 path, just duplicate it.
530
//
531
if (FirstDevicePath == NULL) {
532
return DuplicateDevicePath ((SecondDevicePath != NULL) ? SecondDevicePath : &mUefiDevicePathLibEndDevicePath);
533
}
534
535
if (SecondDevicePath == NULL) {
536
return DuplicateDevicePath (FirstDevicePath);
537
}
538
539
if (!IsDevicePathValid (FirstDevicePath, 0) || !IsDevicePathValid (SecondDevicePath, 0)) {
540
return NULL;
541
}
542
543
//
544
// Allocate space for the combined device path. It only has one end node of
545
// length EFI_DEVICE_PATH_PROTOCOL.
546
//
547
Size1 = GetDevicePathSize (FirstDevicePath);
548
Size2 = GetDevicePathSize (SecondDevicePath);
549
Size = Size1 + Size2 - END_DEVICE_PATH_LENGTH;
550
551
NewDevicePath = AllocatePool (Size);
552
553
if (NewDevicePath != NULL) {
554
NewDevicePath = CopyMem (NewDevicePath, FirstDevicePath, Size1);
555
//
556
// Over write FirstDevicePath EndNode and do the copy
557
//
558
DevicePath2 = (EFI_DEVICE_PATH_PROTOCOL *) ((CHAR8 *) NewDevicePath +
559
(Size1 - END_DEVICE_PATH_LENGTH));
560
CopyMem (DevicePath2, SecondDevicePath, Size2);
561
}
562
563
return NewDevicePath;
564
}
565
566
/**
567
Creates a new path by appending the device node to the device path.
568
569
This function creates a new device path by appending a copy of the device node
570
specified by DevicePathNode to a copy of the device path specified by DevicePath
571
in an allocated buffer. The end-of-device-path device node is moved after the
572
end of the appended device node.
573
If DevicePathNode is NULL then a copy of DevicePath is returned.
574
If DevicePath is NULL then a copy of DevicePathNode, followed by an end-of-device
575
path device node is returned.
576
If both DevicePathNode and DevicePath are NULL then a copy of an end-of-device-path
577
device node is returned.
578
If there is not enough memory to allocate space for the new device path, then
579
NULL is returned.
580
The memory is allocated from EFI boot services memory. It is the responsibility
581
of the caller to free the memory allocated.
582
583
@param DevicePath A pointer to a device path data structure.
584
@param DevicePathNode A pointer to a single device path node.
585
586
@retval NULL If there is not enough memory for the new device path.
587
@retval Others A pointer to the new device path if success.
588
A copy of DevicePathNode followed by an end-of-device-path node
589
if both FirstDevicePath and SecondDevicePath are NULL.
590
A copy of an end-of-device-path node if both FirstDevicePath
591
and SecondDevicePath are NULL.
592
593
**/
594
EFI_DEVICE_PATH_PROTOCOL *
595
EFIAPI
596
AppendDevicePathNode (
597
IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePath, OPTIONAL
598
IN CONST EFI_DEVICE_PATH_PROTOCOL *DevicePathNode OPTIONAL
599
)
600
{
601
EFI_DEVICE_PATH_PROTOCOL *TempDevicePath;
602
EFI_DEVICE_PATH_PROTOCOL *NextNode;
603
EFI_DEVICE_PATH_PROTOCOL *NewDevicePath;
604
UINTN NodeLength;
605
606
if (DevicePathNode == NULL) {
607
return DuplicateDevicePath ((DevicePath != NULL) ? DevicePath : &mUefiDevicePathLibEndDevicePath);
608
}
609
//
610
// Build a Node that has a terminator on it
611
//
612
NodeLength = DevicePathNodeLength (DevicePathNode);
613
614
TempDevicePath = AllocatePool (NodeLength + END_DEVICE_PATH_LENGTH);
615
if (TempDevicePath == NULL) {
616
return NULL;
617
}
618
TempDevicePath = CopyMem (TempDevicePath, DevicePathNode, NodeLength);
619
//
620
// Add and end device path node to convert Node to device path
621
//
622
NextNode = NextDevicePathNode (TempDevicePath);
623
SetDevicePathEndNode (NextNode);
624
//
625
// Append device paths
626
//
627
NewDevicePath = AppendDevicePath (DevicePath, TempDevicePath);
628
629
FreePool (TempDevicePath);
630
631
return NewDevicePath;
632
}
633
634