OSDN Git Service

Make Sata() device paths get generated maybe kinda.
[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 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.
10  *
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.
15  *
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/>.
18  */
19 #define _GNU_SOURCE 1
20 #include <dirent.h>
21 #include <errno.h>
22 #include <inttypes.h>
23 #include <limits.h>
24 #include <linux/nvme.h>
25 #include <scsi/scsi.h>
26 #include <stdio.h>
27 #include <sys/ioctl.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 #include <unistd.h>
31
32 #include <efivar.h>
33 #include <efiboot.h>
34 #include "dp.h"
35 #include "linux.h"
36
37 #ifndef SCSI_IOCTL_GET_IDLUN
38 #define SCSI_IOCTL_GET_IDLUN 0x5382
39 #endif
40
41 int
42 __attribute__((__visibility__ ("hidden")))
43 eb_nvme_ns_id(int fd, uint32_t *ns_id)
44 {
45         uint64_t ret = ioctl(fd, NVME_IOCTL_ID, NULL);
46         if ((int)ret < 0)
47                 return ret;
48         *ns_id = (uint32_t)ret;
49         return 0;
50 }
51
52 typedef struct scsi_idlun_s {
53         uint32_t dev_id;
54         uint32_t host_unique_id;
55 } scsi_idlun;
56
57 int
58 __attribute__((__visibility__ ("hidden")))
59 eb_scsi_idlun(int fd, uint8_t *host, uint8_t *channel, uint8_t *id,
60                    uint8_t *lun)
61 {
62         int rc;
63         scsi_idlun idlun = {0, 0};
64
65         if (fd < 0 || !host || !channel || !id || !lun) {
66                 errno = EINVAL;
67                 return -1;
68         }
69
70         rc = ioctl(fd, SCSI_IOCTL_GET_IDLUN, &idlun);
71         if (rc < 0)
72                 return rc;
73
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;
78         return 0;
79 }
80
81 /*
82  * Look up dynamic major device node numbers.
83  */
84 static int
85 get_dynamic_major(char *name, int block)
86 {
87         static int cached;
88         static char cached_name[1024] = "";
89         static int cached_block;
90
91         FILE *f;
92         char line[256];
93         int seen_block = 0;
94         char namenl[strlen(name)+2];
95
96         if (cached != 0 && block==cached_block &&
97             !strncmp(cached_name, name, 1023)) {
98                 return cached;
99         }
100         strcpy(namenl, name);
101         strcat(namenl, "\n");
102
103         cached = -1;
104         f = fopen("/proc/devices", "r");
105         if (f == NULL)
106                 return cached;
107
108         while (fgets(line, sizeof line, f) != NULL) {
109                 size_t len = strlen(line);
110                 int major, scanned = 0;
111
112                 if (!strcmp(line, "Block devices:\n"))
113                         seen_block = 1;
114                 if (len == 0 || line[len - 1] != '\n') {
115                         break;
116                 }
117                 if (seen_block == block &&
118                     sscanf(line, "%d %n", &major, &scanned) == 1 &&
119                     strcmp(line + scanned, namenl) == 0) {
120                         cached = major;
121                         strncpy(cached_name, name, 1023);
122                         cached_block = block;
123                         break;
124                 }
125         }
126         fclose(f);
127         return cached;
128 }
129
130 int
131 __attribute__((__visibility__ ("hidden")))
132 eb_ide_pci(int fd, const struct disk_info *info, uint8_t *bus, uint8_t *device,
133            uint8_t *function)
134 {
135         return -1;
136 }
137
138 #ifndef SCSI_IOCTL_GET_PCI
139 #define SCSI_IOCTL_GET_PCI 0x5387
140 #endif
141
142 /* see scsi_ioctl_get_pci() in linux/drivers/scsi/scsi_ioctl.c */
143 #define SLOT_NAME_SIZE ((size_t)21)
144
145 /* TODO: move this to get it from sysfs? */
146 int
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)
150 {
151         char buf[SLOT_NAME_SIZE] = "";
152         int rc;
153         unsigned int b=0,d=0,f=0;
154
155         /*
156          * Maybe if we're on an old enough kernel,
157          * SCSI_IOCTL_GET_PCI gives b:d.f ...
158          */
159         rc = ioctl(fd, SCSI_IOCTL_GET_PCI, buf);
160         if (rc < 0)
161                 return rc;
162
163         rc = sscanf(buf, "%x:%x:%x", &b, &d, &f);
164         if (rc != 3) {
165                 errno = EINVAL;
166                 return -1;
167         }
168
169         *bus = b & 0xff;
170         *device = d & 0xff;
171         *function = f & 0xff;
172         return 0;
173 }
174
175 int
176 __attribute__((__visibility__ ("hidden")))
177 get_disk_name(uint64_t major, unsigned char minor,
178               char *pathname, size_t max)
179 {
180         char path[PATH_MAX+1] = "";
181         char linkbuf[PATH_MAX+1] = "";
182         ssize_t rc;
183
184         rc = snprintf(path, PATH_MAX, "/sys/dev/block/%"PRIu64":%hhd",
185                       major, minor);
186         if (rc < 0)
187                 return -1;
188
189         rc = readlink(path, linkbuf, PATH_MAX);
190         if (rc < 0)
191                 return -1;
192         linkbuf[PATH_MAX]='\0';
193
194         char *dev;
195         dev = strrchr(linkbuf, '/');
196         if (!dev) {
197                 errno = EINVAL;
198                 return -1;
199         }
200         *dev = '\0';
201
202         dev = strrchr(linkbuf, '/');
203         if (!dev) {
204                 errno = EINVAL;
205                 return -1;
206         }
207
208         strncpy(pathname, dev+1, max);
209         return 0;
210 }
211
212 int
213 __attribute__((__visibility__ ("hidden")))
214 eb_blockdev_pci_fill(struct disk_info *info)
215 {
216         char leftbuf[PATH_MAX+1]="", rightbuf[PATH_MAX+1]="";
217         ssize_t linksz;
218         int off = 0;
219         int sz = 0;
220         int rc;
221
222         rc = snprintf(leftbuf, PATH_MAX, "/sys/dev/block/%"PRIu64":%u",
223                       info->major, info->minor);
224         if (rc < 0)
225                 return -1;
226
227         linksz = readlink(leftbuf, rightbuf, sizeof (rightbuf));
228         rightbuf[linksz] = '\0';
229
230         off = strlen("../../devices/pci0000:00/");
231         int found=0;
232         while (1) {
233                 uint16_t domain;
234                 uint8_t bus, device, function;
235                 rc = sscanf(rightbuf+off, "%hx:%hhx:%hhx.%hhx/%n",
236                             &domain, &bus, &device, &function, &sz);
237                 if (rc != 4)
238                         break;
239                 info->pci_domain = domain;
240                 info->pci_bus = bus;
241                 info->pci_device = device;
242                 info->pci_function = function;
243                 found=1;
244                 off += sz;
245         }
246         if (!found)
247                 return -1;
248
249         if (info->interface_type == scsi) {
250                 char diskname[PATH_MAX+1]="";
251                 rc = get_disk_name(info->major, info->minor, diskname,
252                                    PATH_MAX);
253                 if (rc < 0)
254                         return 0;
255
256                 rc = snprintf(rightbuf, PATH_MAX, "/sys/class/block/%s/device",
257                               diskname);
258                 if (rc < 0)
259                         return 0;
260
261                 linksz = readlink(rightbuf, leftbuf, PATH_MAX);
262                 leftbuf[linksz] = '\0';
263
264                 rc = sscanf(leftbuf, "../../../%d:%d:%d:%llu",
265                             &info->scsi_bus, &info->scsi_device,
266                             &info->scsi_target,
267                             (unsigned long long int *)&info->scsi_lun);
268                 if (rc != 4)
269                         return -1;
270
271                 if (!strncmp(rightbuf+off, "ata", 3)) {
272                         printf("rightbuf: %s\n", rightbuf);
273                         info->interface_type = sata;
274                 } else {
275                         struct stat statbuf = { 0, };
276
277                         rc = snprintf(leftbuf, PATH_MAX,
278                             "/sys/dev/block/%"PRIu64":%u/device/sas_address",
279                             info->major, info->minor);
280                         if (rc < 0)
281                                 return 0;
282
283                         rc = stat(leftbuf, &statbuf);
284                         if (rc >= 0)
285                                 info->interface_type = sas;
286                 }
287         }
288
289         return 0;
290 }
291
292 int
293 __attribute__((__visibility__ ("hidden")))
294 eb_disk_info_from_fd(int fd, struct disk_info *info)
295 {
296         struct stat buf;
297         int rc;
298
299         memset(info, 0, sizeof *info);
300         memset(&buf, 0, sizeof(struct stat));
301         rc = fstat(fd, &buf);
302         if (rc == -1) {
303                 perror("stat");
304                 return 1;
305         }
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;
312         } else {
313                 printf("Cannot stat non-block or non-regular file\n");
314                 return 1;
315         }
316
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.
320          */
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;
326                 return 0;
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;
332                 return 0;
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;
338                 return 0;
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;
344                 return 0;
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;
350                 return 0;
351         }
352
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);
358                 return 0;
359         }
360
361         /* SCSI disks can have up to 16 partitions, or 4 bits worth
362          * and have one bit for the disk number.
363          */
364         if (info->major == 8) {
365                 info->interface_type = scsi;
366                 info->disknum = (info->minor >> 4);
367                 info->part    = (info->minor & 0xF);
368                 return 0;
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);
373                 return 0;
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);
378                 return 0;
379         }
380
381         int64_t major = get_dynamic_major("nvme", 1);
382         if (major >= 0 && (uint64_t)major == info->major) {
383                 info->interface_type = nvme;
384                 return 0;
385         }
386
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;
392                 return 0;
393         }
394
395         errno = ENOSYS;
396         return -1;
397 }