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>
6 * This library is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version.
11 * This library is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this library. If not, see <http://www.gnu.org/licenses/>.
24 #include <linux/ethtool.h>
25 #include <linux/nvme.h>
26 #include <linux/sockios.h>
28 #include <scsi/scsi.h>
30 #include <sys/ioctl.h>
31 #include <sys/socket.h>
32 #include <sys/types.h>
33 #include <sys/param.h>
45 __attribute__((__visibility__ ("hidden")))
46 eb_nvme_ns_id(int fd, uint32_t *ns_id)
48 uint64_t ret = ioctl(fd, NVME_IOCTL_ID, NULL);
51 *ns_id = (uint32_t)ret;
56 __attribute__((__visibility__ ("hidden")))
57 set_disk_and_part_name(struct disk_info *info)
62 rc = sysfs_readlink(&linkbuf, "/sys/dev/block/%"PRIu64":%hhd",
63 info->major, info->minor);
68 ultimate = strrchr(linkbuf, '/');
77 penultimate = strrchr(linkbuf, '/');
84 if (!strcmp(penultimate, "block")) {
85 if (!info->disk_name) {
86 info->disk_name = strdup(ultimate);
90 if (!info->part_name) {
91 rc = asprintf(&info->part_name, "%s%d", info->disk_name,
97 if (!info->disk_name) {
98 info->disk_name = strdup(penultimate);
102 if (!info->part_name) {
103 info->part_name = strdup(ultimate);
104 if (!info->part_name)
113 __attribute__((__visibility__ ("hidden")))
114 get_partition_number(const char *devpath)
116 struct stat statbuf = { 0, };
118 unsigned int maj, min;
123 rc = stat(devpath, &statbuf);
127 if (!S_ISBLK(statbuf.st_mode)) {
132 maj = gnu_dev_major(statbuf.st_rdev);
133 min = gnu_dev_minor(statbuf.st_rdev);
135 rc = sysfs_readlink(&linkbuf, "/sys/dev/block/%u:%u", maj, min);
139 rc = read_sysfs_file(&partbuf, "/sys/dev/block/%s/partition", linkbuf);
143 rc = sscanf(partbuf, "%d\n", &ret);
150 sysfs_test_sata(const char *buf, ssize_t size)
152 if (!strncmp(buf, "ata", MIN(size,3)))
158 sysfs_test_sas(const char *buf, ssize_t size, struct disk_info *info)
162 struct stat statbuf = { 0, };
168 rc = sscanf(buf, "host%d/%n", &host, &sz);
170 return (errno == 0) ? 0 : -1;
172 rc = asprintfa(&path, "/sys/class/scsi_host/host%d/host_sas_address",
177 rc = stat(path, &statbuf);
184 sysfs_sata_get_port_info(uint32_t print_id, struct disk_info *info)
192 d = opendir("/sys/class/ata_device/");
196 while ((de = readdir(d)) != NULL) {
197 uint32_t found_print_id;
199 uint32_t found_devno = 0;
201 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
205 rc = sscanf(de->d_name, "dev%d.%d.%d", &found_print_id,
206 &found_pmp, &found_devno);
208 found_devno = found_pmp;
210 } else if (rc != 3) {
216 if (found_print_id == print_id) {
217 info->sata_info.ata_devno = found_devno;
218 info->sata_info.ata_pmp = found_pmp;
224 rc = read_sysfs_file(&buf, "/sys/class/ata_port/ata%d/port_no",
229 rc = sscanf((char *)buf, "%d", &info->sata_info.ata_port);
233 info->sata_info.ata_port -= 1;
238 sysfs_parse_sata(uint8_t *buf, ssize_t size, ssize_t *off,
239 const char *pbuf, ssize_t psize, ssize_t *poff,
240 struct disk_info *info)
251 uint32_t scsi_device;
252 uint32_t scsi_target;
255 /* find the ata info:
256 * ata1/host0/target0:0:0/
259 rc = sscanf(pbuf, "ata%d/host%d/target%d:%d:%d/%n",
260 &print_id, &scsi_bus, &scsi_device, &scsi_target, &scsi_lun,
266 /* find the emulated scsi bits (and ignore them)
269 uint32_t dummy0, dummy1, dummy2;
271 rc = sscanf(pbuf+*poff, "%d:%d:%d:%"PRIu64"/%n", &dummy0, &dummy1,
272 &dummy2, &dummy3, &psz);
280 char *disk_name = NULL;
281 char *part_name = NULL;
283 rc = sscanf(pbuf+*poff, "block/%m[^/]%n/%m[^/]%n", &disk_name, &psz,
286 rc = asprintf(&part_name, "%s%d", disk_name, info->part);
293 } else if (rc != 2) {
300 info->sata_info.scsi_bus = scsi_bus;
301 info->sata_info.scsi_device = scsi_device;
302 info->sata_info.scsi_target = scsi_target;
303 info->sata_info.scsi_lun = scsi_lun;
305 rc = sysfs_sata_get_port_info(print_id, info);
312 if (pbuf[*poff] != '\0') {
319 info->disk_name = disk_name;
320 info->part_name = part_name;
321 if (info->interface_type == interface_type_unknown)
322 info->interface_type = sata;
324 if (info->interface_type == ata) {
325 *off = efidp_make_atapi(buf, size, info->sata_info.ata_port,
326 info->sata_info.ata_pmp,
327 info->sata_info.ata_devno);
329 *off = efidp_make_sata(buf, size, info->sata_info.ata_port,
330 info->sata_info.ata_pmp,
331 info->sata_info.ata_devno);
337 sysfs_parse_sas(uint8_t *buf, ssize_t size, ssize_t *off,
338 const char *pbuf, ssize_t psize, ssize_t *poff,
339 struct disk_info *info)
343 char *filebuf = NULL;
344 uint64_t sas_address;
350 * host4/port-4:0/end_device-4:0/target4:0:0/4:0:0:0/block/sdc/sdc1
352 uint32_t tosser0, tosser1, tosser2;
354 /* ignore a bunch of stuff
356 * or host4/port-4:0:0
358 rc = sscanf(pbuf+*poff, "host%d/port-%d:%d%n", &tosser0, &tosser1,
365 rc = sscanf(pbuf+*poff, ":%d%n", &tosser0, &psz);
366 if (rc != 0 && rc != 1)
372 * or /end_device-4:0:0
373 * awesomely these are the exact same fields that go into port-blah,
374 * but we don't care for now about any of them anyway.
376 rc = sscanf(pbuf+*poff, "/end_device-%d:%d%n", &tosser0, &tosser1,
383 rc = sscanf(pbuf+*poff, ":%d%n", &tosser0, &psz);
384 if (rc != 0 && rc != 1)
392 rc = sscanf(pbuf+*poff, "/target%d:%d:%"PRIu64"/%n", &tosser0, &tosser1,
401 rc = sscanf(pbuf+*poff, "%d:%d:%d:%"PRIu64"/%n",
402 &info->sas_info.scsi_bus,
403 &info->sas_info.scsi_device,
404 &info->sas_info.scsi_target,
405 &info->sas_info.scsi_lun, &psz);
413 char *disk_name = NULL;
414 char *part_name = NULL;
415 rc = sscanf(pbuf+*poff, "block/%m[^/]/%m[^/]%n", &disk_name, &part_name,
421 if (pbuf[*poff] != '\0') {
429 * we also need to get the actual sas_address from someplace...
431 rc = read_sysfs_file(&filebuf,
432 "/sys/class/block/%s/device/sas_address",
437 rc = sscanf(filebuf, "%"PRIx64, &sas_address);
441 info->sas_info.sas_address = sas_address;
442 info->disk_name = disk_name;
443 info->part_name = part_name;
444 info->interface_type = sas;
446 *off = efidp_make_sas(buf, size, sas_address);
451 make_pci_path(uint8_t *buf, ssize_t size, char *pathstr, ssize_t *pathoff)
458 if (pathstr == NULL || pathoff == NULL || pathstr[0] == '\0') {
465 uint16_t root_domain;
467 uint32_t acpi_hid = 0;
468 uint64_t acpi_uid_int = 0;
470 * find the pci root domain and port; they basically look like:
474 rc = sscanf(pathstr+poff, "pci%hx:%hhx/%n", &root_domain,
481 rc = read_sysfs_file(&fbuf,
482 "/sys/devices/pci%04x:%02x/firmware_node/hid",
483 root_domain, root_bus);
488 rc = sscanf((char *)fbuf, "PNP%hx", &tmp16);
491 acpi_hid = EFIDP_EFI_PNP_ID(tmp16);
493 /* Apparently basically nothing can look up a PcieRoot() node,
494 * because they just check _CID. So since _CID for the root pretty
495 * much always has to be PNP0A03 anyway, just use that no matter
498 if (acpi_hid == EFIDP_ACPI_PCIE_ROOT_HID)
499 acpi_hid = EFIDP_ACPI_PCI_ROOT_HID;
504 rc = read_sysfs_file(&fbuf,
505 "/sys/devices/pci%4x:%02x/firmware_node/uid",
506 root_domain, root_bus);
507 if (rc <= 0 && errno != ENOENT)
510 rc = sscanf((char *)fbuf, "%"PRIu64"\n", &acpi_uid_int);
512 /* kernel uses "%s\n" to print it, so there
513 * should always be some value and a newline... */
514 int l = strlen((char *)buf);
524 sz = efidp_make_acpi_hid_ex(buf+off, size?size-off:0,
525 acpi_hid, 0, 0, "", (char *)fbuf,
528 sz = efidp_make_acpi_hid(buf+off, size?size-off:0,
529 acpi_hid, acpi_uid_int);
535 /* find the pci domain/bus/device/function:
536 * 0000:00:01.0/0000:01:00.0/
537 * ^d ^b ^d ^f (of the last one in the series)
542 uint8_t bus, device, function;
543 rc = sscanf(pathstr+poff, "%hx:%hhx:%hhx.%hhx/%n",
544 &domain, &bus, &device, &function, &psz);
550 sz = efidp_make_pci(buf+off, size?size-off:0, device, function);
563 __attribute__((__visibility__ ("hidden")))
564 make_blockdev_path(uint8_t *buf, ssize_t size, int fd, struct disk_info *info)
566 char *linkbuf = NULL;
567 char *driverbuf = NULL;
568 ssize_t off=0, sz=0, loff=0;
573 rc = sysfs_readlink(&linkbuf, "/sys/dev/block/%"PRIu64":%u",
574 info->major, info->minor);
579 * the sysfs path basically looks like:
580 * ../../devices/pci$PCI_STUFF/$BLOCKDEV_STUFF/block/$DISK/$PART
582 rc = sscanf(linkbuf+loff, "../../devices/%n", &lsz);
588 sz = make_pci_path(buf, size, linkbuf+loff, &tmplsz);
594 char *tmppath = strdupa(linkbuf);
597 tmppath[loff] = '\0';
598 rc = sysfs_readlink(&driverbuf, "/sys/dev/block/%s/driver", tmppath);
602 char *driver = strrchr(driverbuf, '/');
603 if (!driver || !*driver)
607 if (!strncmp(driver, "pata_", 5) || !(strcmp(driver, "ata_piix")))
608 info->interface_type = ata;
610 if (info->interface_type == interface_type_unknown ||
611 info->interface_type == atapi ||
612 info->interface_type == usb ||
613 info->interface_type == i1394 ||
614 info->interface_type == fibre ||
615 info->interface_type == i2o ||
616 info->interface_type == md) {
620 rc = sscanf(linkbuf+loff, "virtio%x/%n", &tosser, &tmpoff);
623 } else if (rc == 1) {
624 info->interface_type = virtblk;
631 int rc = eb_nvme_ns_id(fd, &ns_id);
633 sz = efidp_make_nvme(buf+off, size?size-off:0,
638 info->interface_type = nvme;
645 /* /dev/sda as SATA looks like:
646 * /sys/dev/block/8:0 -> ../../devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda
649 rc = sysfs_test_sata(linkbuf+loff, PATH_MAX-off);
652 if (!found && rc > 0) {
654 rc = sysfs_parse_sata(buf+off, size?size-off:0, &sz,
655 linkbuf+loff, PATH_MAX-off,
665 /* /dev/sdc as SAS looks like:
666 * /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
669 rc = sysfs_test_sas(linkbuf+loff, PATH_MAX-off, info);
674 rc = sysfs_parse_sas(buf+off, size?size-off:0, &sz,
675 linkbuf+loff, PATH_MAX-off,
685 if (!found && info->interface_type == scsi) {
688 rc = sysfs_readlink(&linkbuf, "/sys/class/block/%s/device",
693 rc = sscanf(linkbuf, "../../../%d:%d:%d:%"PRIu64,
694 &info->scsi_info.scsi_bus,
695 &info->scsi_info.scsi_device,
696 &info->scsi_info.scsi_target,
697 &info->scsi_info.scsi_lun);
701 sz = efidp_make_scsi(buf+off, size?size-off:0,
702 info->scsi_info.scsi_target,
703 info->scsi_info.scsi_lun);
719 __attribute__((__visibility__ ("hidden")))
720 eb_disk_info_from_fd(int fd, struct disk_info *info)
725 memset(info, 0, sizeof *info);
727 info->pci_root.root_pci_domain = 0xffff;
728 info->pci_root.root_pci_bus = 0xff;
730 memset(&buf, 0, sizeof(struct stat));
731 rc = fstat(fd, &buf);
736 if (S_ISBLK(buf.st_mode)) {
737 info->major = buf.st_rdev >> 8;
738 info->minor = buf.st_rdev & 0xFF;
739 } else if (S_ISREG(buf.st_mode)) {
740 info->major = buf.st_dev >> 8;
741 info->minor = buf.st_dev & 0xFF;
743 printf("Cannot stat non-block or non-regular file\n");
747 /* IDE disks can have up to 64 partitions, or 6 bits worth,
748 * and have one bit for the disk number.
749 * This leaves an extra bit at the top.
751 if (info->major == 3) {
752 info->disknum = (info->minor >> 6) & 1;
753 info->controllernum = (info->major - 3 + 0) + info->disknum;
754 info->interface_type = ata;
755 info->part = info->minor & 0x3F;
757 } else if (info->major == 22) {
758 info->disknum = (info->minor >> 6) & 1;
759 info->controllernum = (info->major - 22 + 2) + info->disknum;
760 info->interface_type = ata;
761 info->part = info->minor & 0x3F;
763 } else if (info->major >= 33 && info->major <= 34) {
764 info->disknum = (info->minor >> 6) & 1;
765 info->controllernum = (info->major - 33 + 4) + info->disknum;
766 info->interface_type = ata;
767 info->part = info->minor & 0x3F;
769 } else if (info->major >= 56 && info->major <= 57) {
770 info->disknum = (info->minor >> 6) & 1;
771 info->controllernum = (info->major - 56 + 8) + info->disknum;
772 info->interface_type = ata;
773 info->part = info->minor & 0x3F;
775 } else if (info->major >= 88 && info->major <= 91) {
776 info->disknum = (info->minor >> 6) & 1;
777 info->controllernum = (info->major - 88 + 12) + info->disknum;
778 info->interface_type = ata;
779 info->part = info->minor & 0x3F;
783 /* I2O disks can have up to 16 partitions, or 4 bits worth. */
784 if (info->major >= 80 && info->major <= 87) {
785 info->interface_type = i2o;
786 info->disknum = 16*(info->major-80) + (info->minor >> 4);
787 info->part = (info->minor & 0xF);
791 /* SCSI disks can have up to 16 partitions, or 4 bits worth
792 * and have one bit for the disk number.
794 if (info->major == 8) {
795 info->interface_type = scsi;
796 info->disknum = (info->minor >> 4);
797 info->part = (info->minor & 0xF);
799 } else if (info->major >= 65 && info->major <= 71) {
800 info->interface_type = scsi;
801 info->disknum = 16*(info->major-64) + (info->minor >> 4);
802 info->part = (info->minor & 0xF);
804 } else if (info->major >= 128 && info->major <= 135) {
805 info->interface_type = scsi;
806 info->disknum = 16*(info->major-128) + (info->minor >> 4);
807 info->part = (info->minor & 0xF);
816 make_net_pci_path(uint8_t *buf, ssize_t size, const char * const ifname)
818 char *linkbuf = NULL;
819 ssize_t off=0, sz=0, loff=0;
823 rc = sysfs_readlink(&linkbuf, "/sys/class/net/%s", ifname);
828 * the sysfs path basically looks like:
829 * ../../devices/$PCI_STUFF/net/$IFACE
831 rc = sscanf(linkbuf+loff, "../../devices/%n", &lsz);
837 sz = make_pci_path(buf, size, linkbuf+loff, &tmplsz);
847 __attribute__((__visibility__ ("hidden")))
848 make_mac_path(uint8_t *buf, ssize_t size, const char * const ifname)
850 struct ifreq ifr = { 0, };
851 struct ethtool_drvinfo drvinfo = { 0, };
853 ssize_t ret = -1, sz, off=0;
854 char busname[PATH_MAX+1] = "";
856 strncpy(ifr.ifr_name, ifname, IF_NAMESIZE);
857 drvinfo.cmd = ETHTOOL_GDRVINFO;
858 ifr.ifr_data = (caddr_t)&drvinfo;
860 fd = socket(AF_INET, SOCK_DGRAM, 0);
864 rc = ioctl(fd, SIOCETHTOOL, &ifr);
868 strncpy(busname, drvinfo.bus_info, PATH_MAX);
870 rc = ioctl(fd, SIOCGIFHWADDR, &ifr);
874 sz = make_net_pci_path(buf, size, ifname);
879 sz = efidp_make_mac_addr(buf+off, size?size-off:0,
880 ifr.ifr_ifru.ifru_hwaddr.sa_family,
881 (uint8_t *)ifr.ifr_ifru.ifru_hwaddr.sa_data,
882 sizeof(ifr.ifr_ifru.ifru_hwaddr.sa_data));