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