OSDN Git Service

80c2fb7d82e374b1dc7130ba23baa0d0ec87d565
[android-x86/external-efivar.git] / src / linux-scsi.c
1 /*
2  * libefiboot - library for the manipulation of EFI boot variables
3  * Copyright 2012-2018 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public License as
7  * published by the Free Software Foundation; either version 2.1 of the
8  * License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, see
17  * <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "fix_coverity.h"
22
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <inttypes.h>
26 #include <stdint.h>
27 #include <unistd.h>
28
29 #include "efiboot.h"
30
31 /*
32  * support for Old-school SCSI devices
33  */
34
35 /*
36  * helper for scsi formats...
37  */
38 ssize_t HIDDEN
39 parse_scsi_link(const char *current, uint32_t *scsi_host,
40                 uint32_t *scsi_bus, uint32_t *scsi_device,
41                 uint32_t *scsi_target, uint64_t *scsi_lun)
42 {
43         int rc;
44         int sz = 0;
45         int pos0 = 0, pos1 = 0;
46         char *spaces;
47
48         sz = strlen(current);
49         spaces = alloca(sz+1);
50         memset(spaces, ' ', sz+1);
51         spaces[sz] = '\0';
52         sz = 0;
53
54         debug("entry");
55         /*
56          * This structure is completely ridiculous.
57          *
58          * /dev/sdc as SAS looks like:
59          * /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
60          * /dev/sdc1 looks like:
61          * /sys/dev/block/8:33 -> ../../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/sdc1
62          *
63          * OR
64          *
65          * /dev/sdc as SAS looks like:
66          * /sys/dev/block/8:32 -> ../../devices/pci0000:00/0000:00:01.0/0000:01:00.0/host4/port-4:2:0/end_device-4:2:0/target4:2:0/4:2:0:0/block/sdc
67          * /dev/sdc1 looks like:
68          * /sys/dev/block/8:33 -> ../../devices/pci0000:00/0000:00:01.0/0000:01:00.0/host4/port-4:2:0/end_device-4:2:0/target4:2:0/4:2:0:0/block/sdc/sdc1
69          *
70          * /sys/block/sdc/device looks like:
71          * device-> ../../../4:2:0:0
72          *
73          */
74
75         /*
76          * So we start when current is:
77          * host4/port-4:0/end_device-4:0/target4:0:0/4:0:0:0/block/sdc/sdc1
78          */
79         uint32_t tosser0, tosser1, tosser2;
80
81         /* ignore a bunch of stuff
82          *    host4/port-4:0
83          * or host4/port-4:0:0
84          */
85         debug("searching for host4/");
86         rc = sscanf(current, "host%d/%n", scsi_host, &pos0);
87         debug("current:\"%s\" rc:%d pos0:%d\n", current+sz, rc, pos0);
88         arrow(LOG_DEBUG, spaces, 9, pos0, rc, 1);
89         if (rc != 1)
90                 return -1;
91         sz += pos0;
92         pos0 = 0;
93
94         debug("searching for port-4:0 or port-4:0:0");
95         rc = sscanf(current, "port-%d:%d%n:%d%n", &tosser0,
96                     &tosser1, &pos0, &tosser2, &pos1);
97         debug("current:\"%s\" rc:%d pos0:%d pos1:%d\n", current+sz, rc, pos0, pos1);
98         arrow(LOG_DEBUG, spaces, 9, pos0, rc, 2);
99         arrow(LOG_DEBUG, spaces, 9, pos1, rc, 3);
100         if (rc == 2 || rc == 3) {
101                 sz += pos0;
102                 pos0 = 0;
103
104                 /* next:
105                  *    /end_device-4:0
106                  * or /end_device-4:0:0
107                  * awesomely these are the exact same fields that go into port-blah,
108                  * but we don't care for now about any of them anyway.
109                  */
110                 debug("searching for /end_device-4:0/ or /end_device-4:0:0/");
111                 rc = sscanf(current + sz, "/end_device-%d:%d%n", &tosser0, &tosser1, &pos0);
112                 debug("current:\"%s\" rc:%d pos0:%d\n", current+sz, rc, pos0);
113                 arrow(LOG_DEBUG, spaces, 9, pos0, rc, 2);
114                 if (rc != 2)
115                         return -1;
116                 sz += pos0;
117                 pos0 = 0;
118
119                 rc = sscanf(current + sz, ":%d%n", &tosser0, &pos0);
120                 debug("current:\"%s\" rc:%d pos0:%d\n", current+sz, rc, pos0);
121                 arrow(LOG_DEBUG, spaces, 9, pos0, rc, 2);
122                 if (rc != 0 && rc != 1)
123                         return -1;
124                 sz += pos0;
125                 pos0 = 0;
126
127                 if (current[sz] == '/')
128                         sz += 1;
129         } else if (rc != 0) {
130                 return -1;
131         }
132
133         /* now:
134          * /target4:0:0/
135          */
136         uint64_t tosser3;
137         debug("searching for target4:0:0/");
138         rc = sscanf(current + sz, "target%d:%d:%"PRIu64"/%n", &tosser0, &tosser1,
139                     &tosser3, &pos0);
140         debug("current:\"%s\" rc:%d pos0:%d\n", current+sz, rc, pos0);
141         arrow(LOG_DEBUG, spaces, 9, pos0, rc, 3);
142         if (rc != 3)
143                 return -1;
144         sz += pos0;
145         pos0 = 0;
146
147         /* now:
148          * %d:%d:%d:%llu/
149          */
150         debug("searching for 4:0:0:0/");
151         rc = sscanf(current + sz, "%d:%d:%d:%"PRIu64"/%n",
152                     scsi_bus, scsi_device, scsi_target, scsi_lun, &pos0);
153         debug("current:\"%s\" rc:%d pos0:%d\n", current+sz, rc, pos0);
154         arrow(LOG_DEBUG, spaces, 9, pos0, rc, 4);
155         if (rc != 4)
156                 return -1;
157         sz += pos0;
158
159         return sz;
160 }
161
162 static ssize_t
163 parse_scsi(struct device *dev, const char *current, const char *root UNUSED)
164 {
165         uint32_t scsi_host, scsi_bus, scsi_device, scsi_target;
166         uint64_t scsi_lun;
167         ssize_t sz;
168         int pos;
169         int rc;
170         char *spaces;
171
172         pos = strlen(current);
173         spaces = alloca(pos+1);
174         memset(spaces, ' ', pos+1);
175         spaces[pos] = '\0';
176         pos = 0;
177
178         debug("entry");
179
180         debug("searching for ../../../0:0:0:0");
181         rc = sscanf(dev->device, "../../../%d:%d:%d:%"PRIu64"%n",
182                     &dev->scsi_info.scsi_bus,
183                     &dev->scsi_info.scsi_device,
184                     &dev->scsi_info.scsi_target,
185                     &dev->scsi_info.scsi_lun,
186                     &pos);
187         debug("current:\"%s\" rc:%d pos:%d\n", dev->device, rc, pos);
188         arrow(LOG_DEBUG, spaces, 9, pos, rc, 3);
189         if (rc != 4)
190                 return 0;
191
192         sz = parse_scsi_link(current, &scsi_host,
193                               &scsi_bus, &scsi_device,
194                               &scsi_target, &scsi_lun);
195         if (sz < 0)
196                 return 0;
197
198         /*
199          * SCSI disks can have up to 16 partitions, or 4 bits worth
200          * and have one bit for the disk number.
201          */
202         if (dev->major == 8) {
203                 dev->interface_type = scsi;
204                 dev->disknum = (dev->minor >> 4);
205                 set_part(dev, dev->minor & 0xF);
206         } else if (dev->major >= 65 && dev->major <= 71) {
207                 dev->interface_type = scsi;
208                 dev->disknum = 16*(dev->major-64) + (dev->minor >> 4);
209                 set_part(dev, dev->minor & 0xF);
210         } else if (dev->major >= 128 && dev->major <= 135) {
211                 dev->interface_type = scsi;
212                 dev->disknum = 16*(dev->major-128) + (dev->minor >> 4);
213                 set_part(dev, dev->minor & 0xF);
214         } else {
215                 efi_error("couldn't parse scsi major/minor");
216                 return -1;
217         }
218
219         return sz;
220 }
221
222 static ssize_t
223 dp_create_scsi(struct device *dev,
224                uint8_t *buf,  ssize_t size, ssize_t off)
225 {
226         ssize_t sz = 0;
227
228         debug("entry");
229
230         sz = efidp_make_scsi(buf + off, size ? size - off : 0,
231                              dev->scsi_info.scsi_target,
232                              dev->scsi_info.scsi_lun);
233         if (sz < 0)
234                 efi_error("efidp_make_scsi() failed");
235
236         return sz;
237 }
238
239 enum interface_type scsi_iftypes[] = { scsi, unknown };
240
241 struct dev_probe HIDDEN scsi_parser = {
242         .name = "scsi",
243         .iftypes = scsi_iftypes,
244         .flags = DEV_PROVIDES_HD,
245         .parse = parse_scsi,
246         .create = dp_create_scsi,
247 };