Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/stand/efi/libefi/efipart.c
105645 views
1
/*-
2
* Copyright (c) 2010 Marcel Moolenaar
3
* All rights reserved.
4
*
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
7
* are met:
8
* 1. Redistributions of source code must retain the above copyright
9
* notice, this list of conditions and the following disclaimer.
10
* 2. Redistributions in binary form must reproduce the above copyright
11
* notice, this list of conditions and the following disclaimer in the
12
* documentation and/or other materials provided with the distribution.
13
*
14
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
* SUCH DAMAGE.
25
*/
26
27
#include <sys/disk.h>
28
#include <sys/param.h>
29
#include <sys/time.h>
30
#include <sys/queue.h>
31
#include <stddef.h>
32
#include <stdarg.h>
33
34
#include <bootstrap.h>
35
36
#include <efi.h>
37
#include <efilib.h>
38
#include <efichar.h>
39
#include <Protocol/DevicePath.h>
40
#include <Protocol/BlockIo.h>
41
#include <disk.h>
42
43
static EFI_GUID blkio_guid = BLOCK_IO_PROTOCOL;
44
45
typedef bool (*pd_test_cb_t)(pdinfo_t *, pdinfo_t *);
46
static int efipart_initfd(void);
47
static int efipart_initcd(void);
48
static int efipart_inithd(void);
49
static void efipart_cdinfo_add(pdinfo_t *);
50
51
static int efipart_strategy(void *, int, daddr_t, size_t, char *, size_t *);
52
static int efipart_realstrategy(void *, int, daddr_t, size_t, char *, size_t *);
53
54
static int efipart_open(struct open_file *, ...);
55
static int efipart_close(struct open_file *);
56
static int efipart_ioctl(struct open_file *, u_long, void *);
57
58
static int efipart_printfd(int);
59
static int efipart_printcd(int);
60
static int efipart_printhd(int);
61
62
/* EISA PNP ID's for floppy controllers */
63
#define PNP0604 0x604
64
#define PNP0700 0x700
65
#define PNP0701 0x701
66
67
/* Bounce buffer max size */
68
#define BIO_BUFFER_SIZE 0x4000
69
70
struct devsw efipart_fddev = {
71
.dv_name = "fd",
72
.dv_type = DEVT_FD,
73
.dv_init = efipart_initfd,
74
.dv_strategy = efipart_strategy,
75
.dv_open = efipart_open,
76
.dv_close = efipart_close,
77
.dv_ioctl = efipart_ioctl,
78
.dv_print = efipart_printfd,
79
.dv_cleanup = nullsys,
80
};
81
82
struct devsw efipart_cddev = {
83
.dv_name = "cd",
84
.dv_type = DEVT_CD,
85
.dv_init = efipart_initcd,
86
.dv_strategy = efipart_strategy,
87
.dv_open = efipart_open,
88
.dv_close = efipart_close,
89
.dv_ioctl = efipart_ioctl,
90
.dv_print = efipart_printcd,
91
.dv_cleanup = nullsys,
92
};
93
94
struct devsw efipart_hddev = {
95
.dv_name = "disk",
96
.dv_type = DEVT_DISK,
97
.dv_init = efipart_inithd,
98
.dv_strategy = efipart_strategy,
99
.dv_open = efipart_open,
100
.dv_close = efipart_close,
101
.dv_ioctl = efipart_ioctl,
102
.dv_print = efipart_printhd,
103
.dv_cleanup = nullsys,
104
.dv_fmtdev = disk_fmtdev,
105
.dv_parsedev = disk_parsedev,
106
};
107
108
static pdinfo_list_t fdinfo = STAILQ_HEAD_INITIALIZER(fdinfo);
109
static pdinfo_list_t cdinfo = STAILQ_HEAD_INITIALIZER(cdinfo);
110
static pdinfo_list_t hdinfo = STAILQ_HEAD_INITIALIZER(hdinfo);
111
112
/*
113
* efipart_inithandles() is used to build up the pdinfo list from
114
* block device handles. Then each devsw init callback is used to
115
* pick items from pdinfo and move to proper device list.
116
* In ideal world, we should end up with empty pdinfo once all
117
* devsw initializers are called.
118
*/
119
static pdinfo_list_t pdinfo = STAILQ_HEAD_INITIALIZER(pdinfo);
120
121
pdinfo_list_t *
122
efiblk_get_pdinfo_list(struct devsw *dev)
123
{
124
if (dev->dv_type == DEVT_DISK)
125
return (&hdinfo);
126
if (dev->dv_type == DEVT_CD)
127
return (&cdinfo);
128
if (dev->dv_type == DEVT_FD)
129
return (&fdinfo);
130
return (NULL);
131
}
132
133
/* XXX this gets called way way too often, investigate */
134
pdinfo_t *
135
efiblk_get_pdinfo(struct devdesc *dev)
136
{
137
pdinfo_list_t *pdi;
138
pdinfo_t *pd = NULL;
139
140
pdi = efiblk_get_pdinfo_list(dev->d_dev);
141
if (pdi == NULL)
142
return (pd);
143
144
STAILQ_FOREACH(pd, pdi, pd_link) {
145
if (pd->pd_unit == dev->d_unit)
146
return (pd);
147
}
148
return (pd);
149
}
150
151
pdinfo_t *
152
efiblk_get_pdinfo_by_device_path(EFI_DEVICE_PATH *path)
153
{
154
EFI_HANDLE h;
155
EFI_STATUS status;
156
EFI_DEVICE_PATH *devp = path;
157
158
status = BS->LocateDevicePath(&blkio_guid, &devp, &h);
159
if (EFI_ERROR(status))
160
return (NULL);
161
return (efiblk_get_pdinfo_by_handle(h));
162
}
163
164
static bool
165
same_handle(pdinfo_t *pd, EFI_HANDLE h)
166
{
167
168
return (pd->pd_handle == h || pd->pd_alias == h);
169
}
170
171
pdinfo_t *
172
efiblk_get_pdinfo_by_handle(EFI_HANDLE h)
173
{
174
pdinfo_t *dp, *pp;
175
176
/*
177
* Check hard disks, then cd, then floppy
178
*/
179
STAILQ_FOREACH(dp, &hdinfo, pd_link) {
180
if (same_handle(dp, h))
181
return (dp);
182
STAILQ_FOREACH(pp, &dp->pd_part, pd_link) {
183
if (same_handle(pp, h))
184
return (pp);
185
}
186
}
187
STAILQ_FOREACH(dp, &cdinfo, pd_link) {
188
if (same_handle(dp, h))
189
return (dp);
190
STAILQ_FOREACH(pp, &dp->pd_part, pd_link) {
191
if (same_handle(pp, h))
192
return (pp);
193
}
194
}
195
STAILQ_FOREACH(dp, &fdinfo, pd_link) {
196
if (same_handle(dp, h))
197
return (dp);
198
}
199
return (NULL);
200
}
201
202
static int
203
efiblk_pdinfo_count(pdinfo_list_t *pdi)
204
{
205
pdinfo_t *pd;
206
int i = 0;
207
208
STAILQ_FOREACH(pd, pdi, pd_link) {
209
i++;
210
}
211
return (i);
212
}
213
214
static pdinfo_t *
215
efipart_find_parent(pdinfo_list_t *pdi, EFI_DEVICE_PATH *devpath)
216
{
217
pdinfo_t *pd;
218
EFI_DEVICE_PATH *parent;
219
220
/* We want to find direct parent */
221
parent = efi_devpath_trim(devpath);
222
/* We should not get out of memory here but be careful. */
223
if (parent == NULL)
224
return (NULL);
225
226
STAILQ_FOREACH(pd, pdi, pd_link) {
227
/* We must have exact match. */
228
if (efi_devpath_match(pd->pd_devpath, parent))
229
break;
230
}
231
free(parent);
232
return (pd);
233
}
234
235
/*
236
* Return true when we should ignore this device.
237
*/
238
static bool
239
efipart_ignore_device(EFI_HANDLE h, EFI_BLOCK_IO *blkio,
240
EFI_DEVICE_PATH *devpath)
241
{
242
EFI_DEVICE_PATH *node, *parent;
243
244
/*
245
* We assume the block size 512 or greater power of 2.
246
* Also skip devices with block size > 64k (16 is max
247
* ashift supported by zfs).
248
* iPXE is known to insert stub BLOCK IO device with
249
* BlockSize 1.
250
*/
251
if (blkio->Media->BlockSize < 512 ||
252
blkio->Media->BlockSize > (1 << 16) ||
253
!powerof2(blkio->Media->BlockSize)) {
254
efi_close_devpath(h);
255
return (true);
256
}
257
258
/* Allowed values are 0, 1 and power of 2. */
259
if (blkio->Media->IoAlign > 1 &&
260
!powerof2(blkio->Media->IoAlign)) {
261
efi_close_devpath(h);
262
return (true);
263
}
264
265
/*
266
* With device tree setup:
267
* PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)
268
* PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x1)
269
* PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x2)
270
* PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x3)
271
* PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x3)/CDROM..
272
* PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x3)/CDROM..
273
* PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x4)
274
* PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x5)
275
* PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x6)
276
* PciRoot(0x0)/Pci(0x14,0x0)/USB(0x5,0)/USB(0x2,0x0)/Unit(0x7)
277
*
278
* In above exmple only Unit(0x3) has media, all other nodes are
279
* missing media and should not be used.
280
*
281
* No media does not always mean there is no device, but in above
282
* case, we can not really assume there is any device.
283
* Therefore, if this node is USB, or this node is Unit (LUN) and
284
* direct parent is USB and we have no media, we will ignore this
285
* device.
286
*
287
* Variation of the same situation, but with SCSI devices:
288
* PciRoot(0x0)/Pci(0x1a,0x0)/USB(0x1,0)/USB(0x3,0x0)/SCSI(0x0,0x1)
289
* PciRoot(0x0)/Pci(0x1a,0x0)/USB(0x1,0)/USB(0x3,0x0)/SCSI(0x0,0x2)
290
* PciRoot(0x0)/Pci(0x1a,0x0)/USB(0x1,0)/USB(0x3,0x0)/SCSI(0x0,0x3)
291
* PciRoot(0x0)/Pci(0x1a,0x0)/USB(0x1,0)/USB(0x3,0x0)/SCSI(0x0,0x3)/CD..
292
* PciRoot(0x0)/Pci(0x1a,0x0)/USB(0x1,0)/USB(0x3,0x0)/SCSI(0x0,0x3)/CD..
293
* PciRoot(0x0)/Pci(0x1a,0x0)/USB(0x1,0)/USB(0x3,0x0)/SCSI(0x0,0x4)
294
*
295
* Here above the SCSI luns 1,2 and 4 have no media.
296
*/
297
298
/* Do not ignore device with media. */
299
if (blkio->Media->MediaPresent)
300
return (false);
301
302
node = efi_devpath_last_node(devpath);
303
if (node == NULL)
304
return (false);
305
306
/* USB without media present */
307
if (DevicePathType(node) == MESSAGING_DEVICE_PATH &&
308
DevicePathSubType(node) == MSG_USB_DP) {
309
efi_close_devpath(h);
310
return (true);
311
}
312
313
parent = efi_devpath_trim(devpath);
314
if (parent != NULL) {
315
bool parent_is_usb = false;
316
317
node = efi_devpath_last_node(parent);
318
if (node == NULL) {
319
free(parent);
320
return (false);
321
}
322
if (DevicePathType(node) == MESSAGING_DEVICE_PATH &&
323
DevicePathSubType(node) == MSG_USB_DP)
324
parent_is_usb = true;
325
free(parent);
326
327
node = efi_devpath_last_node(devpath);
328
if (node == NULL)
329
return (false);
330
if (parent_is_usb &&
331
DevicePathType(node) == MESSAGING_DEVICE_PATH) {
332
/*
333
* no media, parent is USB and devicepath is
334
* LUN or SCSI.
335
*/
336
if (DevicePathSubType(node) ==
337
MSG_DEVICE_LOGICAL_UNIT_DP ||
338
DevicePathSubType(node) == MSG_SCSI_DP) {
339
efi_close_devpath(h);
340
return (true);
341
}
342
}
343
}
344
return (false);
345
}
346
347
int
348
efipart_inithandles(void)
349
{
350
unsigned i, nin;
351
UINTN sz;
352
EFI_HANDLE *hin;
353
EFI_DEVICE_PATH *devpath;
354
EFI_BLOCK_IO *blkio;
355
EFI_STATUS status;
356
pdinfo_t *pd;
357
358
if (!STAILQ_EMPTY(&pdinfo))
359
return (0);
360
361
sz = 0;
362
hin = NULL;
363
status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz, hin);
364
if (status == EFI_BUFFER_TOO_SMALL) {
365
hin = malloc(sz);
366
if (hin == NULL)
367
return (ENOMEM);
368
status = BS->LocateHandle(ByProtocol, &blkio_guid, 0, &sz,
369
hin);
370
if (EFI_ERROR(status))
371
free(hin);
372
}
373
if (EFI_ERROR(status))
374
return (efi_status_to_errno(status));
375
376
nin = sz / sizeof(*hin);
377
#ifdef EFIPART_DEBUG
378
printf("%s: Got %d BLOCK IO MEDIA handle(s)\n", __func__, nin);
379
#endif
380
381
for (i = 0; i < nin; i++) {
382
/*
383
* Get devpath and open protocol.
384
* We should not get errors here
385
*/
386
if ((devpath = efi_lookup_devpath(hin[i])) == NULL)
387
continue;
388
389
status = OpenProtocolByHandle(hin[i], &blkio_guid,
390
(void **)&blkio);
391
if (EFI_ERROR(status)) {
392
printf("error %lu\n", DECODE_ERROR(status));
393
continue;
394
}
395
396
if (efipart_ignore_device(hin[i], blkio, devpath))
397
continue;
398
399
/* This is bad. */
400
if ((pd = calloc(1, sizeof(*pd))) == NULL) {
401
printf("efipart_inithandles: Out of memory.\n");
402
free(hin);
403
return (ENOMEM);
404
}
405
STAILQ_INIT(&pd->pd_part);
406
407
pd->pd_handle = hin[i];
408
pd->pd_devpath = devpath;
409
pd->pd_blkio = blkio;
410
STAILQ_INSERT_TAIL(&pdinfo, pd, pd_link);
411
}
412
413
/*
414
* Walk pdinfo and set parents based on device path.
415
*/
416
STAILQ_FOREACH(pd, &pdinfo, pd_link) {
417
pd->pd_parent = efipart_find_parent(&pdinfo, pd->pd_devpath);
418
}
419
free(hin);
420
return (0);
421
}
422
423
/*
424
* Get node identified by pd_test() from plist.
425
*/
426
static pdinfo_t *
427
efipart_get_pd(pdinfo_list_t *plist, pd_test_cb_t pd_test, pdinfo_t *data)
428
{
429
pdinfo_t *pd;
430
431
STAILQ_FOREACH(pd, plist, pd_link) {
432
if (pd_test(pd, data))
433
break;
434
}
435
436
return (pd);
437
}
438
439
static ACPI_HID_DEVICE_PATH *
440
efipart_floppy(EFI_DEVICE_PATH *node)
441
{
442
ACPI_HID_DEVICE_PATH *acpi;
443
444
if (DevicePathType(node) == ACPI_DEVICE_PATH &&
445
DevicePathSubType(node) == ACPI_DP) {
446
acpi = (ACPI_HID_DEVICE_PATH *) node;
447
if (acpi->HID == EISA_PNP_ID(PNP0604) ||
448
acpi->HID == EISA_PNP_ID(PNP0700) ||
449
acpi->HID == EISA_PNP_ID(PNP0701)) {
450
return (acpi);
451
}
452
}
453
return (NULL);
454
}
455
456
static bool
457
efipart_testfd(pdinfo_t *fd, pdinfo_t *data __unused)
458
{
459
EFI_DEVICE_PATH *node;
460
461
node = efi_devpath_last_node(fd->pd_devpath);
462
if (node == NULL)
463
return (false);
464
465
if (efipart_floppy(node) != NULL)
466
return (true);
467
468
return (false);
469
}
470
471
static int
472
efipart_initfd(void)
473
{
474
EFI_DEVICE_PATH *node;
475
ACPI_HID_DEVICE_PATH *acpi;
476
pdinfo_t *parent, *fd;
477
478
while ((fd = efipart_get_pd(&pdinfo, efipart_testfd, NULL)) != NULL) {
479
if ((node = efi_devpath_last_node(fd->pd_devpath)) == NULL)
480
continue;
481
482
if ((acpi = efipart_floppy(node)) == NULL)
483
continue;
484
485
STAILQ_REMOVE(&pdinfo, fd, pdinfo, pd_link);
486
parent = fd->pd_parent;
487
if (parent != NULL) {
488
STAILQ_REMOVE(&pdinfo, parent, pdinfo, pd_link);
489
parent->pd_alias = fd->pd_handle;
490
parent->pd_unit = acpi->UID;
491
free(fd);
492
fd = parent;
493
} else {
494
fd->pd_unit = acpi->UID;
495
}
496
fd->pd_devsw = &efipart_fddev;
497
STAILQ_INSERT_TAIL(&fdinfo, fd, pd_link);
498
}
499
500
bcache_add_dev(efiblk_pdinfo_count(&fdinfo));
501
return (0);
502
}
503
504
/*
505
* Add or update entries with new handle data.
506
*/
507
static void
508
efipart_cdinfo_add(pdinfo_t *cd)
509
{
510
pdinfo_t *parent, *pd, *last;
511
512
if (cd == NULL)
513
return;
514
515
parent = cd->pd_parent;
516
/* Make sure we have parent added */
517
efipart_cdinfo_add(parent);
518
519
STAILQ_FOREACH(pd, &pdinfo, pd_link) {
520
if (efi_devpath_match(pd->pd_devpath, cd->pd_devpath)) {
521
STAILQ_REMOVE(&pdinfo, cd, pdinfo, pd_link);
522
break;
523
}
524
}
525
if (pd == NULL) {
526
/* This device is already added. */
527
return;
528
}
529
530
if (parent != NULL) {
531
last = STAILQ_LAST(&parent->pd_part, pdinfo, pd_link);
532
if (last != NULL)
533
cd->pd_unit = last->pd_unit + 1;
534
else
535
cd->pd_unit = 0;
536
cd->pd_devsw = &efipart_cddev;
537
STAILQ_INSERT_TAIL(&parent->pd_part, cd, pd_link);
538
return;
539
}
540
541
last = STAILQ_LAST(&cdinfo, pdinfo, pd_link);
542
if (last != NULL)
543
cd->pd_unit = last->pd_unit + 1;
544
else
545
cd->pd_unit = 0;
546
547
cd->pd_devsw = &efipart_cddev;
548
STAILQ_INSERT_TAIL(&cdinfo, cd, pd_link);
549
}
550
551
static bool
552
efipart_testcd(pdinfo_t *cd, pdinfo_t *data __unused)
553
{
554
EFI_DEVICE_PATH *node;
555
556
node = efi_devpath_last_node(cd->pd_devpath);
557
if (node == NULL)
558
return (false);
559
560
if (efipart_floppy(node) != NULL)
561
return (false);
562
563
if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
564
DevicePathSubType(node) == MEDIA_CDROM_DP) {
565
return (true);
566
}
567
568
/* cd drive without the media. */
569
if (cd->pd_blkio->Media->RemovableMedia &&
570
!cd->pd_blkio->Media->MediaPresent) {
571
return (true);
572
}
573
574
return (false);
575
}
576
577
/*
578
* Test if pd is parent for device.
579
*/
580
static bool
581
efipart_testchild(pdinfo_t *dev, pdinfo_t *pd)
582
{
583
/* device with no parent. */
584
if (dev->pd_parent == NULL)
585
return (false);
586
587
if (efi_devpath_match(dev->pd_parent->pd_devpath, pd->pd_devpath)) {
588
return (true);
589
}
590
return (false);
591
}
592
593
static int
594
efipart_initcd(void)
595
{
596
pdinfo_t *cd;
597
598
while ((cd = efipart_get_pd(&pdinfo, efipart_testcd, NULL)) != NULL)
599
efipart_cdinfo_add(cd);
600
601
/* Find all children of CD devices we did add above. */
602
STAILQ_FOREACH(cd, &cdinfo, pd_link) {
603
pdinfo_t *child;
604
605
for (child = efipart_get_pd(&pdinfo, efipart_testchild, cd);
606
child != NULL;
607
child = efipart_get_pd(&pdinfo, efipart_testchild, cd))
608
efipart_cdinfo_add(child);
609
}
610
bcache_add_dev(efiblk_pdinfo_count(&cdinfo));
611
return (0);
612
}
613
614
static void
615
efipart_hdinfo_add_node(pdinfo_t *hd, EFI_DEVICE_PATH *node)
616
{
617
pdinfo_t *parent, *ptr;
618
619
if (node == NULL)
620
return;
621
622
parent = hd->pd_parent;
623
/*
624
* If the node is not MEDIA_HARDDRIVE_DP, it is sub-partition.
625
* This can happen with Vendor nodes, and since we do not know
626
* the more about those nodes, we just count them.
627
*/
628
if (DevicePathSubType(node) != MEDIA_HARDDRIVE_DP) {
629
ptr = STAILQ_LAST(&parent->pd_part, pdinfo, pd_link);
630
if (ptr != NULL)
631
hd->pd_unit = ptr->pd_unit + 1;
632
else
633
hd->pd_unit = 0;
634
} else {
635
hd->pd_unit = ((HARDDRIVE_DEVICE_PATH *)node)->PartitionNumber;
636
}
637
638
hd->pd_devsw = &efipart_hddev;
639
STAILQ_INSERT_TAIL(&parent->pd_part, hd, pd_link);
640
}
641
642
/*
643
* The MEDIA_FILEPATH_DP has device name.
644
* From U-Boot sources it looks like names are in the form
645
* of typeN:M, where type is interface type, N is disk id
646
* and M is partition id.
647
*/
648
static void
649
efipart_hdinfo_add_filepath(pdinfo_t *hd, FILEPATH_DEVICE_PATH *node)
650
{
651
char *pathname, *p;
652
int len;
653
pdinfo_t *last;
654
655
last = STAILQ_LAST(&hdinfo, pdinfo, pd_link);
656
if (last != NULL)
657
hd->pd_unit = last->pd_unit + 1;
658
else
659
hd->pd_unit = 0;
660
661
/* FILEPATH_DEVICE_PATH has 0 terminated string */
662
len = ucs2len(node->PathName);
663
if ((pathname = malloc(len + 1)) == NULL) {
664
printf("Failed to add disk, out of memory\n");
665
free(hd);
666
return;
667
}
668
cpy16to8(node->PathName, pathname, len + 1);
669
p = strchr(pathname, ':');
670
671
/*
672
* Assume we are receiving handles in order, first disk handle,
673
* then partitions for this disk. If this assumption proves
674
* false, this code would need update.
675
*/
676
if (p == NULL) { /* no colon, add the disk */
677
hd->pd_devsw = &efipart_hddev;
678
STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link);
679
free(pathname);
680
return;
681
}
682
p++; /* skip the colon */
683
errno = 0;
684
hd->pd_unit = (int)strtol(p, NULL, 0);
685
if (errno != 0) {
686
printf("Bad unit number for partition \"%s\"\n", pathname);
687
free(pathname);
688
free(hd);
689
return;
690
}
691
692
/*
693
* We should have disk registered, if not, we are receiving
694
* handles out of order, and this code should be reworked
695
* to create "blank" disk for partition, and to find the
696
* disk based on PathName compares.
697
*/
698
if (last == NULL) {
699
printf("BUG: No disk for partition \"%s\"\n", pathname);
700
free(pathname);
701
free(hd);
702
return;
703
}
704
/* Add the partition. */
705
hd->pd_parent = last;
706
hd->pd_devsw = &efipart_hddev;
707
STAILQ_INSERT_TAIL(&last->pd_part, hd, pd_link);
708
free(pathname);
709
}
710
711
static void
712
efipart_hdinfo_add(pdinfo_t *hd)
713
{
714
pdinfo_t *parent, *pd, *last;
715
EFI_DEVICE_PATH *node;
716
717
if (hd == NULL)
718
return;
719
720
parent = hd->pd_parent;
721
/* Make sure we have parent added */
722
efipart_hdinfo_add(parent);
723
724
STAILQ_FOREACH(pd, &pdinfo, pd_link) {
725
if (efi_devpath_match(pd->pd_devpath, hd->pd_devpath)) {
726
STAILQ_REMOVE(&pdinfo, hd, pdinfo, pd_link);
727
break;
728
}
729
}
730
if (pd == NULL) {
731
/* This device is already added. */
732
return;
733
}
734
735
if ((node = efi_devpath_last_node(hd->pd_devpath)) == NULL)
736
return;
737
738
if (DevicePathType(node) == MEDIA_DEVICE_PATH &&
739
DevicePathSubType(node) == MEDIA_FILEPATH_DP) {
740
efipart_hdinfo_add_filepath(hd,
741
(FILEPATH_DEVICE_PATH *)node);
742
return;
743
}
744
745
if (parent != NULL) {
746
efipart_hdinfo_add_node(hd, node);
747
return;
748
}
749
750
last = STAILQ_LAST(&hdinfo, pdinfo, pd_link);
751
if (last != NULL)
752
hd->pd_unit = last->pd_unit + 1;
753
else
754
hd->pd_unit = 0;
755
756
/* Add the disk. */
757
hd->pd_devsw = &efipart_hddev;
758
STAILQ_INSERT_TAIL(&hdinfo, hd, pd_link);
759
}
760
761
static bool
762
efipart_testhd(pdinfo_t *hd, pdinfo_t *data __unused)
763
{
764
if (efipart_testfd(hd, NULL))
765
return (false);
766
767
if (efipart_testcd(hd, NULL))
768
return (false);
769
770
/* Anything else must be HD. */
771
return (true);
772
}
773
774
static int
775
efipart_inithd(void)
776
{
777
pdinfo_t *hd;
778
779
while ((hd = efipart_get_pd(&pdinfo, efipart_testhd, NULL)) != NULL)
780
efipart_hdinfo_add(hd);
781
782
bcache_add_dev(efiblk_pdinfo_count(&hdinfo));
783
return (0);
784
}
785
786
static int
787
efipart_print_common(struct devsw *dev, pdinfo_list_t *pdlist, int verbose)
788
{
789
int ret = 0;
790
EFI_BLOCK_IO *blkio;
791
EFI_STATUS status;
792
EFI_HANDLE h;
793
pdinfo_t *pd;
794
CHAR16 *text;
795
struct disk_devdesc pd_dev;
796
char line[80];
797
798
if (STAILQ_EMPTY(pdlist))
799
return (0);
800
801
printf("%s devices:", dev->dv_name);
802
if ((ret = pager_output("\n")) != 0)
803
return (ret);
804
805
STAILQ_FOREACH(pd, pdlist, pd_link) {
806
h = pd->pd_handle;
807
if (verbose) { /* Output the device path. */
808
text = efi_devpath_name(efi_lookup_devpath(h));
809
if (text != NULL) {
810
printf(" %S", text);
811
efi_free_devpath_name(text);
812
if ((ret = pager_output("\n")) != 0)
813
break;
814
}
815
}
816
snprintf(line, sizeof(line),
817
" %s%d", dev->dv_name, pd->pd_unit);
818
printf("%s:", line);
819
status = OpenProtocolByHandle(h, &blkio_guid, (void **)&blkio);
820
if (!EFI_ERROR(status)) {
821
printf(" %llu",
822
blkio->Media->LastBlock == 0? 0:
823
(unsigned long long) (blkio->Media->LastBlock + 1));
824
if (blkio->Media->LastBlock != 0) {
825
printf(" X %u", blkio->Media->BlockSize);
826
}
827
printf(" blocks");
828
if (blkio->Media->MediaPresent) {
829
if (blkio->Media->RemovableMedia)
830
printf(" (removable)");
831
} else {
832
printf(" (no media)");
833
}
834
if ((ret = pager_output("\n")) != 0)
835
break;
836
if (!blkio->Media->MediaPresent)
837
continue;
838
839
pd->pd_blkio = blkio;
840
pd_dev.dd.d_dev = dev;
841
pd_dev.dd.d_unit = pd->pd_unit;
842
pd_dev.d_slice = D_SLICENONE;
843
pd_dev.d_partition = D_PARTNONE;
844
ret = disk_open(&pd_dev, blkio->Media->BlockSize *
845
(blkio->Media->LastBlock + 1),
846
blkio->Media->BlockSize);
847
if (ret == 0) {
848
ret = disk_print(&pd_dev, line, verbose);
849
disk_close(&pd_dev);
850
if (ret != 0)
851
return (ret);
852
} else {
853
/* Do not fail from disk_open() */
854
ret = 0;
855
}
856
} else {
857
if ((ret = pager_output("\n")) != 0)
858
break;
859
}
860
}
861
return (ret);
862
}
863
864
static int
865
efipart_printfd(int verbose)
866
{
867
return (efipart_print_common(&efipart_fddev, &fdinfo, verbose));
868
}
869
870
static int
871
efipart_printcd(int verbose)
872
{
873
return (efipart_print_common(&efipart_cddev, &cdinfo, verbose));
874
}
875
876
static int
877
efipart_printhd(int verbose)
878
{
879
return (efipart_print_common(&efipart_hddev, &hdinfo, verbose));
880
}
881
882
static int
883
efipart_open(struct open_file *f, ...)
884
{
885
va_list args;
886
struct disk_devdesc *dev;
887
pdinfo_t *pd;
888
EFI_BLOCK_IO *blkio;
889
EFI_STATUS status;
890
891
va_start(args, f);
892
dev = va_arg(args, struct disk_devdesc *);
893
va_end(args);
894
if (dev == NULL)
895
return (EINVAL);
896
897
pd = efiblk_get_pdinfo((struct devdesc *)dev);
898
if (pd == NULL)
899
return (EIO);
900
901
if (pd->pd_blkio == NULL) {
902
status = OpenProtocolByHandle(pd->pd_handle, &blkio_guid,
903
(void **)&pd->pd_blkio);
904
if (EFI_ERROR(status))
905
return (efi_status_to_errno(status));
906
}
907
908
blkio = pd->pd_blkio;
909
if (!blkio->Media->MediaPresent)
910
return (EAGAIN);
911
912
pd->pd_open++;
913
if (pd->pd_bcache == NULL)
914
pd->pd_bcache = bcache_allocate();
915
916
if (dev->dd.d_dev->dv_type == DEVT_DISK) {
917
int rc;
918
919
rc = disk_open(dev,
920
blkio->Media->BlockSize * (blkio->Media->LastBlock + 1),
921
blkio->Media->BlockSize);
922
if (rc != 0) {
923
pd->pd_open--;
924
if (pd->pd_open == 0) {
925
pd->pd_blkio = NULL;
926
bcache_free(pd->pd_bcache);
927
pd->pd_bcache = NULL;
928
}
929
}
930
return (rc);
931
}
932
return (0);
933
}
934
935
static int
936
efipart_close(struct open_file *f)
937
{
938
struct disk_devdesc *dev;
939
pdinfo_t *pd;
940
941
dev = (struct disk_devdesc *)(f->f_devdata);
942
if (dev == NULL)
943
return (EINVAL);
944
945
pd = efiblk_get_pdinfo((struct devdesc *)dev);
946
if (pd == NULL)
947
return (EINVAL);
948
949
pd->pd_open--;
950
if (pd->pd_open == 0) {
951
pd->pd_blkio = NULL;
952
if (dev->dd.d_dev->dv_type != DEVT_DISK) {
953
bcache_free(pd->pd_bcache);
954
pd->pd_bcache = NULL;
955
}
956
}
957
if (dev->dd.d_dev->dv_type == DEVT_DISK)
958
return (disk_close(dev));
959
return (0);
960
}
961
962
static int
963
efipart_ioctl(struct open_file *f, u_long cmd, void *data)
964
{
965
struct disk_devdesc *dev;
966
pdinfo_t *pd;
967
int rc;
968
969
dev = (struct disk_devdesc *)(f->f_devdata);
970
if (dev == NULL)
971
return (EINVAL);
972
973
pd = efiblk_get_pdinfo((struct devdesc *)dev);
974
if (pd == NULL)
975
return (EINVAL);
976
977
if (dev->dd.d_dev->dv_type == DEVT_DISK) {
978
rc = disk_ioctl(dev, cmd, data);
979
if (rc != ENOTTY)
980
return (rc);
981
}
982
983
switch (cmd) {
984
case DIOCGSECTORSIZE:
985
*(u_int *)data = pd->pd_blkio->Media->BlockSize;
986
break;
987
case DIOCGMEDIASIZE:
988
*(uint64_t *)data = pd->pd_blkio->Media->BlockSize *
989
(pd->pd_blkio->Media->LastBlock + 1);
990
break;
991
default:
992
return (ENOTTY);
993
}
994
995
return (0);
996
}
997
998
/*
999
* efipart_readwrite()
1000
* Internal equivalent of efipart_strategy(), which operates on the
1001
* media-native block size. This function expects all I/O requests
1002
* to be within the media size and returns an error if such is not
1003
* the case.
1004
*/
1005
static int
1006
efipart_readwrite(EFI_BLOCK_IO *blkio, int rw, daddr_t blk, daddr_t nblks,
1007
char *buf)
1008
{
1009
EFI_STATUS status;
1010
1011
TSENTER();
1012
1013
if (blkio == NULL)
1014
return (ENXIO);
1015
if (blk < 0 || blk > blkio->Media->LastBlock)
1016
return (EIO);
1017
if ((blk + nblks - 1) > blkio->Media->LastBlock)
1018
return (EIO);
1019
1020
switch (rw & F_MASK) {
1021
case F_READ:
1022
status = blkio->ReadBlocks(blkio, blkio->Media->MediaId, blk,
1023
nblks * blkio->Media->BlockSize, buf);
1024
break;
1025
case F_WRITE:
1026
if (blkio->Media->ReadOnly)
1027
return (EROFS);
1028
status = blkio->WriteBlocks(blkio, blkio->Media->MediaId, blk,
1029
nblks * blkio->Media->BlockSize, buf);
1030
break;
1031
default:
1032
return (ENOSYS);
1033
}
1034
1035
if (EFI_ERROR(status)) {
1036
printf("%s: rw=%d, blk=%ju size=%ju status=%lu\n", __func__, rw,
1037
blk, nblks, DECODE_ERROR(status));
1038
}
1039
TSEXIT();
1040
return (efi_status_to_errno(status));
1041
}
1042
1043
static int
1044
efipart_strategy(void *devdata, int rw, daddr_t blk, size_t size,
1045
char *buf, size_t *rsize)
1046
{
1047
struct bcache_devdata bcd;
1048
struct disk_devdesc *dev;
1049
pdinfo_t *pd;
1050
1051
dev = (struct disk_devdesc *)devdata;
1052
if (dev == NULL)
1053
return (EINVAL);
1054
1055
pd = efiblk_get_pdinfo((struct devdesc *)dev);
1056
if (pd == NULL)
1057
return (EINVAL);
1058
1059
if (pd->pd_blkio->Media->RemovableMedia &&
1060
!pd->pd_blkio->Media->MediaPresent)
1061
return (ENXIO);
1062
1063
bcd.dv_strategy = efipart_realstrategy;
1064
bcd.dv_devdata = devdata;
1065
bcd.dv_cache = pd->pd_bcache;
1066
1067
if (dev->dd.d_dev->dv_type == DEVT_DISK) {
1068
daddr_t offset;
1069
1070
offset = dev->d_offset * pd->pd_blkio->Media->BlockSize;
1071
offset /= 512;
1072
return (bcache_strategy(&bcd, rw, blk + offset,
1073
size, buf, rsize));
1074
}
1075
return (bcache_strategy(&bcd, rw, blk, size, buf, rsize));
1076
}
1077
1078
static int
1079
efipart_realstrategy(void *devdata, int rw, daddr_t blk, size_t size,
1080
char *buf, size_t *rsize)
1081
{
1082
struct disk_devdesc *dev = (struct disk_devdesc *)devdata;
1083
pdinfo_t *pd;
1084
EFI_BLOCK_IO *blkio;
1085
uint64_t off, disk_blocks, d_offset = 0;
1086
char *blkbuf;
1087
size_t blkoff, blksz, bio_size;
1088
unsigned ioalign;
1089
bool need_buf;
1090
int rc;
1091
uint64_t diskend, readstart;
1092
1093
if (dev == NULL || blk < 0)
1094
return (EINVAL);
1095
1096
pd = efiblk_get_pdinfo((struct devdesc *)dev);
1097
if (pd == NULL)
1098
return (EINVAL);
1099
1100
blkio = pd->pd_blkio;
1101
if (blkio == NULL)
1102
return (ENXIO);
1103
1104
if (size == 0 || (size % 512) != 0)
1105
return (EIO);
1106
1107
off = blk * 512;
1108
/*
1109
* Get disk blocks, this value is either for whole disk or for
1110
* partition.
1111
*/
1112
disk_blocks = 0;
1113
if (dev->dd.d_dev->dv_type == DEVT_DISK) {
1114
if (disk_ioctl(dev, DIOCGMEDIASIZE, &disk_blocks) == 0) {
1115
/* DIOCGMEDIASIZE does return bytes. */
1116
disk_blocks /= blkio->Media->BlockSize;
1117
}
1118
d_offset = dev->d_offset;
1119
}
1120
if (disk_blocks == 0)
1121
disk_blocks = blkio->Media->LastBlock + 1 - d_offset;
1122
1123
/* make sure we don't read past disk end */
1124
if ((off + size) / blkio->Media->BlockSize > d_offset + disk_blocks) {
1125
diskend = d_offset + disk_blocks;
1126
readstart = off / blkio->Media->BlockSize;
1127
1128
if (diskend <= readstart) {
1129
if (rsize != NULL)
1130
*rsize = 0;
1131
1132
return (EIO);
1133
}
1134
size = diskend - readstart;
1135
size = size * blkio->Media->BlockSize;
1136
}
1137
1138
need_buf = true;
1139
/* Do we need bounce buffer? */
1140
if ((size % blkio->Media->BlockSize == 0) &&
1141
(off % blkio->Media->BlockSize == 0))
1142
need_buf = false;
1143
1144
/* Do we have IO alignment requirement? */
1145
ioalign = blkio->Media->IoAlign;
1146
if (ioalign == 0)
1147
ioalign++;
1148
1149
if (ioalign > 1 && (uintptr_t)buf != roundup2((uintptr_t)buf, ioalign))
1150
need_buf = true;
1151
1152
if (need_buf) {
1153
for (bio_size = BIO_BUFFER_SIZE; bio_size > 0;
1154
bio_size -= blkio->Media->BlockSize) {
1155
blkbuf = memalign(ioalign, bio_size);
1156
if (blkbuf != NULL)
1157
break;
1158
}
1159
} else {
1160
blkbuf = buf;
1161
bio_size = size;
1162
}
1163
1164
if (blkbuf == NULL)
1165
return (ENOMEM);
1166
1167
if (rsize != NULL)
1168
*rsize = size;
1169
1170
rc = 0;
1171
blk = off / blkio->Media->BlockSize;
1172
blkoff = off % blkio->Media->BlockSize;
1173
1174
while (size > 0) {
1175
size_t x = min(size, bio_size);
1176
1177
if (x < blkio->Media->BlockSize)
1178
x = 1;
1179
else
1180
x /= blkio->Media->BlockSize;
1181
1182
switch (rw & F_MASK) {
1183
case F_READ:
1184
blksz = blkio->Media->BlockSize * x - blkoff;
1185
if (size < blksz)
1186
blksz = size;
1187
1188
rc = efipart_readwrite(blkio, rw, blk, x, blkbuf);
1189
if (rc != 0)
1190
goto error;
1191
1192
if (need_buf)
1193
bcopy(blkbuf + blkoff, buf, blksz);
1194
break;
1195
case F_WRITE:
1196
rc = 0;
1197
if (blkoff != 0) {
1198
/*
1199
* We got offset to sector, read 1 sector to
1200
* blkbuf.
1201
*/
1202
x = 1;
1203
blksz = blkio->Media->BlockSize - blkoff;
1204
blksz = min(blksz, size);
1205
rc = efipart_readwrite(blkio, F_READ, blk, x,
1206
blkbuf);
1207
} else if (size < blkio->Media->BlockSize) {
1208
/*
1209
* The remaining block is not full
1210
* sector. Read 1 sector to blkbuf.
1211
*/
1212
x = 1;
1213
blksz = size;
1214
rc = efipart_readwrite(blkio, F_READ, blk, x,
1215
blkbuf);
1216
} else {
1217
/* We can write full sector(s). */
1218
blksz = blkio->Media->BlockSize * x;
1219
}
1220
1221
if (rc != 0)
1222
goto error;
1223
/*
1224
* Put your Data In, Put your Data out,
1225
* Put your Data In, and shake it all about
1226
*/
1227
if (need_buf)
1228
bcopy(buf, blkbuf + blkoff, blksz);
1229
rc = efipart_readwrite(blkio, F_WRITE, blk, x, blkbuf);
1230
if (rc != 0)
1231
goto error;
1232
break;
1233
default:
1234
/* DO NOTHING */
1235
rc = EROFS;
1236
goto error;
1237
}
1238
1239
blkoff = 0;
1240
buf += blksz;
1241
size -= blksz;
1242
blk += x;
1243
}
1244
1245
error:
1246
if (rsize != NULL)
1247
*rsize -= size;
1248
1249
if (need_buf)
1250
free(blkbuf);
1251
return (rc);
1252
}
1253
1254