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/nvme.h>
25 #include <scsi/scsi.h>
27 #include <sys/ioctl.h>
28 #include <sys/types.h>
29 #include <sys/param.h>
40 #ifndef SCSI_IOCTL_GET_IDLUN
41 #define SCSI_IOCTL_GET_IDLUN 0x5382
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;
55 typedef struct scsi_idlun_s {
57 uint32_t host_unique_id;
61 __attribute__((__visibility__ ("hidden")))
62 eb_scsi_idlun(int fd, uint8_t *host, uint8_t *channel, uint8_t *id,
66 scsi_idlun idlun = {0, 0};
68 if (fd < 0 || !host || !channel || !id || !lun) {
73 rc = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &idlun);
77 *host = (idlun.dev_id >> 24) & 0xff;
78 *channel = (idlun.dev_id >> 16) & 0xff;
79 *lun = (idlun.dev_id >> 8) & 0xff;
80 *id = idlun.dev_id & 0xff;
85 * Look up dynamic major device node numbers.
88 get_dynamic_major(char *name, int block)
91 static char cached_name[1024] = "";
92 static int cached_block;
97 char namenl[strlen(name)+2];
99 if (cached != 0 && block==cached_block &&
100 !strncmp(cached_name, name, 1023)) {
103 strcpy(namenl, name);
104 strcat(namenl, "\n");
107 f = fopen("/proc/devices", "r");
111 while (fgets(line, sizeof line, f) != NULL) {
112 size_t len = strlen(line);
113 int major, scanned = 0;
115 if (!strcmp(line, "Block devices:\n"))
117 if (len == 0 || line[len - 1] != '\n') {
120 if (seen_block == block &&
121 sscanf(line, "%d %n", &major, &scanned) == 1 &&
122 strcmp(line + scanned, namenl) == 0) {
124 strncpy(cached_name, name, 1023);
125 cached_block = block;
134 __attribute__((__visibility__ ("hidden")))
135 eb_ide_pci(int fd, const struct disk_info *info, uint8_t *bus, uint8_t *device,
141 #ifndef SCSI_IOCTL_GET_PCI
142 #define SCSI_IOCTL_GET_PCI 0x5387
145 /* see scsi_ioctl_get_pci() in linux/drivers/scsi/scsi_ioctl.c */
146 #define SLOT_NAME_SIZE ((size_t)21)
148 /* TODO: move this to get it from sysfs? */
150 __attribute__((__visibility__ ("hidden")))
151 eb_scsi_pci(int fd, const struct disk_info *info, uint8_t *bus,
152 uint8_t *device, uint8_t *function)
154 char buf[SLOT_NAME_SIZE] = "";
156 unsigned int b=0,d=0,f=0;
159 * Maybe if we're on an old enough kernel,
160 * SCSI_IOCTL_GET_PCI gives b:d.f ...
162 rc = ioctl(fd, SCSI_IOCTL_GET_PCI, buf);
166 rc = sscanf(buf, "%x:%x:%x", &b, &d, &f);
174 *function = f & 0xff;
179 __attribute__((__visibility__ ("hidden")))
180 set_disk_and_part_name(struct disk_info *info)
185 rc = sysfs_readlink(&linkbuf, "/sys/dev/block/%"PRIu64":%hhd",
186 info->major, info->minor);
191 ultimate = strrchr(linkbuf, '/');
200 penultimate = strrchr(linkbuf, '/');
207 if (!strcmp(penultimate, "block")) {
208 if (!info->disk_name) {
209 info->disk_name = strdup(ultimate);
210 if (!info->disk_name)
213 if (!info->part_name) {
214 rc = asprintf(&info->part_name, "%s%d", info->disk_name,
220 if (!info->disk_name) {
221 info->disk_name = strdup(penultimate);
222 if (!info->disk_name)
225 if (!info->part_name) {
226 info->part_name = strdup(ultimate);
227 if (!info->part_name)
236 sysfs_test_sata(const char *buf, ssize_t size)
238 if (!strncmp(buf, "ata", MIN(size,3)))
244 sysfs_test_sas(const char *buf, ssize_t size, struct disk_info *info)
248 struct stat statbuf = { 0, };
254 rc = sscanf(buf, "host%d/%n", &host, &sz);
256 return (errno == 0) ? 0 : -1;
258 rc = asprintfa(&path, "/sys/class/scsi_host/host%d/host_sas_address",
263 rc = stat(path, &statbuf);
270 sysfs_sata_get_port_info(uint32_t print_id, struct disk_info *info)
278 d = opendir("/sys/class/ata_device/");
282 while ((de = readdir(d)) != NULL) {
283 uint32_t found_print_id;
285 uint32_t found_devno = 0;
287 if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
291 rc = sscanf(de->d_name, "dev%d.%d.%d", &found_print_id,
292 &found_pmp, &found_devno);
294 found_devno = found_pmp;
296 } else if (rc != 3) {
302 if (found_print_id == print_id) {
303 info->sata_info.ata_devno = found_devno;
304 info->sata_info.ata_pmp = found_pmp;
310 rc = read_sysfs_file(&buf, "/sys/class/ata_port/ata%d/port_no",
315 rc = sscanf((char *)buf, "%d", &info->sata_info.ata_port);
319 info->sata_info.ata_port -= 1;
324 sysfs_parse_sata(uint8_t *buf, ssize_t size, ssize_t *off,
325 const char *pbuf, ssize_t psize, ssize_t *poff,
326 struct disk_info *info)
337 uint32_t scsi_device;
338 uint32_t scsi_target;
341 /* find the ata info:
342 * ata1/host0/target0:0:0/
345 rc = sscanf(pbuf, "ata%d/host%d/target%d:%d:%d/%n",
346 &print_id, &scsi_bus, &scsi_device, &scsi_target, &scsi_lun,
352 /* find the emulated scsi bits (and ignore them)
355 uint32_t dummy0, dummy1, dummy2;
357 rc = sscanf(pbuf+*poff, "%d:%d:%d:%"PRIu64"/%n", &dummy0, &dummy1,
358 &dummy2, &dummy3, &psz);
366 char *disk_name = NULL;
367 char *part_name = NULL;
369 rc = sscanf(pbuf+*poff, "block/%m[^/]%n/%m[^/]%n", &disk_name, &psz,
372 rc = asprintf(&part_name, "%s%d", disk_name, info->part);
379 } else if (rc != 2) {
386 info->sata_info.scsi_bus = scsi_bus;
387 info->sata_info.scsi_device = scsi_device;
388 info->sata_info.scsi_target = scsi_target;
389 info->sata_info.scsi_lun = scsi_lun;
391 rc = sysfs_sata_get_port_info(print_id, info);
398 if (pbuf[*poff] != '\0') {
405 info->disk_name = disk_name;
406 info->part_name = part_name;
407 info->interface_type = sata;
409 *off = efidp_make_sata(buf, size, info->sata_info.ata_port,
410 info->sata_info.ata_pmp,
411 info->sata_info.ata_devno);
416 sysfs_parse_sas(uint8_t *buf, ssize_t size, ssize_t *off,
417 const char *pbuf, ssize_t psize, ssize_t *poff,
418 struct disk_info *info)
422 char *filebuf = NULL;
423 uint64_t sas_address;
429 * host4/port-4:0/end_device-4:0/target4:0:0/4:0:0:0/block/sdc/sdc1
431 uint32_t tosser0, tosser1, tosser2;
433 /* ignore a bunch of stuff
435 * or host4/port-4:0:0
437 rc = sscanf(pbuf+*poff, "host%d/port-%d:%d%n", &tosser0, &tosser1,
444 rc = sscanf(pbuf+*poff, ":%d%n", &tosser0, &psz);
445 if (rc != 0 && rc != 1)
451 * or /end_device-4:0:0
452 * awesomely these are the exact same fields that go into port-blah,
453 * but we don't care for now about any of them anyway.
455 rc = sscanf(pbuf+*poff, "/end_device-%d:%d%n", &tosser0, &tosser1,
462 rc = sscanf(pbuf+*poff, ":%d%n", &tosser0, &psz);
463 if (rc != 0 && rc != 1)
471 rc = sscanf(pbuf+*poff, "/target%d:%d:%"PRIu64"/%n", &tosser0, &tosser1,
480 rc = sscanf(pbuf+*poff, "%d:%d:%d:%"PRIu64"/%n",
481 &info->sas_info.scsi_bus,
482 &info->sas_info.scsi_device,
483 &info->sas_info.scsi_target,
484 &info->sas_info.scsi_lun, &psz);
492 char *disk_name = NULL;
493 char *part_name = NULL;
494 rc = sscanf(pbuf+*poff, "block/%m[^/]/%m[^/]%n", &disk_name, &part_name,
500 if (pbuf[*poff] != '\0') {
508 * we also need to get the actual sas_address from someplace...
510 rc = read_sysfs_file(&filebuf,
511 "/sys/class/block/%s/device/sas_address",
516 rc = sscanf(filebuf, "%"PRIx64, &sas_address);
520 info->sas_info.sas_address = sas_address;
521 info->disk_name = disk_name;
522 info->part_name = part_name;
523 info->interface_type = sas;
525 *off = efidp_make_sas(buf, size, sas_address);
530 __attribute__((__visibility__ ("hidden")))
531 make_blockdev_path(uint8_t *buf, ssize_t size, int fd, struct disk_info *info)
533 char *linkbuf = NULL;
539 rc = sysfs_readlink(&linkbuf, "/sys/dev/block/%"PRIu64":%u",
540 info->major, info->minor);
545 * find the pci root domain and port; they basically look like:
546 * ../../devices/pci0000:00/
549 uint16_t root_domain;
551 rc = sscanf(linkbuf+loff, "../../devices/pci%hx:%hhx/%n",
552 &root_domain, &root_bus, &lsz);
555 info->pci_root.root_pci_domain = root_domain;
556 info->pci_root.root_pci_bus = root_bus;
560 rc = read_sysfs_file(&fbuf, "/sys/devices/pci%04x:%02x/firmware_node/hid",
561 root_domain, root_bus);
566 rc = sscanf((char *)fbuf, "PNP%hx", &acpi_hid);
569 info->pci_root.root_pci_acpi_hid = EFIDP_EFI_PNP_ID(acpi_hid);
573 rc = read_sysfs_file(&fbuf, "/sys/devices/pci%4x:%02x/firmware_node/uid",
574 root_domain, root_bus);
575 uint64_t acpi_uid_int = 0;
577 if (rc <= 0 && errno != ENOENT)
580 rc = sscanf((char *)fbuf, "%"PRIu64"\n", &acpi_uid_int);
582 /* kernel uses "%s\n" to print it, so there
583 * should always be some value and a newline... */
584 int l = strlen((char *)buf);
592 info->pci_root.root_pci_acpi_uid = acpi_uid_int;
595 sz = efidp_make_acpi_hid_ex(buf+off, size?size-off:0,
596 info->pci_root.root_pci_acpi_hid,
597 0, 0, "", (char *)fbuf, "");
599 sz = efidp_make_acpi_hid(buf+off, size?size-off:0,
600 info->pci_root.root_pci_acpi_hid,
601 info->pci_root.root_pci_acpi_uid);
607 /* find the pci domain/bus/device/function:
608 * 0000:00:01.0/0000:01:00.0/
609 * ^d ^b ^d ^f (of the last one in the series)
614 uint8_t bus, device, function;
615 rc = sscanf(linkbuf+loff, "%hx:%hhx:%hhx.%hhx/%n",
616 &domain, &bus, &device, &function, &lsz);
619 info->pci_dev.pci_domain = domain;
620 info->pci_dev.pci_bus = bus;
621 info->pci_dev.pci_device = device;
622 info->pci_dev.pci_function = function;
626 sz = efidp_make_pci(buf+off, size?size-off:0, device, function);
636 if (info->interface_type == interface_type_unknown ||
637 info->interface_type == ata ||
638 info->interface_type == atapi ||
639 info->interface_type == usb ||
640 info->interface_type == i1394 ||
641 info->interface_type == fibre ||
642 info->interface_type == i2o ||
643 info->interface_type == md) {
648 if (info->interface_type == virtblk) {
652 if (info->interface_type == nvme) {
654 int rc = eb_nvme_ns_id(fd, &ns_id);
658 sz = efidp_make_nvme(buf+off, size?size-off:0,
666 /* /dev/sda as SATA looks like:
667 * /sys/dev/block/8:0 -> ../../devices/pci0000:00/0000:00:1f.2/ata1/host0/target0:0:0/0:0:0:0/block/sda
669 rc = sysfs_test_sata(linkbuf+loff, PATH_MAX-off);
672 if (!found && rc > 0) {
674 rc = sysfs_parse_sata(buf+off, size?size-off:0, &sz,
675 linkbuf+loff, PATH_MAX-off, &linksz,
684 /* /dev/sdc as SAS looks like:
685 * /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
687 rc = sysfs_test_sas(linkbuf+loff, PATH_MAX-off, info);
690 if (!found && rc > 0) {
692 rc = sysfs_parse_sas(buf+off, size?size-off:0, &sz,
693 linkbuf+loff, PATH_MAX-off, &linksz,
702 if (!found && info->interface_type == scsi) {
705 rc = sysfs_readlink(&linkbuf, "/sys/class/block/%s/device",
710 rc = sscanf(linkbuf, "../../../%d:%d:%d:%"PRIu64,
711 &info->scsi_info.scsi_bus,
712 &info->scsi_info.scsi_device,
713 &info->scsi_info.scsi_target,
714 &info->scsi_info.scsi_lun);
718 sz = efidp_make_scsi(buf+off, size?size-off:0,
719 info->scsi_info.scsi_target,
720 info->scsi_info.scsi_lun);
736 __attribute__((__visibility__ ("hidden")))
737 eb_disk_info_from_fd(int fd, struct disk_info *info)
742 memset(info, 0, sizeof *info);
744 info->pci_root.root_pci_domain = 0xffff;
745 info->pci_root.root_pci_bus = 0xff;
747 memset(&buf, 0, sizeof(struct stat));
748 rc = fstat(fd, &buf);
753 if (S_ISBLK(buf.st_mode)) {
754 info->major = buf.st_rdev >> 8;
755 info->minor = buf.st_rdev & 0xFF;
756 } else if (S_ISREG(buf.st_mode)) {
757 info->major = buf.st_dev >> 8;
758 info->minor = buf.st_dev & 0xFF;
760 printf("Cannot stat non-block or non-regular file\n");
764 /* IDE disks can have up to 64 partitions, or 6 bits worth,
765 * and have one bit for the disk number.
766 * This leaves an extra bit at the top.
768 if (info->major == 3) {
769 info->disknum = (info->minor >> 6) & 1;
770 info->controllernum = (info->major - 3 + 0) + info->disknum;
771 info->interface_type = ata;
772 info->part = info->minor & 0x3F;
774 } else if (info->major == 22) {
775 info->disknum = (info->minor >> 6) & 1;
776 info->controllernum = (info->major - 22 + 2) + info->disknum;
777 info->interface_type = ata;
778 info->part = info->minor & 0x3F;
780 } else if (info->major >= 33 && info->major <= 34) {
781 info->disknum = (info->minor >> 6) & 1;
782 info->controllernum = (info->major - 33 + 4) + info->disknum;
783 info->interface_type = ata;
784 info->part = info->minor & 0x3F;
786 } else if (info->major >= 56 && info->major <= 57) {
787 info->disknum = (info->minor >> 6) & 1;
788 info->controllernum = (info->major - 56 + 8) + info->disknum;
789 info->interface_type = ata;
790 info->part = info->minor & 0x3F;
792 } else if (info->major >= 88 && info->major <= 91) {
793 info->disknum = (info->minor >> 6) & 1;
794 info->controllernum = (info->major - 88 + 12) + info->disknum;
795 info->interface_type = ata;
796 info->part = info->minor & 0x3F;
800 /* I2O disks can have up to 16 partitions, or 4 bits worth. */
801 if (info->major >= 80 && info->major <= 87) {
802 info->interface_type = i2o;
803 info->disknum = 16*(info->major-80) + (info->minor >> 4);
804 info->part = (info->minor & 0xF);
808 /* SCSI disks can have up to 16 partitions, or 4 bits worth
809 * and have one bit for the disk number.
811 if (info->major == 8) {
812 info->interface_type = scsi;
813 info->disknum = (info->minor >> 4);
814 info->part = (info->minor & 0xF);
816 } else if (info->major >= 65 && info->major <= 71) {
817 info->interface_type = scsi;
818 info->disknum = 16*(info->major-64) + (info->minor >> 4);
819 info->part = (info->minor & 0xF);
821 } else if (info->major >= 128 && info->major <= 135) {
822 info->interface_type = scsi;
823 info->disknum = 16*(info->major-128) + (info->minor >> 4);
824 info->part = (info->minor & 0xF);
828 int64_t major = get_dynamic_major("nvme", 1);
829 if (major >= 0 && (uint64_t)major == info->major) {
830 info->interface_type = nvme;
834 major = get_dynamic_major("virtblk", 1);
835 if (major >= 0 && (uint64_t)major == info->major) {
836 info->interface_type = virtblk;
837 info->disknum = info->minor >> 4;
838 info->part = info->minor & 0xF;