Commit | Line | Data |
---|---|---|
d9523678 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
138fe4e0 | 2 | /* |
4e639fdf | 3 | * Copyright 2007-2010 Red Hat, Inc. |
138fe4e0 KR |
4 | * by Peter Jones <pjones@redhat.com> |
5 | * Copyright 2008 IBM, Inc. | |
6 | * by Konrad Rzeszutek <konradr@linux.vnet.ibm.com> | |
7 | * Copyright 2008 | |
8 | * by Konrad Rzeszutek <ketuzsezr@darnok.org> | |
9 | * | |
10 | * This code exposes the iSCSI Boot Format Table to userland via sysfs. | |
11 | * | |
138fe4e0 KR |
12 | * Changelog: |
13 | * | |
4e639fdf PJ |
14 | * 06 Jan 2010 - Peter Jones <pjones@redhat.com> |
15 | * New changelog entries are in the git log from now on. Not here. | |
16 | * | |
138fe4e0 KR |
17 | * 14 Mar 2008 - Konrad Rzeszutek <ketuzsezr@darnok.org> |
18 | * Updated comments and copyrights. (v0.4.9) | |
19 | * | |
20 | * 11 Feb 2008 - Konrad Rzeszutek <konradr@linux.vnet.ibm.com> | |
21 | * Converted to using ibft_addr. (v0.4.8) | |
22 | * | |
23 | * 8 Feb 2008 - Konrad Rzeszutek <konradr@linux.vnet.ibm.com> | |
24 | * Combined two functions in one: reserve_ibft_region. (v0.4.7) | |
25 | * | |
26 | * 30 Jan 2008 - Konrad Rzeszutek <konradr@linux.vnet.ibm.com> | |
27 | * Added logic to handle IPv6 addresses. (v0.4.6) | |
28 | * | |
29 | * 25 Jan 2008 - Konrad Rzeszutek <konradr@linux.vnet.ibm.com> | |
30 | * Added logic to handle badly not-to-spec iBFT. (v0.4.5) | |
31 | * | |
32 | * 4 Jan 2008 - Konrad Rzeszutek <konradr@linux.vnet.ibm.com> | |
33 | * Added __init to function declarations. (v0.4.4) | |
34 | * | |
35 | * 21 Dec 2007 - Konrad Rzeszutek <konradr@linux.vnet.ibm.com> | |
36 | * Updated kobject registration, combined unregister functions in one | |
37 | * and code and style cleanup. (v0.4.3) | |
38 | * | |
39 | * 5 Dec 2007 - Konrad Rzeszutek <konradr@linux.vnet.ibm.com> | |
40 | * Added end-markers to enums and re-organized kobject registration. (v0.4.2) | |
41 | * | |
42 | * 4 Dec 2007 - Konrad Rzeszutek <konradr@linux.vnet.ibm.com> | |
43 | * Created 'device' sysfs link to the NIC and style cleanup. (v0.4.1) | |
44 | * | |
45 | * 28 Nov 2007 - Konrad Rzeszutek <konradr@linux.vnet.ibm.com> | |
46 | * Added sysfs-ibft documentation, moved 'find_ibft' function to | |
47 | * in its own file and added text attributes for every struct field. (v0.4) | |
48 | * | |
49 | * 21 Nov 2007 - Konrad Rzeszutek <konradr@linux.vnet.ibm.com> | |
50 | * Added text attributes emulating OpenFirmware /proc/device-tree naming. | |
51 | * Removed binary /sysfs interface (v0.3) | |
52 | * | |
53 | * 29 Aug 2007 - Konrad Rzeszutek <konradr@linux.vnet.ibm.com> | |
54 | * Added functionality in setup.c to reserve iBFT region. (v0.2) | |
55 | * | |
56 | * 27 Aug 2007 - Konrad Rzeszutek <konradr@linux.vnet.ibm.com> | |
57 | * First version exposing iBFT data via a binary /sysfs. (v0.1) | |
138fe4e0 KR |
58 | */ |
59 | ||
60 | ||
61 | #include <linux/blkdev.h> | |
62 | #include <linux/capability.h> | |
63 | #include <linux/ctype.h> | |
64 | #include <linux/device.h> | |
65 | #include <linux/err.h> | |
66 | #include <linux/init.h> | |
67 | #include <linux/iscsi_ibft.h> | |
68 | #include <linux/limits.h> | |
69 | #include <linux/module.h> | |
70 | #include <linux/pci.h> | |
71 | #include <linux/slab.h> | |
72 | #include <linux/stat.h> | |
73 | #include <linux/string.h> | |
74 | #include <linux/types.h> | |
4e639fdf | 75 | #include <linux/acpi.h> |
b33a84a3 | 76 | #include <linux/iscsi_boot_sysfs.h> |
138fe4e0 | 77 | |
4e639fdf PJ |
78 | #define IBFT_ISCSI_VERSION "0.5.0" |
79 | #define IBFT_ISCSI_DATE "2010-Feb-25" | |
138fe4e0 | 80 | |
85ee7a1d JP |
81 | MODULE_AUTHOR("Peter Jones <pjones@redhat.com> and " |
82 | "Konrad Rzeszutek <ketuzsezr@darnok.org>"); | |
138fe4e0 KR |
83 | MODULE_DESCRIPTION("sysfs interface to BIOS iBFT information"); |
84 | MODULE_LICENSE("GPL"); | |
85 | MODULE_VERSION(IBFT_ISCSI_VERSION); | |
86 | ||
94bccc34 TT |
87 | #ifndef CONFIG_ISCSI_IBFT_FIND |
88 | struct acpi_table_ibft *ibft_addr; | |
89 | #endif | |
90 | ||
138fe4e0 KR |
91 | struct ibft_hdr { |
92 | u8 id; | |
93 | u8 version; | |
94 | u16 length; | |
95 | u8 index; | |
96 | u8 flags; | |
97 | } __attribute__((__packed__)); | |
98 | ||
99 | struct ibft_control { | |
100 | struct ibft_hdr hdr; | |
101 | u16 extensions; | |
102 | u16 initiator_off; | |
103 | u16 nic0_off; | |
104 | u16 tgt0_off; | |
105 | u16 nic1_off; | |
106 | u16 tgt1_off; | |
8192e60c | 107 | u16 expansion[]; |
138fe4e0 KR |
108 | } __attribute__((__packed__)); |
109 | ||
110 | struct ibft_initiator { | |
111 | struct ibft_hdr hdr; | |
112 | char isns_server[16]; | |
113 | char slp_server[16]; | |
114 | char pri_radius_server[16]; | |
115 | char sec_radius_server[16]; | |
116 | u16 initiator_name_len; | |
117 | u16 initiator_name_off; | |
118 | } __attribute__((__packed__)); | |
119 | ||
120 | struct ibft_nic { | |
121 | struct ibft_hdr hdr; | |
122 | char ip_addr[16]; | |
123 | u8 subnet_mask_prefix; | |
124 | u8 origin; | |
125 | char gateway[16]; | |
126 | char primary_dns[16]; | |
127 | char secondary_dns[16]; | |
128 | char dhcp[16]; | |
129 | u16 vlan; | |
130 | char mac[6]; | |
131 | u16 pci_bdf; | |
132 | u16 hostname_len; | |
133 | u16 hostname_off; | |
134 | } __attribute__((__packed__)); | |
135 | ||
136 | struct ibft_tgt { | |
137 | struct ibft_hdr hdr; | |
138 | char ip_addr[16]; | |
139 | u16 port; | |
140 | char lun[8]; | |
141 | u8 chap_type; | |
142 | u8 nic_assoc; | |
143 | u16 tgt_name_len; | |
144 | u16 tgt_name_off; | |
145 | u16 chap_name_len; | |
146 | u16 chap_name_off; | |
147 | u16 chap_secret_len; | |
148 | u16 chap_secret_off; | |
149 | u16 rev_chap_name_len; | |
150 | u16 rev_chap_name_off; | |
151 | u16 rev_chap_secret_len; | |
152 | u16 rev_chap_secret_off; | |
153 | } __attribute__((__packed__)); | |
154 | ||
155 | /* | |
156 | * The kobject different types and its names. | |
157 | * | |
158 | */ | |
159 | enum ibft_id { | |
160 | id_reserved = 0, /* We don't support. */ | |
161 | id_control = 1, /* Should show up only once and is not exported. */ | |
162 | id_initiator = 2, | |
163 | id_nic = 3, | |
164 | id_target = 4, | |
165 | id_extensions = 5, /* We don't support. */ | |
166 | id_end_marker, | |
167 | }; | |
168 | ||
138fe4e0 KR |
169 | /* |
170 | * The kobject and attribute structures. | |
171 | */ | |
172 | ||
173 | struct ibft_kobject { | |
4e639fdf | 174 | struct acpi_table_ibft *header; |
138fe4e0 KR |
175 | union { |
176 | struct ibft_initiator *initiator; | |
177 | struct ibft_nic *nic; | |
178 | struct ibft_tgt *tgt; | |
179 | struct ibft_hdr *hdr; | |
180 | }; | |
138fe4e0 KR |
181 | }; |
182 | ||
b33a84a3 | 183 | static struct iscsi_boot_kset *boot_kset; |
138fe4e0 | 184 | |
e6050b61 | 185 | /* fully null address */ |
138fe4e0 KR |
186 | static const char nulls[16]; |
187 | ||
e6050b61 CL |
188 | /* IPv4-mapped IPv6 ::ffff:0.0.0.0 */ |
189 | static const char mapped_nulls[16] = { 0x00, 0x00, 0x00, 0x00, | |
190 | 0x00, 0x00, 0x00, 0x00, | |
191 | 0x00, 0x00, 0xff, 0xff, | |
192 | 0x00, 0x00, 0x00, 0x00 }; | |
193 | ||
194 | static int address_not_null(u8 *ip) | |
195 | { | |
196 | return (memcmp(ip, nulls, 16) && memcmp(ip, mapped_nulls, 16)); | |
197 | } | |
198 | ||
138fe4e0 KR |
199 | /* |
200 | * Helper functions to parse data properly. | |
201 | */ | |
202 | static ssize_t sprintf_ipaddr(char *buf, u8 *ip) | |
203 | { | |
204 | char *str = buf; | |
205 | ||
206 | if (ip[0] == 0 && ip[1] == 0 && ip[2] == 0 && ip[3] == 0 && | |
207 | ip[4] == 0 && ip[5] == 0 && ip[6] == 0 && ip[7] == 0 && | |
208 | ip[8] == 0 && ip[9] == 0 && ip[10] == 0xff && ip[11] == 0xff) { | |
209 | /* | |
210 | * IPV4 | |
211 | */ | |
63779436 | 212 | str += sprintf(buf, "%pI4", ip + 12); |
138fe4e0 KR |
213 | } else { |
214 | /* | |
215 | * IPv6 | |
216 | */ | |
5b095d98 | 217 | str += sprintf(str, "%pI6", ip); |
138fe4e0 KR |
218 | } |
219 | str += sprintf(str, "\n"); | |
220 | return str - buf; | |
221 | } | |
222 | ||
223 | static ssize_t sprintf_string(char *str, int len, char *buf) | |
224 | { | |
225 | return sprintf(str, "%.*s\n", len, buf); | |
226 | } | |
227 | ||
228 | /* | |
229 | * Helper function to verify the IBFT header. | |
230 | */ | |
231 | static int ibft_verify_hdr(char *t, struct ibft_hdr *hdr, int id, int length) | |
232 | { | |
233 | if (hdr->id != id) { | |
b33a84a3 | 234 | printk(KERN_ERR "iBFT error: We expected the %s " \ |
138fe4e0 | 235 | "field header.id to have %d but " \ |
b33a84a3 | 236 | "found %d instead!\n", t, id, hdr->id); |
138fe4e0 KR |
237 | return -ENODEV; |
238 | } | |
c0840603 | 239 | if (length && hdr->length != length) { |
b33a84a3 | 240 | printk(KERN_ERR "iBFT error: We expected the %s " \ |
138fe4e0 | 241 | "field header.length to have %d but " \ |
b33a84a3 | 242 | "found %d instead!\n", t, length, hdr->length); |
138fe4e0 KR |
243 | return -ENODEV; |
244 | } | |
245 | ||
246 | return 0; | |
247 | } | |
248 | ||
138fe4e0 KR |
249 | /* |
250 | * Routines for parsing the iBFT data to be human readable. | |
251 | */ | |
b33a84a3 | 252 | static ssize_t ibft_attr_show_initiator(void *data, int type, char *buf) |
138fe4e0 | 253 | { |
b33a84a3 | 254 | struct ibft_kobject *entry = data; |
138fe4e0 KR |
255 | struct ibft_initiator *initiator = entry->initiator; |
256 | void *ibft_loc = entry->header; | |
257 | char *str = buf; | |
258 | ||
259 | if (!initiator) | |
260 | return 0; | |
261 | ||
b33a84a3 MC |
262 | switch (type) { |
263 | case ISCSI_BOOT_INI_INDEX: | |
138fe4e0 KR |
264 | str += sprintf(str, "%d\n", initiator->hdr.index); |
265 | break; | |
b33a84a3 | 266 | case ISCSI_BOOT_INI_FLAGS: |
138fe4e0 KR |
267 | str += sprintf(str, "%d\n", initiator->hdr.flags); |
268 | break; | |
b33a84a3 | 269 | case ISCSI_BOOT_INI_ISNS_SERVER: |
138fe4e0 KR |
270 | str += sprintf_ipaddr(str, initiator->isns_server); |
271 | break; | |
b33a84a3 | 272 | case ISCSI_BOOT_INI_SLP_SERVER: |
138fe4e0 KR |
273 | str += sprintf_ipaddr(str, initiator->slp_server); |
274 | break; | |
b33a84a3 | 275 | case ISCSI_BOOT_INI_PRI_RADIUS_SERVER: |
138fe4e0 KR |
276 | str += sprintf_ipaddr(str, initiator->pri_radius_server); |
277 | break; | |
b33a84a3 | 278 | case ISCSI_BOOT_INI_SEC_RADIUS_SERVER: |
138fe4e0 KR |
279 | str += sprintf_ipaddr(str, initiator->sec_radius_server); |
280 | break; | |
b33a84a3 | 281 | case ISCSI_BOOT_INI_INITIATOR_NAME: |
138fe4e0 KR |
282 | str += sprintf_string(str, initiator->initiator_name_len, |
283 | (char *)ibft_loc + | |
284 | initiator->initiator_name_off); | |
285 | break; | |
286 | default: | |
287 | break; | |
288 | } | |
289 | ||
290 | return str - buf; | |
291 | } | |
292 | ||
b33a84a3 | 293 | static ssize_t ibft_attr_show_nic(void *data, int type, char *buf) |
138fe4e0 | 294 | { |
b33a84a3 | 295 | struct ibft_kobject *entry = data; |
138fe4e0 KR |
296 | struct ibft_nic *nic = entry->nic; |
297 | void *ibft_loc = entry->header; | |
298 | char *str = buf; | |
00e7825b | 299 | __be32 val; |
138fe4e0 KR |
300 | |
301 | if (!nic) | |
302 | return 0; | |
303 | ||
b33a84a3 MC |
304 | switch (type) { |
305 | case ISCSI_BOOT_ETH_INDEX: | |
138fe4e0 KR |
306 | str += sprintf(str, "%d\n", nic->hdr.index); |
307 | break; | |
b33a84a3 | 308 | case ISCSI_BOOT_ETH_FLAGS: |
138fe4e0 KR |
309 | str += sprintf(str, "%d\n", nic->hdr.flags); |
310 | break; | |
b33a84a3 | 311 | case ISCSI_BOOT_ETH_IP_ADDR: |
138fe4e0 KR |
312 | str += sprintf_ipaddr(str, nic->ip_addr); |
313 | break; | |
b33a84a3 | 314 | case ISCSI_BOOT_ETH_SUBNET_MASK: |
00e7825b JP |
315 | val = cpu_to_be32(~((1 << (32-nic->subnet_mask_prefix))-1)); |
316 | str += sprintf(str, "%pI4", &val); | |
138fe4e0 | 317 | break; |
9a99425f HR |
318 | case ISCSI_BOOT_ETH_PREFIX_LEN: |
319 | str += sprintf(str, "%d\n", nic->subnet_mask_prefix); | |
320 | break; | |
b33a84a3 | 321 | case ISCSI_BOOT_ETH_ORIGIN: |
138fe4e0 KR |
322 | str += sprintf(str, "%d\n", nic->origin); |
323 | break; | |
b33a84a3 | 324 | case ISCSI_BOOT_ETH_GATEWAY: |
138fe4e0 KR |
325 | str += sprintf_ipaddr(str, nic->gateway); |
326 | break; | |
b33a84a3 | 327 | case ISCSI_BOOT_ETH_PRIMARY_DNS: |
138fe4e0 KR |
328 | str += sprintf_ipaddr(str, nic->primary_dns); |
329 | break; | |
b33a84a3 | 330 | case ISCSI_BOOT_ETH_SECONDARY_DNS: |
138fe4e0 KR |
331 | str += sprintf_ipaddr(str, nic->secondary_dns); |
332 | break; | |
b33a84a3 | 333 | case ISCSI_BOOT_ETH_DHCP: |
138fe4e0 KR |
334 | str += sprintf_ipaddr(str, nic->dhcp); |
335 | break; | |
b33a84a3 | 336 | case ISCSI_BOOT_ETH_VLAN: |
138fe4e0 KR |
337 | str += sprintf(str, "%d\n", nic->vlan); |
338 | break; | |
b33a84a3 | 339 | case ISCSI_BOOT_ETH_MAC: |
2c352948 | 340 | str += sprintf(str, "%pM\n", nic->mac); |
138fe4e0 | 341 | break; |
b33a84a3 | 342 | case ISCSI_BOOT_ETH_HOSTNAME: |
138fe4e0 KR |
343 | str += sprintf_string(str, nic->hostname_len, |
344 | (char *)ibft_loc + nic->hostname_off); | |
345 | break; | |
346 | default: | |
347 | break; | |
348 | } | |
349 | ||
350 | return str - buf; | |
351 | }; | |
352 | ||
b33a84a3 | 353 | static ssize_t ibft_attr_show_target(void *data, int type, char *buf) |
138fe4e0 | 354 | { |
b33a84a3 | 355 | struct ibft_kobject *entry = data; |
138fe4e0 KR |
356 | struct ibft_tgt *tgt = entry->tgt; |
357 | void *ibft_loc = entry->header; | |
358 | char *str = buf; | |
359 | int i; | |
360 | ||
361 | if (!tgt) | |
362 | return 0; | |
363 | ||
b33a84a3 MC |
364 | switch (type) { |
365 | case ISCSI_BOOT_TGT_INDEX: | |
138fe4e0 KR |
366 | str += sprintf(str, "%d\n", tgt->hdr.index); |
367 | break; | |
b33a84a3 | 368 | case ISCSI_BOOT_TGT_FLAGS: |
138fe4e0 KR |
369 | str += sprintf(str, "%d\n", tgt->hdr.flags); |
370 | break; | |
b33a84a3 | 371 | case ISCSI_BOOT_TGT_IP_ADDR: |
138fe4e0 KR |
372 | str += sprintf_ipaddr(str, tgt->ip_addr); |
373 | break; | |
b33a84a3 | 374 | case ISCSI_BOOT_TGT_PORT: |
138fe4e0 KR |
375 | str += sprintf(str, "%d\n", tgt->port); |
376 | break; | |
b33a84a3 | 377 | case ISCSI_BOOT_TGT_LUN: |
138fe4e0 KR |
378 | for (i = 0; i < 8; i++) |
379 | str += sprintf(str, "%x", (u8)tgt->lun[i]); | |
380 | str += sprintf(str, "\n"); | |
381 | break; | |
b33a84a3 | 382 | case ISCSI_BOOT_TGT_NIC_ASSOC: |
138fe4e0 KR |
383 | str += sprintf(str, "%d\n", tgt->nic_assoc); |
384 | break; | |
b33a84a3 | 385 | case ISCSI_BOOT_TGT_CHAP_TYPE: |
138fe4e0 KR |
386 | str += sprintf(str, "%d\n", tgt->chap_type); |
387 | break; | |
b33a84a3 | 388 | case ISCSI_BOOT_TGT_NAME: |
138fe4e0 KR |
389 | str += sprintf_string(str, tgt->tgt_name_len, |
390 | (char *)ibft_loc + tgt->tgt_name_off); | |
391 | break; | |
b33a84a3 | 392 | case ISCSI_BOOT_TGT_CHAP_NAME: |
138fe4e0 KR |
393 | str += sprintf_string(str, tgt->chap_name_len, |
394 | (char *)ibft_loc + tgt->chap_name_off); | |
395 | break; | |
b33a84a3 | 396 | case ISCSI_BOOT_TGT_CHAP_SECRET: |
138fe4e0 KR |
397 | str += sprintf_string(str, tgt->chap_secret_len, |
398 | (char *)ibft_loc + tgt->chap_secret_off); | |
399 | break; | |
b33a84a3 | 400 | case ISCSI_BOOT_TGT_REV_CHAP_NAME: |
138fe4e0 KR |
401 | str += sprintf_string(str, tgt->rev_chap_name_len, |
402 | (char *)ibft_loc + | |
403 | tgt->rev_chap_name_off); | |
404 | break; | |
b33a84a3 | 405 | case ISCSI_BOOT_TGT_REV_CHAP_SECRET: |
138fe4e0 KR |
406 | str += sprintf_string(str, tgt->rev_chap_secret_len, |
407 | (char *)ibft_loc + | |
408 | tgt->rev_chap_secret_off); | |
409 | break; | |
410 | default: | |
411 | break; | |
412 | } | |
413 | ||
414 | return str - buf; | |
415 | } | |
416 | ||
b3c8eb50 DB |
417 | static ssize_t ibft_attr_show_acpitbl(void *data, int type, char *buf) |
418 | { | |
419 | struct ibft_kobject *entry = data; | |
420 | char *str = buf; | |
421 | ||
422 | switch (type) { | |
423 | case ISCSI_BOOT_ACPITBL_SIGNATURE: | |
32786755 | 424 | str += sprintf_string(str, ACPI_NAMESEG_SIZE, |
b3c8eb50 DB |
425 | entry->header->header.signature); |
426 | break; | |
427 | case ISCSI_BOOT_ACPITBL_OEM_ID: | |
428 | str += sprintf_string(str, ACPI_OEM_ID_SIZE, | |
429 | entry->header->header.oem_id); | |
430 | break; | |
431 | case ISCSI_BOOT_ACPITBL_OEM_TABLE_ID: | |
432 | str += sprintf_string(str, ACPI_OEM_TABLE_ID_SIZE, | |
433 | entry->header->header.oem_table_id); | |
434 | break; | |
435 | default: | |
436 | break; | |
437 | } | |
438 | ||
439 | return str - buf; | |
440 | } | |
441 | ||
138fe4e0 KR |
442 | static int __init ibft_check_device(void) |
443 | { | |
444 | int len; | |
445 | u8 *pos; | |
446 | u8 csum = 0; | |
447 | ||
4e639fdf | 448 | len = ibft_addr->header.length; |
138fe4e0 KR |
449 | |
450 | /* Sanity checking of iBFT. */ | |
4e639fdf | 451 | if (ibft_addr->header.revision != 1) { |
138fe4e0 | 452 | printk(KERN_ERR "iBFT module supports only revision 1, " \ |
4e639fdf PJ |
453 | "while this is %d.\n", |
454 | ibft_addr->header.revision); | |
138fe4e0 KR |
455 | return -ENOENT; |
456 | } | |
457 | for (pos = (u8 *)ibft_addr; pos < (u8 *)ibft_addr + len; pos++) | |
458 | csum += *pos; | |
459 | ||
460 | if (csum) { | |
461 | printk(KERN_ERR "iBFT has incorrect checksum (0x%x)!\n", csum); | |
462 | return -ENOENT; | |
463 | } | |
464 | ||
465 | return 0; | |
466 | } | |
467 | ||
b33a84a3 MC |
468 | /* |
469 | * Helper routiners to check to determine if the entry is valid | |
470 | * in the proper iBFT structure. | |
471 | */ | |
587a1f16 | 472 | static umode_t ibft_check_nic_for(void *data, int type) |
b33a84a3 MC |
473 | { |
474 | struct ibft_kobject *entry = data; | |
475 | struct ibft_nic *nic = entry->nic; | |
587a1f16 | 476 | umode_t rc = 0; |
b33a84a3 MC |
477 | |
478 | switch (type) { | |
479 | case ISCSI_BOOT_ETH_INDEX: | |
480 | case ISCSI_BOOT_ETH_FLAGS: | |
481 | rc = S_IRUGO; | |
482 | break; | |
483 | case ISCSI_BOOT_ETH_IP_ADDR: | |
e6050b61 | 484 | if (address_not_null(nic->ip_addr)) |
b33a84a3 MC |
485 | rc = S_IRUGO; |
486 | break; | |
9a99425f | 487 | case ISCSI_BOOT_ETH_PREFIX_LEN: |
b33a84a3 MC |
488 | case ISCSI_BOOT_ETH_SUBNET_MASK: |
489 | if (nic->subnet_mask_prefix) | |
490 | rc = S_IRUGO; | |
491 | break; | |
492 | case ISCSI_BOOT_ETH_ORIGIN: | |
493 | rc = S_IRUGO; | |
494 | break; | |
495 | case ISCSI_BOOT_ETH_GATEWAY: | |
e6050b61 | 496 | if (address_not_null(nic->gateway)) |
b33a84a3 MC |
497 | rc = S_IRUGO; |
498 | break; | |
499 | case ISCSI_BOOT_ETH_PRIMARY_DNS: | |
e6050b61 | 500 | if (address_not_null(nic->primary_dns)) |
b33a84a3 MC |
501 | rc = S_IRUGO; |
502 | break; | |
503 | case ISCSI_BOOT_ETH_SECONDARY_DNS: | |
e6050b61 | 504 | if (address_not_null(nic->secondary_dns)) |
b33a84a3 MC |
505 | rc = S_IRUGO; |
506 | break; | |
507 | case ISCSI_BOOT_ETH_DHCP: | |
e6050b61 | 508 | if (address_not_null(nic->dhcp)) |
b33a84a3 MC |
509 | rc = S_IRUGO; |
510 | break; | |
511 | case ISCSI_BOOT_ETH_VLAN: | |
512 | case ISCSI_BOOT_ETH_MAC: | |
513 | rc = S_IRUGO; | |
514 | break; | |
515 | case ISCSI_BOOT_ETH_HOSTNAME: | |
516 | if (nic->hostname_off) | |
517 | rc = S_IRUGO; | |
518 | break; | |
519 | default: | |
520 | break; | |
521 | } | |
522 | ||
523 | return rc; | |
524 | } | |
525 | ||
587a1f16 | 526 | static umode_t __init ibft_check_tgt_for(void *data, int type) |
b33a84a3 MC |
527 | { |
528 | struct ibft_kobject *entry = data; | |
529 | struct ibft_tgt *tgt = entry->tgt; | |
587a1f16 | 530 | umode_t rc = 0; |
b33a84a3 MC |
531 | |
532 | switch (type) { | |
533 | case ISCSI_BOOT_TGT_INDEX: | |
534 | case ISCSI_BOOT_TGT_FLAGS: | |
535 | case ISCSI_BOOT_TGT_IP_ADDR: | |
536 | case ISCSI_BOOT_TGT_PORT: | |
537 | case ISCSI_BOOT_TGT_LUN: | |
538 | case ISCSI_BOOT_TGT_NIC_ASSOC: | |
539 | case ISCSI_BOOT_TGT_CHAP_TYPE: | |
540 | rc = S_IRUGO; | |
df997abe | 541 | break; |
b33a84a3 MC |
542 | case ISCSI_BOOT_TGT_NAME: |
543 | if (tgt->tgt_name_len) | |
544 | rc = S_IRUGO; | |
545 | break; | |
546 | case ISCSI_BOOT_TGT_CHAP_NAME: | |
547 | case ISCSI_BOOT_TGT_CHAP_SECRET: | |
548 | if (tgt->chap_name_len) | |
549 | rc = S_IRUGO; | |
550 | break; | |
551 | case ISCSI_BOOT_TGT_REV_CHAP_NAME: | |
552 | case ISCSI_BOOT_TGT_REV_CHAP_SECRET: | |
553 | if (tgt->rev_chap_name_len) | |
554 | rc = S_IRUGO; | |
555 | break; | |
556 | default: | |
557 | break; | |
558 | } | |
559 | ||
560 | return rc; | |
561 | } | |
562 | ||
587a1f16 | 563 | static umode_t __init ibft_check_initiator_for(void *data, int type) |
b33a84a3 MC |
564 | { |
565 | struct ibft_kobject *entry = data; | |
566 | struct ibft_initiator *init = entry->initiator; | |
587a1f16 | 567 | umode_t rc = 0; |
b33a84a3 MC |
568 | |
569 | switch (type) { | |
570 | case ISCSI_BOOT_INI_INDEX: | |
571 | case ISCSI_BOOT_INI_FLAGS: | |
572 | rc = S_IRUGO; | |
573 | break; | |
574 | case ISCSI_BOOT_INI_ISNS_SERVER: | |
e6050b61 | 575 | if (address_not_null(init->isns_server)) |
b33a84a3 MC |
576 | rc = S_IRUGO; |
577 | break; | |
578 | case ISCSI_BOOT_INI_SLP_SERVER: | |
e6050b61 | 579 | if (address_not_null(init->slp_server)) |
b33a84a3 MC |
580 | rc = S_IRUGO; |
581 | break; | |
582 | case ISCSI_BOOT_INI_PRI_RADIUS_SERVER: | |
e6050b61 | 583 | if (address_not_null(init->pri_radius_server)) |
b33a84a3 MC |
584 | rc = S_IRUGO; |
585 | break; | |
586 | case ISCSI_BOOT_INI_SEC_RADIUS_SERVER: | |
e6050b61 | 587 | if (address_not_null(init->sec_radius_server)) |
b33a84a3 MC |
588 | rc = S_IRUGO; |
589 | break; | |
590 | case ISCSI_BOOT_INI_INITIATOR_NAME: | |
591 | if (init->initiator_name_len) | |
592 | rc = S_IRUGO; | |
593 | break; | |
594 | default: | |
595 | break; | |
596 | } | |
597 | ||
598 | return rc; | |
599 | } | |
600 | ||
b3c8eb50 DB |
601 | static umode_t __init ibft_check_acpitbl_for(void *data, int type) |
602 | { | |
603 | ||
604 | umode_t rc = 0; | |
605 | ||
606 | switch (type) { | |
607 | case ISCSI_BOOT_ACPITBL_SIGNATURE: | |
608 | case ISCSI_BOOT_ACPITBL_OEM_ID: | |
609 | case ISCSI_BOOT_ACPITBL_OEM_TABLE_ID: | |
610 | rc = S_IRUGO; | |
611 | break; | |
612 | default: | |
613 | break; | |
614 | } | |
615 | ||
616 | return rc; | |
617 | } | |
618 | ||
f457a46f MC |
619 | static void ibft_kobj_release(void *data) |
620 | { | |
621 | kfree(data); | |
622 | } | |
623 | ||
138fe4e0 KR |
624 | /* |
625 | * Helper function for ibft_register_kobjects. | |
626 | */ | |
4e639fdf | 627 | static int __init ibft_create_kobject(struct acpi_table_ibft *header, |
b33a84a3 | 628 | struct ibft_hdr *hdr) |
138fe4e0 | 629 | { |
b33a84a3 | 630 | struct iscsi_boot_kobj *boot_kobj = NULL; |
138fe4e0 KR |
631 | struct ibft_kobject *ibft_kobj = NULL; |
632 | struct ibft_nic *nic = (struct ibft_nic *)hdr; | |
633 | struct pci_dev *pci_dev; | |
634 | int rc = 0; | |
635 | ||
636 | ibft_kobj = kzalloc(sizeof(*ibft_kobj), GFP_KERNEL); | |
637 | if (!ibft_kobj) | |
638 | return -ENOMEM; | |
639 | ||
640 | ibft_kobj->header = header; | |
641 | ibft_kobj->hdr = hdr; | |
642 | ||
643 | switch (hdr->id) { | |
644 | case id_initiator: | |
645 | rc = ibft_verify_hdr("initiator", hdr, id_initiator, | |
646 | sizeof(*ibft_kobj->initiator)); | |
b33a84a3 MC |
647 | if (rc) |
648 | break; | |
649 | ||
650 | boot_kobj = iscsi_boot_create_initiator(boot_kset, hdr->index, | |
651 | ibft_kobj, | |
652 | ibft_attr_show_initiator, | |
f457a46f MC |
653 | ibft_check_initiator_for, |
654 | ibft_kobj_release); | |
b33a84a3 MC |
655 | if (!boot_kobj) { |
656 | rc = -ENOMEM; | |
657 | goto free_ibft_obj; | |
658 | } | |
138fe4e0 KR |
659 | break; |
660 | case id_nic: | |
661 | rc = ibft_verify_hdr("ethernet", hdr, id_nic, | |
662 | sizeof(*ibft_kobj->nic)); | |
b33a84a3 MC |
663 | if (rc) |
664 | break; | |
665 | ||
666 | boot_kobj = iscsi_boot_create_ethernet(boot_kset, hdr->index, | |
667 | ibft_kobj, | |
668 | ibft_attr_show_nic, | |
f457a46f MC |
669 | ibft_check_nic_for, |
670 | ibft_kobj_release); | |
b33a84a3 MC |
671 | if (!boot_kobj) { |
672 | rc = -ENOMEM; | |
673 | goto free_ibft_obj; | |
674 | } | |
138fe4e0 KR |
675 | break; |
676 | case id_target: | |
677 | rc = ibft_verify_hdr("target", hdr, id_target, | |
678 | sizeof(*ibft_kobj->tgt)); | |
b33a84a3 MC |
679 | if (rc) |
680 | break; | |
681 | ||
682 | boot_kobj = iscsi_boot_create_target(boot_kset, hdr->index, | |
683 | ibft_kobj, | |
684 | ibft_attr_show_target, | |
f457a46f MC |
685 | ibft_check_tgt_for, |
686 | ibft_kobj_release); | |
b33a84a3 MC |
687 | if (!boot_kobj) { |
688 | rc = -ENOMEM; | |
689 | goto free_ibft_obj; | |
690 | } | |
138fe4e0 KR |
691 | break; |
692 | case id_reserved: | |
693 | case id_control: | |
694 | case id_extensions: | |
695 | /* Fields which we don't support. Ignore them */ | |
696 | rc = 1; | |
697 | break; | |
698 | default: | |
699 | printk(KERN_ERR "iBFT has unknown structure type (%d). " \ | |
700 | "Report this bug to %.6s!\n", hdr->id, | |
4e639fdf | 701 | header->header.oem_id); |
138fe4e0 KR |
702 | rc = 1; |
703 | break; | |
704 | } | |
705 | ||
706 | if (rc) { | |
707 | /* Skip adding this kobject, but exit with non-fatal error. */ | |
b33a84a3 MC |
708 | rc = 0; |
709 | goto free_ibft_obj; | |
138fe4e0 KR |
710 | } |
711 | ||
138fe4e0 KR |
712 | if (hdr->id == id_nic) { |
713 | /* | |
714 | * We don't search for the device in other domains than | |
715 | * zero. This is because on x86 platforms the BIOS | |
716 | * executes only devices which are in domain 0. Furthermore, the | |
717 | * iBFT spec doesn't have a domain id field :-( | |
718 | */ | |
cb5264be SK |
719 | pci_dev = pci_get_domain_bus_and_slot(0, |
720 | (nic->pci_bdf & 0xff00) >> 8, | |
721 | (nic->pci_bdf & 0xff)); | |
138fe4e0 | 722 | if (pci_dev) { |
b33a84a3 | 723 | rc = sysfs_create_link(&boot_kobj->kobj, |
138fe4e0 KR |
724 | &pci_dev->dev.kobj, "device"); |
725 | pci_dev_put(pci_dev); | |
726 | } | |
727 | } | |
b33a84a3 | 728 | return 0; |
138fe4e0 | 729 | |
b33a84a3 MC |
730 | free_ibft_obj: |
731 | kfree(ibft_kobj); | |
138fe4e0 | 732 | return rc; |
138fe4e0 KR |
733 | } |
734 | ||
735 | /* | |
736 | * Scan the IBFT table structure for the NIC and Target fields. When | |
737 | * found add them on the passed-in list. We do not support the other | |
738 | * fields at this point, so they are skipped. | |
739 | */ | |
b33a84a3 | 740 | static int __init ibft_register_kobjects(struct acpi_table_ibft *header) |
138fe4e0 KR |
741 | { |
742 | struct ibft_control *control = NULL; | |
b3c8eb50 DB |
743 | struct iscsi_boot_kobj *boot_kobj; |
744 | struct ibft_kobject *ibft_kobj; | |
138fe4e0 KR |
745 | void *ptr, *end; |
746 | int rc = 0; | |
747 | u16 offset; | |
748 | u16 eot_offset; | |
749 | ||
750 | control = (void *)header + sizeof(*header); | |
751 | end = (void *)control + control->hdr.length; | |
4e639fdf | 752 | eot_offset = (void *)header + header->header.length - (void *)control; |
c0840603 | 753 | rc = ibft_verify_hdr("control", (struct ibft_hdr *)control, id_control, 0); |
138fe4e0 KR |
754 | |
755 | /* iBFT table safety checking */ | |
756 | rc |= ((control->hdr.index) ? -ENODEV : 0); | |
c0840603 | 757 | rc |= ((control->hdr.length < sizeof(*control)) ? -ENODEV : 0); |
138fe4e0 KR |
758 | if (rc) { |
759 | printk(KERN_ERR "iBFT error: Control header is invalid!\n"); | |
760 | return rc; | |
761 | } | |
c0840603 | 762 | for (ptr = &control->initiator_off; ptr + sizeof(u16) <= end; ptr += sizeof(u16)) { |
138fe4e0 | 763 | offset = *(u16 *)ptr; |
4e639fdf PJ |
764 | if (offset && offset < header->header.length && |
765 | offset < eot_offset) { | |
138fe4e0 | 766 | rc = ibft_create_kobject(header, |
b33a84a3 | 767 | (void *)header + offset); |
138fe4e0 KR |
768 | if (rc) |
769 | break; | |
770 | } | |
771 | } | |
b3c8eb50 DB |
772 | if (rc) |
773 | return rc; | |
774 | ||
775 | ibft_kobj = kzalloc(sizeof(*ibft_kobj), GFP_KERNEL); | |
776 | if (!ibft_kobj) | |
777 | return -ENOMEM; | |
778 | ||
779 | ibft_kobj->header = header; | |
780 | ibft_kobj->hdr = NULL; /*for ibft_unregister*/ | |
781 | ||
782 | boot_kobj = iscsi_boot_create_acpitbl(boot_kset, 0, | |
783 | ibft_kobj, | |
784 | ibft_attr_show_acpitbl, | |
785 | ibft_check_acpitbl_for, | |
786 | ibft_kobj_release); | |
787 | if (!boot_kobj) { | |
788 | kfree(ibft_kobj); | |
789 | rc = -ENOMEM; | |
790 | } | |
138fe4e0 KR |
791 | |
792 | return rc; | |
793 | } | |
794 | ||
b33a84a3 | 795 | static void ibft_unregister(void) |
138fe4e0 | 796 | { |
b33a84a3 MC |
797 | struct iscsi_boot_kobj *boot_kobj, *tmp_kobj; |
798 | struct ibft_kobject *ibft_kobj; | |
799 | ||
800 | list_for_each_entry_safe(boot_kobj, tmp_kobj, | |
801 | &boot_kset->kobj_list, list) { | |
802 | ibft_kobj = boot_kobj->data; | |
b3c8eb50 | 803 | if (ibft_kobj->hdr && ibft_kobj->hdr->id == id_nic) |
b33a84a3 | 804 | sysfs_remove_link(&boot_kobj->kobj, "device"); |
138fe4e0 | 805 | }; |
138fe4e0 KR |
806 | } |
807 | ||
b33a84a3 | 808 | static void ibft_cleanup(void) |
138fe4e0 | 809 | { |
a12415ff KRW |
810 | if (boot_kset) { |
811 | ibft_unregister(); | |
812 | iscsi_boot_destroy_kset(boot_kset); | |
813 | } | |
138fe4e0 KR |
814 | } |
815 | ||
b33a84a3 | 816 | static void __exit ibft_exit(void) |
138fe4e0 | 817 | { |
b33a84a3 | 818 | ibft_cleanup(); |
138fe4e0 KR |
819 | } |
820 | ||
935a9fee YL |
821 | #ifdef CONFIG_ACPI |
822 | static const struct { | |
823 | char *sign; | |
824 | } ibft_signs[] = { | |
825 | /* | |
826 | * One spec says "IBFT", the other says "iBFT". We have to check | |
827 | * for both. | |
828 | */ | |
829 | { ACPI_SIG_IBFT }, | |
830 | { "iBFT" }, | |
629c27aa | 831 | { "BIFT" }, /* Broadcom iSCSI Offload */ |
935a9fee YL |
832 | }; |
833 | ||
834 | static void __init acpi_find_ibft_region(void) | |
835 | { | |
836 | int i; | |
837 | struct acpi_table_header *table = NULL; | |
838 | ||
839 | if (acpi_disabled) | |
840 | return; | |
841 | ||
842 | for (i = 0; i < ARRAY_SIZE(ibft_signs) && !ibft_addr; i++) { | |
843 | acpi_get_table(ibft_signs[i].sign, 0, &table); | |
844 | ibft_addr = (struct acpi_table_ibft *)table; | |
845 | } | |
846 | } | |
847 | #else | |
848 | static void __init acpi_find_ibft_region(void) | |
849 | { | |
850 | } | |
851 | #endif | |
852 | ||
138fe4e0 KR |
853 | /* |
854 | * ibft_init() - creates sysfs tree entries for the iBFT data. | |
855 | */ | |
856 | static int __init ibft_init(void) | |
857 | { | |
858 | int rc = 0; | |
859 | ||
935a9fee YL |
860 | /* |
861 | As on UEFI systems the setup_arch()/find_ibft_region() | |
862 | is called before ACPI tables are parsed and it only does | |
863 | legacy finding. | |
864 | */ | |
865 | if (!ibft_addr) | |
866 | acpi_find_ibft_region(); | |
867 | ||
138fe4e0 | 868 | if (ibft_addr) { |
935a9fee | 869 | pr_info("iBFT detected.\n"); |
138fe4e0 KR |
870 | |
871 | rc = ibft_check_device(); | |
872 | if (rc) | |
b33a84a3 | 873 | return rc; |
138fe4e0 | 874 | |
b33a84a3 MC |
875 | boot_kset = iscsi_boot_create_kset("ibft"); |
876 | if (!boot_kset) | |
877 | return -ENOMEM; | |
138fe4e0 | 878 | |
b33a84a3 MC |
879 | /* Scan the IBFT for data and register the kobjects. */ |
880 | rc = ibft_register_kobjects(ibft_addr); | |
138fe4e0 KR |
881 | if (rc) |
882 | goto out_free; | |
883 | } else | |
884 | printk(KERN_INFO "No iBFT detected.\n"); | |
885 | ||
886 | return 0; | |
887 | ||
888 | out_free: | |
b33a84a3 | 889 | ibft_cleanup(); |
138fe4e0 KR |
890 | return rc; |
891 | } | |
892 | ||
138fe4e0 KR |
893 | module_init(ibft_init); |
894 | module_exit(ibft_exit); |