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>
37 #ifndef SCSI_IOCTL_GET_IDLUN
38 #define SCSI_IOCTL_GET_IDLUN 0x5382
42 __attribute__((__visibility__ ("hidden")))
43 eb_nvme_ns_id(int fd, uint32_t *ns_id)
45 uint64_t ret = ioctl(fd, NVME_IOCTL_ID, NULL);
48 *ns_id = (uint32_t)ret;
52 typedef struct scsi_idlun_s {
54 uint32_t host_unique_id;
58 __attribute__((__visibility__ ("hidden")))
59 eb_scsi_idlun(int fd, uint8_t *host, uint8_t *channel, uint8_t *id,
63 scsi_idlun idlun = {0, 0};
65 if (fd < 0 || !host || !channel || !id || !lun) {
70 rc = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &idlun);
74 *host = (idlun.dev_id >> 24) & 0xff;
75 *channel = (idlun.dev_id >> 16) & 0xff;
76 *lun = (idlun.dev_id >> 8) & 0xff;
77 *id = idlun.dev_id & 0xff;
82 * Look up dynamic major device node numbers.
85 get_dynamic_major(char *name, int block)
88 static char cached_name[1024] = "";
89 static int cached_block;
94 char namenl[strlen(name)+2];
96 if (cached != 0 && block==cached_block &&
97 !strncmp(cached_name, name, 1023)) {
100 strcpy(namenl, name);
101 strcat(namenl, "\n");
104 f = fopen("/proc/devices", "r");
108 while (fgets(line, sizeof line, f) != NULL) {
109 size_t len = strlen(line);
110 int major, scanned = 0;
112 if (!strcmp(line, "Block devices:\n"))
114 if (len == 0 || line[len - 1] != '\n') {
117 if (seen_block == block &&
118 sscanf(line, "%d %n", &major, &scanned) == 1 &&
119 strcmp(line + scanned, namenl) == 0) {
121 strncpy(cached_name, name, 1023);
122 cached_block = block;
131 __attribute__((__visibility__ ("hidden")))
132 eb_ide_pci(int fd, const struct disk_info *info, uint8_t *bus, uint8_t *device,
138 #ifndef SCSI_IOCTL_GET_PCI
139 #define SCSI_IOCTL_GET_PCI 0x5387
142 /* see scsi_ioctl_get_pci() in linux/drivers/scsi/scsi_ioctl.c */
143 #define SLOT_NAME_SIZE ((size_t)21)
145 /* TODO: move this to get it from sysfs? */
147 __attribute__((__visibility__ ("hidden")))
148 eb_scsi_pci(int fd, const struct disk_info *info, uint8_t *bus,
149 uint8_t *device, uint8_t *function)
151 char buf[SLOT_NAME_SIZE] = "";
153 unsigned int b=0,d=0,f=0;
156 * Maybe if we're on an old enough kernel,
157 * SCSI_IOCTL_GET_PCI gives b:d.f ...
159 rc = ioctl(fd, SCSI_IOCTL_GET_PCI, buf);
163 rc = sscanf(buf, "%x:%x:%x", &b, &d, &f);
171 *function = f & 0xff;
176 __attribute__((__visibility__ ("hidden")))
177 get_disk_name(uint64_t major, unsigned char minor,
178 char *pathname, size_t max)
180 char path[PATH_MAX+1] = "";
181 char linkbuf[PATH_MAX+1] = "";
184 rc = snprintf(path, PATH_MAX, "/sys/dev/block/%"PRIu64":%hhd",
189 rc = readlink(path, linkbuf, PATH_MAX);
192 linkbuf[PATH_MAX]='\0';
195 dev = strrchr(linkbuf, '/');
202 dev = strrchr(linkbuf, '/');
208 strncpy(pathname, dev+1, max);
213 __attribute__((__visibility__ ("hidden")))
214 eb_blockdev_pci_fill(struct disk_info *info)
216 char leftbuf[PATH_MAX+1]="", rightbuf[PATH_MAX+1]="";
222 rc = snprintf(leftbuf, PATH_MAX, "/sys/dev/block/%"PRIu64":%u",
223 info->major, info->minor);
227 linksz = readlink(leftbuf, rightbuf, sizeof (rightbuf));
228 rightbuf[linksz] = '\0';
230 off = strlen("../../devices/pci0000:00/");
234 uint8_t bus, device, function;
235 rc = sscanf(rightbuf+off, "%hx:%hhx:%hhx.%hhx/%n",
236 &domain, &bus, &device, &function, &sz);
239 info->pci_domain = domain;
241 info->pci_device = device;
242 info->pci_function = function;
249 if (info->interface_type == scsi) {
250 char diskname[PATH_MAX+1]="";
251 rc = get_disk_name(info->major, info->minor, diskname,
256 rc = snprintf(rightbuf, PATH_MAX, "/sys/class/block/%s/device",
261 linksz = readlink(rightbuf, leftbuf, PATH_MAX);
262 leftbuf[linksz] = '\0';
264 rc = sscanf(leftbuf, "../../../%d:%d:%d:%llu",
265 &info->scsi_bus, &info->scsi_device,
267 (unsigned long long int *)&info->scsi_lun);
271 if (!strncmp(rightbuf+off, "ata", 3)) {
272 printf("rightbuf: %s\n", rightbuf);
273 info->interface_type = sata;
275 struct stat statbuf = { 0, };
277 rc = snprintf(leftbuf, PATH_MAX,
278 "/sys/dev/block/%"PRIu64":%u/device/sas_address",
279 info->major, info->minor);
283 rc = stat(leftbuf, &statbuf);
285 info->interface_type = sas;
293 __attribute__((__visibility__ ("hidden")))
294 eb_disk_info_from_fd(int fd, struct disk_info *info)
299 memset(info, 0, sizeof *info);
300 memset(&buf, 0, sizeof(struct stat));
301 rc = fstat(fd, &buf);
306 if (S_ISBLK(buf.st_mode)) {
307 info->major = buf.st_rdev >> 8;
308 info->minor = buf.st_rdev & 0xFF;
309 } else if (S_ISREG(buf.st_mode)) {
310 info->major = buf.st_dev >> 8;
311 info->minor = buf.st_dev & 0xFF;
313 printf("Cannot stat non-block or non-regular file\n");
317 /* IDE disks can have up to 64 partitions, or 6 bits worth,
318 * and have one bit for the disk number.
319 * This leaves an extra bit at the top.
321 if (info->major == 3) {
322 info->disknum = (info->minor >> 6) & 1;
323 info->controllernum = (info->major - 3 + 0) + info->disknum;
324 info->interface_type = ata;
325 info->part = info->minor & 0x3F;
327 } else if (info->major == 22) {
328 info->disknum = (info->minor >> 6) & 1;
329 info->controllernum = (info->major - 22 + 2) + info->disknum;
330 info->interface_type = ata;
331 info->part = info->minor & 0x3F;
333 } else if (info->major >= 33 && info->major <= 34) {
334 info->disknum = (info->minor >> 6) & 1;
335 info->controllernum = (info->major - 33 + 4) + info->disknum;
336 info->interface_type = ata;
337 info->part = info->minor & 0x3F;
339 } else if (info->major >= 56 && info->major <= 57) {
340 info->disknum = (info->minor >> 6) & 1;
341 info->controllernum = (info->major - 56 + 8) + info->disknum;
342 info->interface_type = ata;
343 info->part = info->minor & 0x3F;
345 } else if (info->major >= 88 && info->major <= 91) {
346 info->disknum = (info->minor >> 6) & 1;
347 info->controllernum = (info->major - 88 + 12) + info->disknum;
348 info->interface_type = ata;
349 info->part = info->minor & 0x3F;
353 /* I2O disks can have up to 16 partitions, or 4 bits worth. */
354 if (info->major >= 80 && info->major <= 87) {
355 info->interface_type = i2o;
356 info->disknum = 16*(info->major-80) + (info->minor >> 4);
357 info->part = (info->minor & 0xF);
361 /* SCSI disks can have up to 16 partitions, or 4 bits worth
362 * and have one bit for the disk number.
364 if (info->major == 8) {
365 info->interface_type = scsi;
366 info->disknum = (info->minor >> 4);
367 info->part = (info->minor & 0xF);
369 } else if (info->major >= 65 && info->major <= 71) {
370 info->interface_type = scsi;
371 info->disknum = 16*(info->major-64) + (info->minor >> 4);
372 info->part = (info->minor & 0xF);
374 } else if (info->major >= 128 && info->major <= 135) {
375 info->interface_type = scsi;
376 info->disknum = 16*(info->major-128) + (info->minor >> 4);
377 info->part = (info->minor & 0xF);
381 int64_t major = get_dynamic_major("nvme", 1);
382 if (major >= 0 && (uint64_t)major == info->major) {
383 info->interface_type = nvme;
387 major = get_dynamic_major("virtblk", 1);
388 if (major >= 0 && (uint64_t)major == info->major) {
389 info->interface_type = virtblk;
390 info->disknum = info->minor >> 4;
391 info->part = info->minor & 0xF;