OSDN Git Service

Check for the NVMe EUI in the base sysfs dir for the device as well
[android-x86/external-efivar.git] / src / linux.c
1 /*
2  * libefiboot - library for the manipulation of EFI boot variables
3  * Copyright 2012-2015 Red Hat, Inc.
4  * Copyright (C) 2001 Dell Computer Corporation <Matt_Domsch@dell.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public License as
8  * published by the Free Software Foundation; either version 2.1 of the
9  * License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, see
18  * <http://www.gnu.org/licenses/>.
19  *
20  */
21 #include <dirent.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <inttypes.h>
25 #include <limits.h>
26 #include <linux/ethtool.h>
27 #include <linux/version.h>
28 #include <linux/sockios.h>
29 #include <net/if.h>
30 #include <scsi/scsi.h>
31 #include <stdio.h>
32 #include <sys/ioctl.h>
33 #include <sys/socket.h>
34 #include <sys/sysmacros.h>
35 #include <sys/types.h>
36 #include <sys/param.h>
37 #include <sys/stat.h>
38 #include <unistd.h>
39
40 #ifndef major
41 #include <sys/sysmacros.h>
42 #endif
43
44 #include <efivar.h>
45 #include <efiboot.h>
46
47 #include "dp.h"
48 #include "linux.h"
49 #include "util.h"
50
51 int
52 __attribute__((__visibility__ ("hidden")))
53 set_disk_and_part_name(struct disk_info *info)
54 {
55         char *linkbuf;
56         ssize_t rc;
57
58         rc = sysfs_readlink(&linkbuf, "/sys/dev/block/%"PRIu64":%hhu",
59                       info->major, info->minor);
60         if (rc < 0)
61                 return -1;
62
63         char *ultimate;
64         ultimate = strrchr(linkbuf, '/');
65         if (!ultimate) {
66                 errno = EINVAL;
67                 return -1;
68         }
69         *ultimate = '\0';
70         ultimate++;
71
72         char *penultimate;
73         penultimate = strrchr(linkbuf, '/');
74         if (!penultimate) {
75                 errno = EINVAL;
76                 return -1;
77         }
78         penultimate++;
79
80         /*
81          * If there's a better way to figure this out, it'd be good, because
82          * I don't want to have to change this for every new disk type...
83          */
84         if (!strcmp(penultimate, "block")) {
85                 if (!info->disk_name) {
86                         info->disk_name = strdup(ultimate);
87                         if (!info->disk_name)
88                                 return -1;
89                 }
90                 if (!info->part_name) {
91                         rc = asprintf(&info->part_name, "%s%d", info->disk_name,
92                                       info->part);
93                         if (rc < 0)
94                                 return -1;
95                 }
96         } else if (!strncmp(penultimate, "nvme", 4)) {
97                 if (!info->disk_name) {
98                         info->disk_name = strdup(ultimate);
99                         if (!info->disk_name)
100                                 return -1;
101                 }
102                 if (!info->part_name) {
103                         rc = asprintf(&info->part_name, "%sp%d",
104                                       info->disk_name, info->part);
105                         if (rc < 0)
106                                 return -1;
107                 }
108         } else {
109                 if (!info->disk_name) {
110                         info->disk_name = strdup(penultimate);
111                         if (!info->disk_name)
112                                 return -1;
113                 }
114                 if (!info->part_name) {
115                         info->part_name = strdup(ultimate);
116                         if (!info->part_name)
117                                 return -1;
118                 }
119         }
120
121         return 0;
122 }
123
124 int
125 __attribute__((__visibility__ ("hidden")))
126 get_partition_number(const char *devpath)
127 {
128         struct stat statbuf = { 0, };
129         int rc;
130         unsigned int maj, min;
131         char *linkbuf;
132         uint8_t *partbuf;
133         int ret = -1;
134
135         rc = stat(devpath, &statbuf);
136         if (rc < 0)
137                 return -1;
138
139         if (!S_ISBLK(statbuf.st_mode)) {
140                 errno = EINVAL;
141                 return -1;
142         }
143
144         maj = major(statbuf.st_rdev);
145         min = minor(statbuf.st_rdev);
146
147         rc = sysfs_readlink(&linkbuf, "/sys/dev/block/%u:%u", maj, min);
148         if (rc < 0)
149                 return -1;
150
151         rc = read_sysfs_file(&partbuf, "/sys/dev/block/%s/partition", linkbuf);
152         if (rc < 0)
153                 return -1;
154
155         rc = sscanf((char *)partbuf, "%d\n", &ret);
156         if (rc != 1)
157                 return -1;
158         return ret;
159 }
160
161 int
162 __attribute__((__visibility__ ("hidden")))
163 find_parent_devpath(const char * const child, char **parent)
164 {
165         int ret;
166         char *node;
167         char *linkbuf;
168
169         /* strip leading /dev/ */
170         node = strrchr(child, '/');
171         if (!node)
172                 return -1;
173         node++;
174
175         /* look up full path symlink */
176         ret = sysfs_readlink(&linkbuf, "/sys/class/block/%s", node);
177         if (ret < 0)
178                 return ret;
179
180         /* strip child */
181         node = strrchr(linkbuf, '/');
182         if (!node)
183                 return -1;
184         *node = '\0';
185
186         /* read parent */
187         node = strrchr(linkbuf, '/');
188         if (!node)
189                 return -1;
190         *node = '\0';
191         node++;
192
193         /* write out new path */
194         ret = asprintf(parent, "/dev/%s", node);
195         if (ret < 0)
196                 return ret;
197
198         return 0;
199 }
200
201 static int
202 sysfs_test_nvme(const char *buf, ssize_t size)
203 {
204         if (!strncmp(buf, "nvme/", MIN(size, 5)))
205                 return 1;
206         return 0;
207 }
208
209 static int
210 sysfs_test_sata(const char *buf, ssize_t size)
211 {
212         if (!strncmp(buf, "ata", MIN(size, 3)))
213                 return 1;
214         return 0;
215 }
216
217 static int
218 sysfs_test_sas(const char *buf, ssize_t size)
219 {
220         int rc;
221         char *path;
222         struct stat statbuf = { 0, };
223         char *newbuf;
224
225         int host;
226         int sz;
227
228         newbuf = strndupa(buf, size+1);
229         if (!newbuf)
230                 return -1;
231         newbuf[size] = '\0';
232
233         errno = 0;
234         rc = sscanf(newbuf, "host%d/%n", &host, &sz);
235         if (rc < 1)
236                 return (errno == 0) ? 0 : -1;
237
238         rc = asprintfa(&path, "/sys/class/scsi_host/host%d/host_sas_address",
239                         host);
240         if (rc < 0)
241                 return -1;
242
243         rc = stat(path, &statbuf);
244         if (rc >= 0)
245                 return 1;
246         return 0;
247 }
248
249 static ssize_t
250 sysfs_sata_get_port_info(uint32_t print_id, struct disk_info *info)
251 {
252         DIR *d;
253         struct dirent *de;
254         uint8_t *buf = NULL;
255         int rc;
256
257         d = opendir("/sys/class/ata_device/");
258         if (!d) {
259                 efi_error("opendir failed on /sys/class/ata_device/");
260                 return -1;
261         }
262
263         while ((de = readdir(d)) != NULL) {
264                 uint32_t found_print_id;
265                 uint32_t found_pmp;
266                 uint32_t found_devno = 0;
267
268                 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
269                         continue;
270
271                 int rc;
272                 rc = sscanf(de->d_name, "dev%d.%d.%d", &found_print_id,
273                             &found_pmp, &found_devno);
274                 if (rc < 2 || rc > 3) {
275                         closedir(d);
276                         errno = EINVAL;
277                         return -1;
278                 } else if (found_print_id != print_id) {
279                         continue;
280                 } else if (rc == 3) {
281                         /*
282                          * the kernel doesn't't ever tell us the SATA PMPN
283                          * sentinal value, it'll give us devM.N instead of
284                          * devM.N.O in that case instead.
285                          */
286                         if (found_pmp > 0x7fff) {
287                                 closedir(d);
288                                 errno = EINVAL;
289                                 return -1;
290                         }
291                         info->sata_info.ata_devno = 0;
292                         info->sata_info.ata_pmp = found_pmp;
293                         break;
294                 } else if (rc == 2) {
295                         info->sata_info.ata_devno = 0;
296                         info->sata_info.ata_pmp = 0xffff;
297                         break;
298                 }
299         }
300         closedir(d);
301
302         rc = read_sysfs_file(&buf, "/sys/class/ata_port/ata%d/port_no",
303                              print_id);
304         if (rc <= 0)
305                 return -1;
306
307         rc = sscanf((char *)buf, "%d", &info->sata_info.ata_port);
308         if (rc != 1)
309                 return -1;
310
311         /*
312          * ata_port numbers are 1-indexed from libata in the kernel, but
313          * they're 0-indexed in the spec.  For maximal confusion.
314          */
315         if (info->sata_info.ata_port == 0) {
316                 errno = EINVAL;
317                 return -1;
318         } else {
319                 info->sata_info.ata_port -= 1;
320         }
321
322         return 0;
323 }
324
325 static ssize_t
326 sysfs_parse_nvme(uint8_t *buf, ssize_t size, ssize_t *off,
327                 const char *pbuf, ssize_t psize, ssize_t *poff,
328                 struct disk_info *info)
329 {
330         int rc;
331         int psz = 0;
332         uint8_t *filebuf = NULL;
333
334         *poff = 0;
335         *off = 0;
336
337         char *newpbuf;
338
339         newpbuf = strndupa(pbuf, psize+1);
340         if (!newpbuf)
341                 return -1;
342         newpbuf[psize] = '\0';
343
344         int32_t tosser0;
345         int32_t ctrl_id;
346         int32_t ns_id;
347
348         /* buf is:
349          * nvme/nvme0/nvme0n1
350          */
351         rc = sscanf(newpbuf, "nvme/nvme%d/nvme%dn%d%n", &tosser0,
352                     &ctrl_id, &ns_id, &psz);
353         if (rc != 3)
354                 return -1;
355         *poff += psz;
356
357         info->nvme_info.ctrl_id = ctrl_id;
358         info->nvme_info.ns_id = ns_id;
359         info->nvme_info.has_eui = 0;
360         info->interface_type = nvme;
361
362         /*
363          * now fish the eui out of sysfs is there is one...
364          */
365         rc = read_sysfs_file(&filebuf,
366                              "/sys/class/block/nvme%dn%d/eui",
367                              ctrl_id, ns_id);
368         if (rc < 0 && errno == ENOENT) {
369                 rc = read_sysfs_file(&filebuf,
370                              "/sys/class/block/nvme%dn%d/device/eui",
371                              ctrl_id, ns_id);
372         }
373         if (rc >= 0) {
374                 uint8_t eui[8];
375                 if (rc < 23) {
376                         errno = EINVAL;
377                         return -1;
378                 }
379                 rc = sscanf((char *)filebuf,
380                             "%02hhx %02hhx %02hhx %02hhx "
381                             "%02hhx %02hhx %02hhx %02hhx",
382                             &eui[0], &eui[1], &eui[2], &eui[3],
383                             &eui[4], &eui[5], &eui[6], &eui[7]);
384                 if (rc < 8) {
385                         errno = EINVAL;
386                         return -1;
387                 }
388                 info->nvme_info.has_eui = 1;
389                 memcpy(info->nvme_info.eui, eui, sizeof(eui));
390         }
391
392         *off = efidp_make_nvme(buf, size,
393                                info->nvme_info.ns_id,
394                                info->nvme_info.has_eui ? info->nvme_info.eui
395                                                        : NULL);
396         return *off;
397 }
398
399
400 static ssize_t
401 sysfs_parse_sata(uint8_t *buf, ssize_t size, ssize_t *off,
402                  const char *pbuf, ssize_t psize, ssize_t *poff,
403                  struct disk_info *info)
404 {
405         int psz = 0;
406         int rc;
407
408         *poff = 0;
409         *off = 0;
410
411         uint32_t print_id;
412
413         uint32_t scsi_bus;
414         uint32_t scsi_device;
415         uint32_t scsi_target;
416         uint32_t scsi_lun;
417
418         char *newpbuf;
419
420         newpbuf = strndupa(pbuf, psize+1);
421         if (!newpbuf)
422                 return -1;
423         newpbuf[psize] = '\0';
424
425         /* find the ata info:
426          * ata1/host0/target0:0:0/
427          *    ^dev  ^host   x y z
428          */
429         rc = sscanf(newpbuf, "ata%d/host%d/target%d:%d:%d/%n",
430                     &print_id, &scsi_bus, &scsi_device, &scsi_target, &scsi_lun,
431                     &psz);
432         if (rc != 5)
433                 return -1;
434         *poff += psz;
435
436         /* find the emulated scsi bits (and ignore them)
437          * 0:0:0:0/
438          */
439         uint32_t dummy0, dummy1, dummy2;
440         uint64_t dummy3;
441         rc = sscanf(newpbuf+*poff, "%d:%d:%d:%"PRIu64"/%n", &dummy0, &dummy1,
442                     &dummy2, &dummy3, &psz);
443         if (rc != 4)
444                 return -1;
445         *poff += psz;
446
447         /* what's left is:
448          * block/sda/sda4
449          */
450         char *disk_name = NULL;
451         char *part_name = NULL;
452         int psz1 = 0;
453         rc = sscanf(newpbuf+*poff, "block/%m[^/]%n/%m[^/]%n", &disk_name, &psz,
454                     &part_name, &psz1);
455         if (rc == 1) {
456                 rc = asprintf(&part_name, "%s%d", disk_name, info->part);
457                 if (rc < 0) {
458                         free(disk_name);
459                         errno = EINVAL;
460                         return -1;
461                 }
462                 *poff += psz;
463         } else if (rc != 2) {
464                 errno = EINVAL;
465                 return -1;
466         } else {
467                 *poff += psz1;
468         }
469
470         info->sata_info.scsi_bus = scsi_bus;
471         info->sata_info.scsi_device = scsi_device;
472         info->sata_info.scsi_target = scsi_target;
473         info->sata_info.scsi_lun = scsi_lun;
474
475         rc = sysfs_sata_get_port_info(print_id, info);
476         if (rc < 0) {
477                 free(disk_name);
478                 free(part_name);
479                 return -1;
480         }
481
482         /* check the original of this; it's guaranteed in our copy */
483         if (pbuf[*poff] != '\0') {
484                 free(disk_name);
485                 free(part_name);
486                 errno = EINVAL;
487                 return -1;
488         }
489
490         info->disk_name = disk_name;
491         info->part_name = part_name;
492         if (info->interface_type == interface_type_unknown)
493                 info->interface_type = sata;
494
495         if (info->interface_type == ata) {
496                 *off = efidp_make_atapi(buf, size, info->sata_info.ata_port,
497                                         info->sata_info.ata_pmp,
498                                         info->sata_info.ata_devno);
499         } else {
500                 *off = efidp_make_sata(buf, size, info->sata_info.ata_port,
501                                        info->sata_info.ata_pmp,
502                                        info->sata_info.ata_devno);
503         }
504         return *off;
505 }
506
507 static ssize_t
508 sysfs_parse_sas(uint8_t *buf, ssize_t size, ssize_t *off,
509                 const char *pbuf, ssize_t psize, ssize_t *poff,
510                 struct disk_info *info)
511 {
512         int rc;
513         int psz = 0;
514         uint8_t *filebuf = NULL;
515         uint64_t sas_address;
516
517         char *newpbuf;
518
519         newpbuf = strndupa(pbuf, psize+1);
520         if (!newpbuf)
521                 return -1;
522         newpbuf[psize] = '\0';
523
524         *poff = 0;
525         *off = 0;
526
527         /* buf is:
528          * host4/port-4:0/end_device-4:0/target4:0:0/4:0:0:0/block/sdc/sdc1
529          */
530         uint32_t tosser0, tosser1, tosser2;
531
532         /* ignore a bunch of stuff
533          *    host4/port-4:0
534          * or host4/port-4:0:0
535          */
536         rc = sscanf(newpbuf+*poff, "host%d/port-%d:%d%n", &tosser0, &tosser1,
537                     &tosser2, &psz);
538         if (rc != 3)
539                 return -1;
540         *poff += psz;
541
542         psz = 0;
543         rc = sscanf(pbuf+*poff, ":%d%n", &tosser0, &psz);
544         if (rc != 0 && rc != 1)
545                 return -1;
546         *poff += psz;
547
548         /* next:
549          *    /end_device-4:0
550          * or /end_device-4:0:0
551          * awesomely these are the exact same fields that go into port-blah,
552          * but we don't care for now about any of them anyway.
553          */
554         rc = sscanf(newpbuf+*poff, "/end_device-%d:%d%n", &tosser0, &tosser1,
555                     &psz);
556         if (rc != 2)
557                 return -1;
558         *poff += psz;
559
560         psz = 0;
561         rc = sscanf(newpbuf+*poff, ":%d%n", &tosser0, &psz);
562         if (rc != 0 && rc != 1)
563                 return -1;
564         *poff += psz;
565
566         /* now:
567          * /target4:0:0/
568          */
569         uint64_t tosser3;
570         rc = sscanf(newpbuf+*poff, "/target%d:%d:%"PRIu64"/%n", &tosser0,
571                     &tosser1, &tosser3, &psz);
572         if (rc != 3)
573                 return -1;
574         *poff += psz;
575
576         /* now:
577          * %d:%d:%d:%llu/
578          */
579         rc = sscanf(newpbuf+*poff, "%d:%d:%d:%"PRIu64"/%n",
580                     &info->sas_info.scsi_bus,
581                     &info->sas_info.scsi_device,
582                     &info->sas_info.scsi_target,
583                     &info->sas_info.scsi_lun, &psz);
584         if (rc != 4)
585                 return -1;
586         *poff += psz;
587
588         /* what's left is:
589          * block/sdc/sdc1
590          */
591         char *disk_name = NULL;
592         char *part_name = NULL;
593         rc = sscanf(newpbuf+*poff, "block/%m[^/]/%m[^/]%n", &disk_name,
594                     &part_name, &psz);
595         if (rc != 2)
596                 return -1;
597         *poff += psz;
598
599         /* check the original of this; it's guaranteed in our copy */
600         if (pbuf[*poff] != '\0') {
601                 free(disk_name);
602                 free(part_name);
603                 errno = EINVAL;
604                 return -1;
605         }
606
607         /*
608          * we also need to get the actual sas_address from someplace...
609          */
610         rc = read_sysfs_file(&filebuf,
611                              "/sys/class/block/%s/device/sas_address",
612                              disk_name);
613         if (rc < 0)
614                 return -1;
615
616         rc = sscanf((char *)filebuf, "%"PRIx64, &sas_address);
617         if (rc != 1)
618                 return -1;
619
620         info->sas_info.sas_address = sas_address;
621         info->disk_name = disk_name;
622         info->part_name = part_name;
623         info->interface_type = sas;
624
625         *off = efidp_make_sas(buf, size, sas_address);
626         return *off;
627 }
628
629 static ssize_t
630 make_pci_path(uint8_t *buf, ssize_t size, char *pathstr, ssize_t *pathoff)
631 {
632         ssize_t off=0, sz=0;
633         ssize_t poff = 0;
634         int psz;
635         int rc;
636
637         if (pathstr == NULL || pathoff == NULL || pathstr[0] == '\0') {
638                 errno = EINVAL;
639                 return -1;
640         }
641
642         *pathoff = 0;
643
644         uint16_t root_domain;
645         uint8_t root_bus;
646         uint32_t acpi_hid = 0;
647         uint64_t acpi_uid_int = 0;
648         /*
649          * find the pci root domain and port; they basically look like:
650          * pci0000:00/
651          *    ^d   ^p
652          */
653         rc = sscanf(pathstr+poff, "pci%hx:%hhx/%n", &root_domain,
654                     &root_bus, &psz);
655         if (rc != 2)
656                 return -1;
657         poff += psz;
658
659         uint8_t *fbuf = NULL;
660         rc = read_sysfs_file(&fbuf,
661                              "/sys/devices/pci%04x:%02x/firmware_node/hid",
662                              root_domain, root_bus);
663         if (rc < 0)
664                 return -1;
665
666         uint16_t tmp16 = 0;
667         rc = sscanf((char *)fbuf, "PNP%hx", &tmp16);
668         if (rc != 1)
669                 return -1;
670         acpi_hid = EFIDP_EFI_PNP_ID(tmp16);
671
672         /* Apparently basically nothing can look up a PcieRoot() node,
673          * because they just check _CID.  So since _CID for the root pretty
674          * much always has to be PNP0A03 anyway, just use that no matter
675          * what.
676          */
677         if (acpi_hid == EFIDP_ACPI_PCIE_ROOT_HID)
678                 acpi_hid = EFIDP_ACPI_PCI_ROOT_HID;
679
680         errno = 0;
681         fbuf = NULL;
682         int use_uid_str = 0;
683         rc = read_sysfs_file(&fbuf,
684                              "/sys/devices/pci%4x:%02x/firmware_node/uid",
685                              root_domain, root_bus);
686         if (rc <= 0 && errno != ENOENT)
687                 return -1;
688         if (rc > 0) {
689                 rc = sscanf((char *)fbuf, "%"PRIu64"\n", &acpi_uid_int);
690                 if (rc != 1) {
691                         /* kernel uses "%s\n" to print it, so there
692                          * should always be some value and a newline... */
693                         int l = strlen((char *)buf);
694                         if (l >= 1) {
695                                 use_uid_str=1;
696                                 fbuf[l-1] = '\0';
697                         }
698                 }
699         }
700         errno = 0;
701
702         if (use_uid_str) {
703                 sz = efidp_make_acpi_hid_ex(buf+off, size?size-off:0,
704                                             acpi_hid, 0, 0, "", (char *)fbuf,
705                                             "");
706         } else {
707                 sz = efidp_make_acpi_hid(buf+off, size?size-off:0,
708                                          acpi_hid, acpi_uid_int);
709         }
710         if (sz < 0)
711                 return -1;
712         off += sz;
713
714         /* find the pci domain/bus/device/function:
715          * 0000:00:01.0/0000:01:00.0/
716          *              ^d   ^b ^d ^f (of the last one in the series)
717          */
718         int found=0;
719         while (1) {
720                 uint16_t domain;
721                 uint8_t bus, device, function;
722                 rc = sscanf(pathstr+poff, "%hx:%hhx:%hhx.%hhx/%n",
723                             &domain, &bus, &device, &function, &psz);
724                 if (rc != 4)
725                         break;
726                 found=1;
727                 poff += psz;
728
729                 sz = efidp_make_pci(buf+off, size?size-off:0, device, function);
730                 if (sz < 0)
731                         return -1;
732                 off += sz;
733         }
734         if (!found)
735                 return -1;
736
737         *pathoff = poff;
738         return off;
739 }
740
741 int
742 __attribute__((__visibility__ ("hidden")))
743 make_blockdev_path(uint8_t *buf, ssize_t size, struct disk_info *info)
744 {
745         char *linkbuf = NULL;
746         char *driverbuf = NULL;
747         ssize_t off=0, sz=0, loff=0;
748         int lsz = 0;
749         int rc;
750         int found = 0;
751
752         rc = sysfs_readlink(&linkbuf, "/sys/dev/block/%"PRIu64":%u",
753                             info->major, info->minor);
754         if (rc < 0)
755                 return -1;
756
757         /*
758          * the sysfs path basically looks like:
759          * ../../devices/pci$PCI_STUFF/$BLOCKDEV_STUFF/block/$DISK/$PART
760          */
761         rc = sscanf(linkbuf+loff, "../../devices/%n", &lsz);
762         if (rc != 0)
763                 return -1;
764         loff += lsz;
765
766         ssize_t tmplsz=0;
767         sz = make_pci_path(buf, size, linkbuf+loff, &tmplsz);
768         if (sz < 0)
769                 return -1;
770         loff += tmplsz;
771         off += sz;
772
773         char *tmppath = strdupa(linkbuf);
774         if (!tmppath)
775                 return -1;
776         tmppath[loff] = '\0';
777         rc = sysfs_readlink(&driverbuf, "/sys/dev/block/%s/driver", tmppath);
778         if (rc < 0)
779                 return -1;
780
781         char *driver = strrchr(driverbuf, '/');
782         if (!driver || !*driver)
783                 return -1;
784         driver+=1;
785
786         if (!strncmp(driver, "pata_", 5) || !(strcmp(driver, "ata_piix")))
787                 info->interface_type = ata;
788
789         if (info->interface_type == interface_type_unknown ||
790             info->interface_type == atapi ||
791             info->interface_type == usb ||
792             info->interface_type == i1394 ||
793             info->interface_type == fibre ||
794             info->interface_type == i2o ||
795             info->interface_type == md) {
796                 uint32_t tosser;
797                 int tmpoff;
798
799                 rc = sscanf(linkbuf+loff, "virtio%x/%n", &tosser, &tmpoff);
800                 if (rc < 0) {
801                         return -1;
802                 } else if (rc == 1) {
803                         info->interface_type = virtblk;
804                         loff += tmpoff;
805                         found = 1;
806                 }
807         }
808
809         /* /dev/nvme0n1 looks like:
810          * /sys/dev/block/259:0 -> ../../devices/pci0000:00/0000:00:1d.0/0000:05:00.0/nvme/nvme0/nvme0n1
811          */
812         if (!found) {
813                 rc = sysfs_test_nvme(linkbuf+loff, PATH_MAX-off);
814                 if (rc < 0)
815                         return -1;
816                 else if (rc > 0) {
817                         ssize_t linksz;
818                         rc = sysfs_parse_nvme(buf+off, size?size-off:0, &sz,
819                                               linkbuf+loff, PATH_MAX-off,
820                                               &linksz, info);
821                         if (rc < 0)
822                                 return -1;
823                         loff += linksz;
824                         off += sz;
825                         found = 1;
826                 }
827         }
828
829         /* /dev/sda as SATA looks like:
830          * /sys/dev/block/8:0 -> ../../devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda
831          */
832         if (!found) {
833                 rc = sysfs_test_sata(linkbuf+loff, PATH_MAX-off);
834                 if (rc < 0)
835                         return -1;
836                 if (!found && rc > 0) {
837                         ssize_t linksz=0;
838                         rc = sysfs_parse_sata(buf+off, size?size-off:0, &sz,
839                                               linkbuf+loff, PATH_MAX-off,
840                                               &linksz, info);
841                         if (rc < 0)
842                                 return -1;
843                         loff += linksz;
844                         off += sz;
845                         found = 1;
846                 }
847         }
848
849         /* /dev/sdc as SAS looks like:
850          * /sys/dev/block/8:32 -> ../../devices/pci0000:00/0000:00:01.0/0000:01:00.0/host4/port-4:0/end_device-4:0/target4:0:0/4:0:0:0/block/sdc
851          */
852         if (!found) {
853                 rc = sysfs_test_sas(linkbuf+loff, PATH_MAX-off);
854                 if (rc < 0)
855                         return -1;
856                 else if (rc > 0) {
857                         ssize_t linksz=0;
858                         rc = sysfs_parse_sas(buf+off, size?size-off:0, &sz,
859                                              linkbuf+loff, PATH_MAX-off,
860                                              &linksz, info);
861                         if (rc < 0)
862                                 return -1;
863                         loff += linksz;
864                         off += sz;
865                         found = 1;
866                 }
867         }
868
869         if (!found && info->interface_type == scsi) {
870                 char *linkbuf;
871
872                 rc = sysfs_readlink(&linkbuf, "/sys/class/block/%s/device",
873                               info->disk_name);
874                 if (rc < 0)
875                         return 0;
876
877                 rc = sscanf(linkbuf, "../../../%d:%d:%d:%"PRIu64,
878                             &info->scsi_info.scsi_bus,
879                             &info->scsi_info.scsi_device,
880                             &info->scsi_info.scsi_target,
881                             &info->scsi_info.scsi_lun);
882                 if (rc != 4)
883                         return -1;
884
885                 sz = efidp_make_scsi(buf+off, size?size-off:0,
886                                      info->scsi_info.scsi_target,
887                                      info->scsi_info.scsi_lun);
888                 if (sz < 0)
889                         return -1;
890                 off += sz;
891                 found = 1;
892         }
893
894         if (!found) {
895                 errno = ENOENT;
896                 return -1;
897         }
898
899         return off;
900 }
901
902 int
903 __attribute__((__visibility__ ("hidden")))
904 eb_disk_info_from_fd(int fd, struct disk_info *info)
905 {
906         struct stat buf;
907         int rc;
908
909         memset(info, 0, sizeof *info);
910
911         info->pci_root.root_pci_domain = 0xffff;
912         info->pci_root.root_pci_bus = 0xff;
913
914         memset(&buf, 0, sizeof(struct stat));
915         rc = fstat(fd, &buf);
916         if (rc == -1) {
917                 perror("stat");
918                 return 1;
919         }
920         if (S_ISBLK(buf.st_mode)) {
921                 info->major = buf.st_rdev >> 8;
922                 info->minor = buf.st_rdev & 0xFF;
923         } else if (S_ISREG(buf.st_mode)) {
924                 info->major = buf.st_dev >> 8;
925                 info->minor = buf.st_dev & 0xFF;
926         } else {
927                 printf("Cannot stat non-block or non-regular file\n");
928                 return 1;
929         }
930
931         /* IDE disks can have up to 64 partitions, or 6 bits worth,
932          * and have one bit for the disk number.
933          * This leaves an extra bit at the top.
934          */
935         if (info->major == 3) {
936                 info->disknum = (info->minor >> 6) & 1;
937                 info->controllernum = (info->major - 3 + 0) + info->disknum;
938                 info->interface_type = ata;
939                 info->part    = info->minor & 0x3F;
940                 return 0;
941         } else if (info->major == 22) {
942                 info->disknum = (info->minor >> 6) & 1;
943                 info->controllernum = (info->major - 22 + 2) + info->disknum;
944                 info->interface_type = ata;
945                 info->part    = info->minor & 0x3F;
946                 return 0;
947         } else if (info->major >= 33 && info->major <= 34) {
948                 info->disknum = (info->minor >> 6) & 1;
949                 info->controllernum = (info->major - 33 + 4) + info->disknum;
950                 info->interface_type = ata;
951                 info->part    = info->minor & 0x3F;
952                 return 0;
953         } else if (info->major >= 56 && info->major <= 57) {
954                 info->disknum = (info->minor >> 6) & 1;
955                 info->controllernum = (info->major - 56 + 8) + info->disknum;
956                 info->interface_type = ata;
957                 info->part    = info->minor & 0x3F;
958                 return 0;
959         } else if (info->major >= 88 && info->major <= 91) {
960                 info->disknum = (info->minor >> 6) & 1;
961                 info->controllernum = (info->major - 88 + 12) + info->disknum;
962                 info->interface_type = ata;
963                 info->part    = info->minor & 0x3F;
964                 return 0;
965         }
966
967         /* I2O disks can have up to 16 partitions, or 4 bits worth. */
968         if (info->major >= 80 && info->major <= 87) {
969                 info->interface_type = i2o;
970                 info->disknum = 16*(info->major-80) + (info->minor >> 4);
971                 info->part    = (info->minor & 0xF);
972                 return 0;
973         }
974
975         /* SCSI disks can have up to 16 partitions, or 4 bits worth
976          * and have one bit for the disk number.
977          */
978         if (info->major == 8) {
979                 info->interface_type = scsi;
980                 info->disknum = (info->minor >> 4);
981                 info->part    = (info->minor & 0xF);
982                 return 0;
983         } else  if (info->major >= 65 && info->major <= 71) {
984                 info->interface_type = scsi;
985                 info->disknum = 16*(info->major-64) + (info->minor >> 4);
986                 info->part    = (info->minor & 0xF);
987                 return 0;
988         } else  if (info->major >= 128 && info->major <= 135) {
989                 info->interface_type = scsi;
990                 info->disknum = 16*(info->major-128) + (info->minor >> 4);
991                 info->part    = (info->minor & 0xF);
992                 return 0;
993         }
994
995         errno = ENOSYS;
996         return -1;
997 }
998
999 static ssize_t
1000 make_net_pci_path(uint8_t *buf, ssize_t size, const char * const ifname)
1001 {
1002         char *linkbuf = NULL;
1003         ssize_t off=0, sz=0, loff=0;
1004         int lsz = 0;
1005         int rc;
1006
1007         rc = sysfs_readlink(&linkbuf, "/sys/class/net/%s", ifname);
1008         if (rc < 0)
1009                 return -1;
1010
1011         /*
1012          * the sysfs path basically looks like:
1013          * ../../devices/$PCI_STUFF/net/$IFACE
1014          */
1015         rc = sscanf(linkbuf+loff, "../../devices/%n", &lsz);
1016         if (rc != 0)
1017                 return -1;
1018         loff += lsz;
1019
1020         ssize_t tmplsz = 0;
1021         sz = make_pci_path(buf, size, linkbuf+loff, &tmplsz);
1022         if (sz < 0)
1023                 return -1;
1024         off += sz;
1025         loff += tmplsz;
1026
1027         return off;
1028 }
1029
1030 ssize_t
1031 __attribute__((__visibility__ ("hidden")))
1032 make_mac_path(uint8_t *buf, ssize_t size, const char * const ifname)
1033 {
1034         struct ifreq ifr;
1035         struct ethtool_drvinfo drvinfo = { 0, };
1036         int fd, rc;
1037         ssize_t ret = -1, sz, off=0;
1038         char busname[PATH_MAX+1] = "";
1039
1040         memset(&ifr, 0, sizeof (ifr));
1041         strncpy(ifr.ifr_name, ifname, IF_NAMESIZE);
1042         drvinfo.cmd = ETHTOOL_GDRVINFO;
1043         ifr.ifr_data = (caddr_t)&drvinfo;
1044
1045         fd = socket(AF_INET, SOCK_DGRAM, 0);
1046         if (fd < 0)
1047                 return -1;
1048
1049         rc = ioctl(fd, SIOCETHTOOL, &ifr);
1050         if (rc < 0)
1051                 goto err;
1052
1053         strncpy(busname, drvinfo.bus_info, PATH_MAX);
1054
1055         rc = ioctl(fd, SIOCGIFHWADDR, &ifr);
1056         if (rc < 0)
1057                 goto err;
1058
1059         sz = make_net_pci_path(buf, size, ifname);
1060         if (sz < 0)
1061                 goto err;
1062         off += sz;
1063
1064         sz = efidp_make_mac_addr(buf+off, size?size-off:0,
1065                                  ifr.ifr_ifru.ifru_hwaddr.sa_family,
1066                                  (uint8_t *)ifr.ifr_ifru.ifru_hwaddr.sa_data,
1067                                  sizeof(ifr.ifr_ifru.ifru_hwaddr.sa_data));
1068         if (sz < 0)
1069                 return -1;
1070         off += sz;
1071         ret = off;
1072 err:
1073         if (fd >= 0)
1074                 close(fd);
1075         return ret;
1076 }