OSDN Git Service

linux-pci-root: remove an unused assignment
[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                 uint32_t *local_port_id, uint32_t *remote_port_id,
43                 uint32_t *remote_target_id)
44 {
45         int rc;
46         int sz = 0;
47         int pos0 = 0, pos1 = 0;
48         char *spaces;
49
50         sz = strlen(current);
51         spaces = alloca(sz+1);
52         memset(spaces, ' ', sz+1);
53         spaces[sz] = '\0';
54         sz = 0;
55
56         debug("entry");
57         /*
58          * This structure is completely ridiculous.
59          *
60          * /dev/sdc as SAS looks like:
61          * /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
62          * /dev/sdc1 looks like:
63          * /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
64          *
65          * OR
66          *
67          * /dev/sdc as SAS looks like:
68          * /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
69          * /dev/sdc1 looks like:
70          * /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
71          *
72          * /sys/block/sdc/device looks like:
73          * device-> ../../../4:2:0:0
74          *
75          * OR
76          *
77          * 8:0 -> ../../devices/pci0000:74/0000:74:02.0/host2/port-2:0/expander-2:0/port-2:0:2/end_device-2:0:2/target2:0:0/2:0:0:0/block/sda
78          * 8:1 -> ../../devices/pci0000:74/0000:74:02.0/host2/port-2:0/expander-2:0/port-2:0:2/end_device-2:0:2/target2:0:0/2:0:0:0/block/sda/sda1
79          *
80          * /sys/block/sda/device looks like:
81          * device -> ../../../2:0:0:0 *
82          *
83          * sas_address exists, but it's hard to find:
84          * /sys/devices/pci0000:74/0000:74:02.0/host2/port-2:0/expander-2:0/sas_device/expander-2:0/sas_address
85          * but sas_host_address is nowhere to be found, and sas_address
86          * doesn't directly exist under /sys/class/ anywhere.  So you actually
87          * have to go to
88          * /sys/devices/pci0000:74/0000:74:02.0/host2/port-2:0/expander-2:0/sas_device/expander-2:0/sas_address
89          * and chop that off to
90          * /sys/devices/pci0000:74/0000:74:02.0/host2/port-2:0/expander-2:0/
91          * and then add a bunch of port and end device crap to it to get:
92          * /sys/devices/pci0000:74/0000:74:02.0/host2/port-2:0/expander-2:0/port-2:0:2/end_device-2:0:2/sas_device/end_device-2:0:2/sas_address
93
94          */
95
96         /*
97          * So we start when current is:
98          * host4/port-4:0/end_device-4:0/target4:0:0/4:0:0:0/block/sdc/sdc1
99          * or
100          * host2/port-2:0/expander-2:0/port-2:0:2/end_device-2:0:2/target2:0:0/2:0:0:0/block/sda/sda1
101          */
102         uint32_t tosser0, tosser1, tosser2;
103
104         /* ignore a bunch of stuff
105          *    host4/port-4:0
106          * or host4/port-4:0:0
107          */
108         debug("searching for host4/");
109         rc = sscanf(current, "host%d/%n", scsi_host, &pos0);
110         debug("current:\"%s\" rc:%d pos0:%d\n", current+sz, rc, pos0);
111         arrow(LOG_DEBUG, spaces, 9, pos0, rc, 1);
112         if (rc != 1)
113                 return -1;
114         sz += pos0;
115         pos0 = 0;
116
117         /*
118          * We might have this next:
119          * port-2:0/expander-2:0/port-2:0:2/end_device-2:0:2/target2:0:0/2:0:0:0/block/sda/sda1
120          * or:
121          * port-2:0/end_device-2:0:2/target2:0:0/2:0:0:0/block/sda/sda1
122          * or maybe (not sure):
123          * port-2:0:2/end_device-2:0:2/target2:0:0/2:0:0:0/block/sda/sda1
124          */
125         debug("searching for port-4:0 or port-4:0:0");
126         rc = sscanf(current+sz, "port-%d:%d%n:%d%n", &tosser0,
127                     &tosser1, &pos0, &tosser2, &pos1);
128         debug("current:\"%s\" rc:%d pos0:%d pos1:%d\n", current+sz, rc, pos0, pos1);
129         arrow(LOG_DEBUG, spaces, 9, pos0, rc, 2);
130         arrow(LOG_DEBUG, spaces, 9, pos1, rc, 3);
131         if (rc == 2 || rc == 3) {
132                 sz += pos0;
133                 pos0 = 0;
134                 if (local_port_id && rc == 2)
135                         *local_port_id = tosser1;
136                 if (remote_port_id && rc == 3)
137                         *remote_port_id = tosser2;
138
139                 if (current[sz] == '/')
140                         sz += 1;
141
142                 /*
143                  * We might have this next:
144                  * expander-2:0/port-2:0:2/end_device-2:0:2/target2:0:0/2:0:0:0/block/sda/sda1
145                  *                       ^ port id
146                  *                     ^ scsi target id
147                  *                   ^ host number
148                  *          ^ host number
149                  * We don't actually care about either number in expander-.../,
150                  * because they're replicated in all the other places.  We just need
151                  * to get past it.
152                  */
153                 debug("searching for expander-4:0/");
154                 rc = sscanf(current+sz, "expander-%d:%d/%n", &tosser0, &tosser1, &pos0);
155                 debug("current:\"%s\" rc:%d pos0:%d\n", current+sz, rc, pos0);
156                 arrow(LOG_DEBUG, spaces, 9, pos0, rc, 2);
157                 if (rc == 2) {
158                         if (!remote_target_id) {
159                                 efi_error("Device is PHY is a remote target, but remote_target_id is NULL");
160                                 return -1;
161                         }
162                         *remote_target_id = tosser1;
163                         sz += pos0;
164                         pos0 = 0;
165
166                         /*
167                          * if we have that, we should have a 3-part port next
168                          */
169                         debug("searching for port-2:0:2/");
170                         rc = sscanf(current+sz, "port-%d:%d:%d/%n", &tosser0, &tosser1, &tosser2, &pos0);
171                         debug("current:\"%s\" rc:%d pos0:%d\n", current+sz, rc, pos0);
172                         arrow(LOG_DEBUG, spaces, 9, pos0, rc, 3);
173                         if (rc != 3) {
174                                 efi_error("Couldn't parse port expander port string");
175                                 return -1;
176                         }
177                         sz += pos0;
178                 }
179                 pos0 = 0;
180
181                 /* next:
182                  *    /end_device-4:0
183                  * or /end_device-4:0:0
184                  * awesomely these are the exact same fields that go into port-blah,
185                  * but we don't care for now about any of them anyway.
186                  */
187                 debug("searching for end_device-4:0/ or end_device-4:0:0/");
188                 rc = sscanf(current + sz, "end_device-%d:%d%n", &tosser0, &tosser1, &pos0);
189                 debug("current:\"%s\" rc:%d pos0:%d\n", current+sz, rc, pos0);
190                 if (rc != 2)
191                         return -1;
192
193                 pos1 = 0;
194                 rc = sscanf(current + sz + pos0, ":%d%n", &tosser2, &pos1);
195                 arrow(LOG_DEBUG, spaces, 9, pos0, rc + 2, 2);
196                 arrow(LOG_DEBUG, spaces, 9, pos0 + pos1, rc + 2, 3);
197                 if (rc != 0 && rc != 1)
198                         return -1;
199                 if (remote_port_id && rc == 1)
200                         *remote_port_id = tosser2;
201                 if (local_port_id && rc == 0)
202                         *local_port_id = tosser1;
203                 sz += pos0 + pos1;
204                 pos0 = pos1 = 0;
205
206                 if (current[sz] == '/')
207                         sz += 1;
208         } else if (rc != 0) {
209                 return -1;
210         }
211
212         /* now:
213          * /target4:0:0/
214          */
215         uint64_t tosser3;
216         debug("searching for target4:0:0/");
217         rc = sscanf(current + sz, "target%d:%d:%"PRIu64"/%n", &tosser0, &tosser1,
218                     &tosser3, &pos0);
219         debug("current:\"%s\" rc:%d pos0:%d\n", current+sz, rc, pos0);
220         arrow(LOG_DEBUG, spaces, 9, pos0, rc, 3);
221         if (rc != 3)
222                 return -1;
223         sz += pos0;
224         pos0 = 0;
225
226         /* now:
227          * %d:%d:%d:%llu/
228          */
229         debug("searching for 4:0:0:0/");
230         rc = sscanf(current + sz, "%d:%d:%d:%"PRIu64"/%n",
231                     scsi_bus, scsi_device, scsi_target, scsi_lun, &pos0);
232         debug("current:\"%s\" rc:%d pos0:%d\n", current+sz, rc, pos0);
233         arrow(LOG_DEBUG, spaces, 9, pos0, rc, 4);
234         if (rc != 4)
235                 return -1;
236         sz += pos0;
237
238         debug("returning %d", sz);
239         return sz;
240 }
241
242 static ssize_t
243 parse_scsi(struct device *dev, const char *current, const char *root UNUSED)
244 {
245         uint32_t scsi_host, scsi_bus, scsi_device, scsi_target;
246         uint64_t scsi_lun;
247         ssize_t sz;
248         int pos;
249         int rc;
250         char *spaces;
251
252         pos = strlen(current);
253         spaces = alloca(pos+1);
254         memset(spaces, ' ', pos+1);
255         spaces[pos] = '\0';
256         pos = 0;
257
258         debug("entry");
259
260         debug("searching for ../../../0:0:0:0");
261         rc = sscanf(dev->device, "../../../%d:%d:%d:%"PRIu64"%n",
262                     &dev->scsi_info.scsi_bus,
263                     &dev->scsi_info.scsi_device,
264                     &dev->scsi_info.scsi_target,
265                     &dev->scsi_info.scsi_lun,
266                     &pos);
267         debug("current:\"%s\" rc:%d pos:%d\n", dev->device, rc, pos);
268         arrow(LOG_DEBUG, spaces, 9, pos, rc, 3);
269         if (rc != 4)
270                 return 0;
271
272         sz = parse_scsi_link(current, &scsi_host,
273                               &scsi_bus, &scsi_device,
274                               &scsi_target, &scsi_lun,
275                               NULL, NULL, NULL);
276         if (sz < 0)
277                 return 0;
278
279         /*
280          * SCSI disks can have up to 16 partitions, or 4 bits worth
281          * and have one bit for the disk number.
282          */
283         if (dev->major == 8) {
284                 dev->interface_type = scsi;
285                 dev->disknum = (dev->minor >> 4);
286                 set_part(dev, dev->minor & 0xF);
287         } else if (dev->major >= 65 && dev->major <= 71) {
288                 dev->interface_type = scsi;
289                 dev->disknum = 16*(dev->major-64) + (dev->minor >> 4);
290                 set_part(dev, dev->minor & 0xF);
291         } else if (dev->major >= 128 && dev->major <= 135) {
292                 dev->interface_type = scsi;
293                 dev->disknum = 16*(dev->major-128) + (dev->minor >> 4);
294                 set_part(dev, dev->minor & 0xF);
295         } else {
296                 efi_error("couldn't parse scsi major/minor");
297                 return -1;
298         }
299
300         return sz;
301 }
302
303 static ssize_t
304 dp_create_scsi(struct device *dev,
305                uint8_t *buf,  ssize_t size, ssize_t off)
306 {
307         ssize_t sz = 0;
308
309         debug("entry");
310
311         sz = efidp_make_scsi(buf + off, size ? size - off : 0,
312                              dev->scsi_info.scsi_target,
313                              dev->scsi_info.scsi_lun);
314         if (sz < 0)
315                 efi_error("efidp_make_scsi() failed");
316
317         return sz;
318 }
319
320 enum interface_type scsi_iftypes[] = { scsi, unknown };
321
322 struct dev_probe HIDDEN scsi_parser = {
323         .name = "scsi",
324         .iftypes = scsi_iftypes,
325         .flags = DEV_PROVIDES_HD,
326         .parse = parse_scsi,
327         .create = dp_create_scsi,
328 };