diff options
Diffstat (limited to 'drivers/firmware')
-rw-r--r-- | drivers/firmware/Kconfig | 61 | ||||
-rw-r--r-- | drivers/firmware/Makefile | 6 | ||||
-rw-r--r-- | drivers/firmware/edd.c | 790 | ||||
-rw-r--r-- | drivers/firmware/efivars.c | 781 | ||||
-rw-r--r-- | drivers/firmware/pcdp.c | 100 | ||||
-rw-r--r-- | drivers/firmware/pcdp.h | 84 |
6 files changed, 1822 insertions, 0 deletions
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig new file mode 100644 index 000000000000..5b29c3b2a331 --- /dev/null +++ b/drivers/firmware/Kconfig | |||
@@ -0,0 +1,61 @@ | |||
1 | # | ||
2 | # For a description of the syntax of this configuration file, | ||
3 | # see Documentation/kbuild/kconfig-language.txt. | ||
4 | # | ||
5 | |||
6 | menu "Firmware Drivers" | ||
7 | |||
8 | config EDD | ||
9 | tristate "BIOS Enhanced Disk Drive calls determine boot disk (EXPERIMENTAL)" | ||
10 | depends on EXPERIMENTAL | ||
11 | depends on !IA64 | ||
12 | help | ||
13 | Say Y or M here if you want to enable BIOS Enhanced Disk Drive | ||
14 | Services real mode BIOS calls to determine which disk | ||
15 | BIOS tries boot from. This information is then exported via sysfs. | ||
16 | |||
17 | This option is experimental and is known to fail to boot on some | ||
18 | obscure configurations. Most disk controller BIOS vendors do | ||
19 | not yet implement this feature. | ||
20 | |||
21 | config EFI_VARS | ||
22 | tristate "EFI Variable Support via sysfs" | ||
23 | depends on EFI | ||
24 | default n | ||
25 | help | ||
26 | If you say Y here, you are able to get EFI (Extensible Firmware | ||
27 | Interface) variable information via sysfs. You may read, | ||
28 | write, create, and destroy EFI variables through this interface. | ||
29 | |||
30 | Note that using this driver in concert with efibootmgr requires | ||
31 | at least test release version 0.5.0-test3 or later, which is | ||
32 | available from Matt Domsch's website located at: | ||
33 | <http://linux.dell.com/efibootmgr/testing/efibootmgr-0.5.0-test3.tar.gz> | ||
34 | |||
35 | Subsequent efibootmgr releases may be found at: | ||
36 | <http://linux.dell.com/efibootmgr> | ||
37 | |||
38 | config EFI_PCDP | ||
39 | bool "Console device selection via EFI PCDP or HCDP table" | ||
40 | depends on ACPI && EFI && IA64 | ||
41 | default y if IA64 | ||
42 | help | ||
43 | If your firmware supplies the PCDP table, and you want to | ||
44 | automatically use the primary console device it describes | ||
45 | as the Linux console, say Y here. | ||
46 | |||
47 | If your firmware supplies the HCDP table, and you want to | ||
48 | use the first serial port it describes as the Linux console, | ||
49 | say Y here. If your EFI ConOut path contains only a UART | ||
50 | device, it will become the console automatically. Otherwise, | ||
51 | you must specify the "console=hcdp" kernel boot argument. | ||
52 | |||
53 | Neither the PCDP nor the HCDP affects naming of serial devices, | ||
54 | so a serial console may be /dev/ttyS0, /dev/ttyS1, etc, depending | ||
55 | on how the driver discovers devices. | ||
56 | |||
57 | You must also enable the appropriate drivers (serial, VGA, etc.) | ||
58 | |||
59 | See <http://www.dig64.org/specifications/DIG64_HCDPv20_042804.pdf> | ||
60 | |||
61 | endmenu | ||
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile new file mode 100644 index 000000000000..90fd0b26db8b --- /dev/null +++ b/drivers/firmware/Makefile | |||
@@ -0,0 +1,6 @@ | |||
1 | # | ||
2 | # Makefile for the linux kernel. | ||
3 | # | ||
4 | obj-$(CONFIG_EDD) += edd.o | ||
5 | obj-$(CONFIG_EFI_VARS) += efivars.o | ||
6 | obj-$(CONFIG_EFI_PCDP) += pcdp.o | ||
diff --git a/drivers/firmware/edd.c b/drivers/firmware/edd.c new file mode 100644 index 000000000000..33b669e6f977 --- /dev/null +++ b/drivers/firmware/edd.c | |||
@@ -0,0 +1,790 @@ | |||
1 | /* | ||
2 | * linux/arch/i386/kernel/edd.c | ||
3 | * Copyright (C) 2002, 2003, 2004 Dell Inc. | ||
4 | * by Matt Domsch <Matt_Domsch@dell.com> | ||
5 | * disk signature by Matt Domsch, Andrew Wilks, and Sandeep K. Shandilya | ||
6 | * legacy CHS by Patrick J. LoPresti <patl@users.sourceforge.net> | ||
7 | * | ||
8 | * BIOS Enhanced Disk Drive Services (EDD) | ||
9 | * conformant to T13 Committee www.t13.org | ||
10 | * projects 1572D, 1484D, 1386D, 1226DT | ||
11 | * | ||
12 | * This code takes information provided by BIOS EDD calls | ||
13 | * fn41 - Check Extensions Present and | ||
14 | * fn48 - Get Device Parametes with EDD extensions | ||
15 | * made in setup.S, copied to safe structures in setup.c, | ||
16 | * and presents it in sysfs. | ||
17 | * | ||
18 | * Please see http://linux.dell.com/edd30/results.html for | ||
19 | * the list of BIOSs which have been reported to implement EDD. | ||
20 | * | ||
21 | * This program is free software; you can redistribute it and/or modify | ||
22 | * it under the terms of the GNU General Public License v2.0 as published by | ||
23 | * the Free Software Foundation | ||
24 | * | ||
25 | * This program is distributed in the hope that it will be useful, | ||
26 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
27 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
28 | * GNU General Public License for more details. | ||
29 | * | ||
30 | */ | ||
31 | |||
32 | #include <linux/module.h> | ||
33 | #include <linux/string.h> | ||
34 | #include <linux/types.h> | ||
35 | #include <linux/init.h> | ||
36 | #include <linux/stat.h> | ||
37 | #include <linux/err.h> | ||
38 | #include <linux/ctype.h> | ||
39 | #include <linux/slab.h> | ||
40 | #include <linux/limits.h> | ||
41 | #include <linux/device.h> | ||
42 | #include <linux/pci.h> | ||
43 | #include <linux/blkdev.h> | ||
44 | #include <linux/edd.h> | ||
45 | |||
46 | #define EDD_VERSION "0.16" | ||
47 | #define EDD_DATE "2004-Jun-25" | ||
48 | |||
49 | MODULE_AUTHOR("Matt Domsch <Matt_Domsch@Dell.com>"); | ||
50 | MODULE_DESCRIPTION("sysfs interface to BIOS EDD information"); | ||
51 | MODULE_LICENSE("GPL"); | ||
52 | MODULE_VERSION(EDD_VERSION); | ||
53 | |||
54 | #define left (PAGE_SIZE - (p - buf) - 1) | ||
55 | |||
56 | struct edd_device { | ||
57 | unsigned int index; | ||
58 | unsigned int mbr_signature; | ||
59 | struct edd_info *info; | ||
60 | struct kobject kobj; | ||
61 | }; | ||
62 | |||
63 | struct edd_attribute { | ||
64 | struct attribute attr; | ||
65 | ssize_t(*show) (struct edd_device * edev, char *buf); | ||
66 | int (*test) (struct edd_device * edev); | ||
67 | }; | ||
68 | |||
69 | /* forward declarations */ | ||
70 | static int edd_dev_is_type(struct edd_device *edev, const char *type); | ||
71 | static struct pci_dev *edd_get_pci_dev(struct edd_device *edev); | ||
72 | |||
73 | static struct edd_device *edd_devices[EDD_MBR_SIG_MAX]; | ||
74 | |||
75 | #define EDD_DEVICE_ATTR(_name,_mode,_show,_test) \ | ||
76 | struct edd_attribute edd_attr_##_name = { \ | ||
77 | .attr = {.name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE }, \ | ||
78 | .show = _show, \ | ||
79 | .test = _test, \ | ||
80 | }; | ||
81 | |||
82 | static int | ||
83 | edd_has_mbr_signature(struct edd_device *edev) | ||
84 | { | ||
85 | return edev->index < min_t(unsigned char, edd.mbr_signature_nr, EDD_MBR_SIG_MAX); | ||
86 | } | ||
87 | |||
88 | static int | ||
89 | edd_has_edd_info(struct edd_device *edev) | ||
90 | { | ||
91 | return edev->index < min_t(unsigned char, edd.edd_info_nr, EDDMAXNR); | ||
92 | } | ||
93 | |||
94 | static inline struct edd_info * | ||
95 | edd_dev_get_info(struct edd_device *edev) | ||
96 | { | ||
97 | return edev->info; | ||
98 | } | ||
99 | |||
100 | static inline void | ||
101 | edd_dev_set_info(struct edd_device *edev, int i) | ||
102 | { | ||
103 | edev->index = i; | ||
104 | if (edd_has_mbr_signature(edev)) | ||
105 | edev->mbr_signature = edd.mbr_signature[i]; | ||
106 | if (edd_has_edd_info(edev)) | ||
107 | edev->info = &edd.edd_info[i]; | ||
108 | } | ||
109 | |||
110 | #define to_edd_attr(_attr) container_of(_attr,struct edd_attribute,attr) | ||
111 | #define to_edd_device(obj) container_of(obj,struct edd_device,kobj) | ||
112 | |||
113 | static ssize_t | ||
114 | edd_attr_show(struct kobject * kobj, struct attribute *attr, char *buf) | ||
115 | { | ||
116 | struct edd_device *dev = to_edd_device(kobj); | ||
117 | struct edd_attribute *edd_attr = to_edd_attr(attr); | ||
118 | ssize_t ret = 0; | ||
119 | |||
120 | if (edd_attr->show) | ||
121 | ret = edd_attr->show(dev, buf); | ||
122 | return ret; | ||
123 | } | ||
124 | |||
125 | static struct sysfs_ops edd_attr_ops = { | ||
126 | .show = edd_attr_show, | ||
127 | }; | ||
128 | |||
129 | static ssize_t | ||
130 | edd_show_host_bus(struct edd_device *edev, char *buf) | ||
131 | { | ||
132 | struct edd_info *info; | ||
133 | char *p = buf; | ||
134 | int i; | ||
135 | |||
136 | if (!edev) | ||
137 | return -EINVAL; | ||
138 | info = edd_dev_get_info(edev); | ||
139 | if (!info || !buf) | ||
140 | return -EINVAL; | ||
141 | |||
142 | for (i = 0; i < 4; i++) { | ||
143 | if (isprint(info->params.host_bus_type[i])) { | ||
144 | p += scnprintf(p, left, "%c", info->params.host_bus_type[i]); | ||
145 | } else { | ||
146 | p += scnprintf(p, left, " "); | ||
147 | } | ||
148 | } | ||
149 | |||
150 | if (!strncmp(info->params.host_bus_type, "ISA", 3)) { | ||
151 | p += scnprintf(p, left, "\tbase_address: %x\n", | ||
152 | info->params.interface_path.isa.base_address); | ||
153 | } else if (!strncmp(info->params.host_bus_type, "PCIX", 4) || | ||
154 | !strncmp(info->params.host_bus_type, "PCI", 3)) { | ||
155 | p += scnprintf(p, left, | ||
156 | "\t%02x:%02x.%d channel: %u\n", | ||
157 | info->params.interface_path.pci.bus, | ||
158 | info->params.interface_path.pci.slot, | ||
159 | info->params.interface_path.pci.function, | ||
160 | info->params.interface_path.pci.channel); | ||
161 | } else if (!strncmp(info->params.host_bus_type, "IBND", 4) || | ||
162 | !strncmp(info->params.host_bus_type, "XPRS", 4) || | ||
163 | !strncmp(info->params.host_bus_type, "HTPT", 4)) { | ||
164 | p += scnprintf(p, left, | ||
165 | "\tTBD: %llx\n", | ||
166 | info->params.interface_path.ibnd.reserved); | ||
167 | |||
168 | } else { | ||
169 | p += scnprintf(p, left, "\tunknown: %llx\n", | ||
170 | info->params.interface_path.unknown.reserved); | ||
171 | } | ||
172 | return (p - buf); | ||
173 | } | ||
174 | |||
175 | static ssize_t | ||
176 | edd_show_interface(struct edd_device *edev, char *buf) | ||
177 | { | ||
178 | struct edd_info *info; | ||
179 | char *p = buf; | ||
180 | int i; | ||
181 | |||
182 | if (!edev) | ||
183 | return -EINVAL; | ||
184 | info = edd_dev_get_info(edev); | ||
185 | if (!info || !buf) | ||
186 | return -EINVAL; | ||
187 | |||
188 | for (i = 0; i < 8; i++) { | ||
189 | if (isprint(info->params.interface_type[i])) { | ||
190 | p += scnprintf(p, left, "%c", info->params.interface_type[i]); | ||
191 | } else { | ||
192 | p += scnprintf(p, left, " "); | ||
193 | } | ||
194 | } | ||
195 | if (!strncmp(info->params.interface_type, "ATAPI", 5)) { | ||
196 | p += scnprintf(p, left, "\tdevice: %u lun: %u\n", | ||
197 | info->params.device_path.atapi.device, | ||
198 | info->params.device_path.atapi.lun); | ||
199 | } else if (!strncmp(info->params.interface_type, "ATA", 3)) { | ||
200 | p += scnprintf(p, left, "\tdevice: %u\n", | ||
201 | info->params.device_path.ata.device); | ||
202 | } else if (!strncmp(info->params.interface_type, "SCSI", 4)) { | ||
203 | p += scnprintf(p, left, "\tid: %u lun: %llu\n", | ||
204 | info->params.device_path.scsi.id, | ||
205 | info->params.device_path.scsi.lun); | ||
206 | } else if (!strncmp(info->params.interface_type, "USB", 3)) { | ||
207 | p += scnprintf(p, left, "\tserial_number: %llx\n", | ||
208 | info->params.device_path.usb.serial_number); | ||
209 | } else if (!strncmp(info->params.interface_type, "1394", 4)) { | ||
210 | p += scnprintf(p, left, "\teui: %llx\n", | ||
211 | info->params.device_path.i1394.eui); | ||
212 | } else if (!strncmp(info->params.interface_type, "FIBRE", 5)) { | ||
213 | p += scnprintf(p, left, "\twwid: %llx lun: %llx\n", | ||
214 | info->params.device_path.fibre.wwid, | ||
215 | info->params.device_path.fibre.lun); | ||
216 | } else if (!strncmp(info->params.interface_type, "I2O", 3)) { | ||
217 | p += scnprintf(p, left, "\tidentity_tag: %llx\n", | ||
218 | info->params.device_path.i2o.identity_tag); | ||
219 | } else if (!strncmp(info->params.interface_type, "RAID", 4)) { | ||
220 | p += scnprintf(p, left, "\tidentity_tag: %x\n", | ||
221 | info->params.device_path.raid.array_number); | ||
222 | } else if (!strncmp(info->params.interface_type, "SATA", 4)) { | ||
223 | p += scnprintf(p, left, "\tdevice: %u\n", | ||
224 | info->params.device_path.sata.device); | ||
225 | } else { | ||
226 | p += scnprintf(p, left, "\tunknown: %llx %llx\n", | ||
227 | info->params.device_path.unknown.reserved1, | ||
228 | info->params.device_path.unknown.reserved2); | ||
229 | } | ||
230 | |||
231 | return (p - buf); | ||
232 | } | ||
233 | |||
234 | /** | ||
235 | * edd_show_raw_data() - copies raw data to buffer for userspace to parse | ||
236 | * | ||
237 | * Returns: number of bytes written, or -EINVAL on failure | ||
238 | */ | ||
239 | static ssize_t | ||
240 | edd_show_raw_data(struct edd_device *edev, char *buf) | ||
241 | { | ||
242 | struct edd_info *info; | ||
243 | ssize_t len = sizeof (info->params); | ||
244 | if (!edev) | ||
245 | return -EINVAL; | ||
246 | info = edd_dev_get_info(edev); | ||
247 | if (!info || !buf) | ||
248 | return -EINVAL; | ||
249 | |||
250 | if (!(info->params.key == 0xBEDD || info->params.key == 0xDDBE)) | ||
251 | len = info->params.length; | ||
252 | |||
253 | /* In case of buggy BIOSs */ | ||
254 | if (len > (sizeof(info->params))) | ||
255 | len = sizeof(info->params); | ||
256 | |||
257 | memcpy(buf, &info->params, len); | ||
258 | return len; | ||
259 | } | ||
260 | |||
261 | static ssize_t | ||
262 | edd_show_version(struct edd_device *edev, char *buf) | ||
263 | { | ||
264 | struct edd_info *info; | ||
265 | char *p = buf; | ||
266 | if (!edev) | ||
267 | return -EINVAL; | ||
268 | info = edd_dev_get_info(edev); | ||
269 | if (!info || !buf) | ||
270 | return -EINVAL; | ||
271 | |||
272 | p += scnprintf(p, left, "0x%02x\n", info->version); | ||
273 | return (p - buf); | ||
274 | } | ||
275 | |||
276 | static ssize_t | ||
277 | edd_show_mbr_signature(struct edd_device *edev, char *buf) | ||
278 | { | ||
279 | char *p = buf; | ||
280 | p += scnprintf(p, left, "0x%08x\n", edev->mbr_signature); | ||
281 | return (p - buf); | ||
282 | } | ||
283 | |||
284 | static ssize_t | ||
285 | edd_show_extensions(struct edd_device *edev, char *buf) | ||
286 | { | ||
287 | struct edd_info *info; | ||
288 | char *p = buf; | ||
289 | if (!edev) | ||
290 | return -EINVAL; | ||
291 | info = edd_dev_get_info(edev); | ||
292 | if (!info || !buf) | ||
293 | return -EINVAL; | ||
294 | |||
295 | if (info->interface_support & EDD_EXT_FIXED_DISK_ACCESS) { | ||
296 | p += scnprintf(p, left, "Fixed disk access\n"); | ||
297 | } | ||
298 | if (info->interface_support & EDD_EXT_DEVICE_LOCKING_AND_EJECTING) { | ||
299 | p += scnprintf(p, left, "Device locking and ejecting\n"); | ||
300 | } | ||
301 | if (info->interface_support & EDD_EXT_ENHANCED_DISK_DRIVE_SUPPORT) { | ||
302 | p += scnprintf(p, left, "Enhanced Disk Drive support\n"); | ||
303 | } | ||
304 | if (info->interface_support & EDD_EXT_64BIT_EXTENSIONS) { | ||
305 | p += scnprintf(p, left, "64-bit extensions\n"); | ||
306 | } | ||
307 | return (p - buf); | ||
308 | } | ||
309 | |||
310 | static ssize_t | ||
311 | edd_show_info_flags(struct edd_device *edev, char *buf) | ||
312 | { | ||
313 | struct edd_info *info; | ||
314 | char *p = buf; | ||
315 | if (!edev) | ||
316 | return -EINVAL; | ||
317 | info = edd_dev_get_info(edev); | ||
318 | if (!info || !buf) | ||
319 | return -EINVAL; | ||
320 | |||
321 | if (info->params.info_flags & EDD_INFO_DMA_BOUNDARY_ERROR_TRANSPARENT) | ||
322 | p += scnprintf(p, left, "DMA boundary error transparent\n"); | ||
323 | if (info->params.info_flags & EDD_INFO_GEOMETRY_VALID) | ||
324 | p += scnprintf(p, left, "geometry valid\n"); | ||
325 | if (info->params.info_flags & EDD_INFO_REMOVABLE) | ||
326 | p += scnprintf(p, left, "removable\n"); | ||
327 | if (info->params.info_flags & EDD_INFO_WRITE_VERIFY) | ||
328 | p += scnprintf(p, left, "write verify\n"); | ||
329 | if (info->params.info_flags & EDD_INFO_MEDIA_CHANGE_NOTIFICATION) | ||
330 | p += scnprintf(p, left, "media change notification\n"); | ||
331 | if (info->params.info_flags & EDD_INFO_LOCKABLE) | ||
332 | p += scnprintf(p, left, "lockable\n"); | ||
333 | if (info->params.info_flags & EDD_INFO_NO_MEDIA_PRESENT) | ||
334 | p += scnprintf(p, left, "no media present\n"); | ||
335 | if (info->params.info_flags & EDD_INFO_USE_INT13_FN50) | ||
336 | p += scnprintf(p, left, "use int13 fn50\n"); | ||
337 | return (p - buf); | ||
338 | } | ||
339 | |||
340 | static ssize_t | ||
341 | edd_show_legacy_max_cylinder(struct edd_device *edev, char *buf) | ||
342 | { | ||
343 | struct edd_info *info; | ||
344 | char *p = buf; | ||
345 | if (!edev) | ||
346 | return -EINVAL; | ||
347 | info = edd_dev_get_info(edev); | ||
348 | if (!info || !buf) | ||
349 | return -EINVAL; | ||
350 | |||
351 | p += snprintf(p, left, "%u\n", info->legacy_max_cylinder); | ||
352 | return (p - buf); | ||
353 | } | ||
354 | |||
355 | static ssize_t | ||
356 | edd_show_legacy_max_head(struct edd_device *edev, char *buf) | ||
357 | { | ||
358 | struct edd_info *info; | ||
359 | char *p = buf; | ||
360 | if (!edev) | ||
361 | return -EINVAL; | ||
362 | info = edd_dev_get_info(edev); | ||
363 | if (!info || !buf) | ||
364 | return -EINVAL; | ||
365 | |||
366 | p += snprintf(p, left, "%u\n", info->legacy_max_head); | ||
367 | return (p - buf); | ||
368 | } | ||
369 | |||
370 | static ssize_t | ||
371 | edd_show_legacy_sectors_per_track(struct edd_device *edev, char *buf) | ||
372 | { | ||
373 | struct edd_info *info; | ||
374 | char *p = buf; | ||
375 | if (!edev) | ||
376 | return -EINVAL; | ||
377 | info = edd_dev_get_info(edev); | ||
378 | if (!info || !buf) | ||
379 | return -EINVAL; | ||
380 | |||
381 | p += snprintf(p, left, "%u\n", info->legacy_sectors_per_track); | ||
382 | return (p - buf); | ||
383 | } | ||
384 | |||
385 | static ssize_t | ||
386 | edd_show_default_cylinders(struct edd_device *edev, char *buf) | ||
387 | { | ||
388 | struct edd_info *info; | ||
389 | char *p = buf; | ||
390 | if (!edev) | ||
391 | return -EINVAL; | ||
392 | info = edd_dev_get_info(edev); | ||
393 | if (!info || !buf) | ||
394 | return -EINVAL; | ||
395 | |||
396 | p += scnprintf(p, left, "%u\n", info->params.num_default_cylinders); | ||
397 | return (p - buf); | ||
398 | } | ||
399 | |||
400 | static ssize_t | ||
401 | edd_show_default_heads(struct edd_device *edev, char *buf) | ||
402 | { | ||
403 | struct edd_info *info; | ||
404 | char *p = buf; | ||
405 | if (!edev) | ||
406 | return -EINVAL; | ||
407 | info = edd_dev_get_info(edev); | ||
408 | if (!info || !buf) | ||
409 | return -EINVAL; | ||
410 | |||
411 | p += scnprintf(p, left, "%u\n", info->params.num_default_heads); | ||
412 | return (p - buf); | ||
413 | } | ||
414 | |||
415 | static ssize_t | ||
416 | edd_show_default_sectors_per_track(struct edd_device *edev, char *buf) | ||
417 | { | ||
418 | struct edd_info *info; | ||
419 | char *p = buf; | ||
420 | if (!edev) | ||
421 | return -EINVAL; | ||
422 | info = edd_dev_get_info(edev); | ||
423 | if (!info || !buf) | ||
424 | return -EINVAL; | ||
425 | |||
426 | p += scnprintf(p, left, "%u\n", info->params.sectors_per_track); | ||
427 | return (p - buf); | ||
428 | } | ||
429 | |||
430 | static ssize_t | ||
431 | edd_show_sectors(struct edd_device *edev, char *buf) | ||
432 | { | ||
433 | struct edd_info *info; | ||
434 | char *p = buf; | ||
435 | if (!edev) | ||
436 | return -EINVAL; | ||
437 | info = edd_dev_get_info(edev); | ||
438 | if (!info || !buf) | ||
439 | return -EINVAL; | ||
440 | |||
441 | p += scnprintf(p, left, "%llu\n", info->params.number_of_sectors); | ||
442 | return (p - buf); | ||
443 | } | ||
444 | |||
445 | |||
446 | /* | ||
447 | * Some device instances may not have all the above attributes, | ||
448 | * or the attribute values may be meaningless (i.e. if | ||
449 | * the device is < EDD 3.0, it won't have host_bus and interface | ||
450 | * information), so don't bother making files for them. Likewise | ||
451 | * if the default_{cylinders,heads,sectors_per_track} values | ||
452 | * are zero, the BIOS doesn't provide sane values, don't bother | ||
453 | * creating files for them either. | ||
454 | */ | ||
455 | |||
456 | static int | ||
457 | edd_has_legacy_max_cylinder(struct edd_device *edev) | ||
458 | { | ||
459 | struct edd_info *info; | ||
460 | if (!edev) | ||
461 | return 0; | ||
462 | info = edd_dev_get_info(edev); | ||
463 | if (!info) | ||
464 | return 0; | ||
465 | return info->legacy_max_cylinder > 0; | ||
466 | } | ||
467 | |||
468 | static int | ||
469 | edd_has_legacy_max_head(struct edd_device *edev) | ||
470 | { | ||
471 | struct edd_info *info; | ||
472 | if (!edev) | ||
473 | return 0; | ||
474 | info = edd_dev_get_info(edev); | ||
475 | if (!info) | ||
476 | return 0; | ||
477 | return info->legacy_max_head > 0; | ||
478 | } | ||
479 | |||
480 | static int | ||
481 | edd_has_legacy_sectors_per_track(struct edd_device *edev) | ||
482 | { | ||
483 | struct edd_info *info; | ||
484 | if (!edev) | ||
485 | return 0; | ||
486 | info = edd_dev_get_info(edev); | ||
487 | if (!info) | ||
488 | return 0; | ||
489 | return info->legacy_sectors_per_track > 0; | ||
490 | } | ||
491 | |||
492 | static int | ||
493 | edd_has_default_cylinders(struct edd_device *edev) | ||
494 | { | ||
495 | struct edd_info *info; | ||
496 | if (!edev) | ||
497 | return 0; | ||
498 | info = edd_dev_get_info(edev); | ||
499 | if (!info) | ||
500 | return 0; | ||
501 | return info->params.num_default_cylinders > 0; | ||
502 | } | ||
503 | |||
504 | static int | ||
505 | edd_has_default_heads(struct edd_device *edev) | ||
506 | { | ||
507 | struct edd_info *info; | ||
508 | if (!edev) | ||
509 | return 0; | ||
510 | info = edd_dev_get_info(edev); | ||
511 | if (!info) | ||
512 | return 0; | ||
513 | return info->params.num_default_heads > 0; | ||
514 | } | ||
515 | |||
516 | static int | ||
517 | edd_has_default_sectors_per_track(struct edd_device *edev) | ||
518 | { | ||
519 | struct edd_info *info; | ||
520 | if (!edev) | ||
521 | return 0; | ||
522 | info = edd_dev_get_info(edev); | ||
523 | if (!info) | ||
524 | return 0; | ||
525 | return info->params.sectors_per_track > 0; | ||
526 | } | ||
527 | |||
528 | static int | ||
529 | edd_has_edd30(struct edd_device *edev) | ||
530 | { | ||
531 | struct edd_info *info; | ||
532 | int i, nonzero_path = 0; | ||
533 | char c; | ||
534 | |||
535 | if (!edev) | ||
536 | return 0; | ||
537 | info = edd_dev_get_info(edev); | ||
538 | if (!info) | ||
539 | return 0; | ||
540 | |||
541 | if (!(info->params.key == 0xBEDD || info->params.key == 0xDDBE)) { | ||
542 | return 0; | ||
543 | } | ||
544 | |||
545 | for (i = 30; i <= 73; i++) { | ||
546 | c = *(((uint8_t *) info) + i + 4); | ||
547 | if (c) { | ||
548 | nonzero_path++; | ||
549 | break; | ||
550 | } | ||
551 | } | ||
552 | if (!nonzero_path) { | ||
553 | return 0; | ||
554 | } | ||
555 | |||
556 | return 1; | ||
557 | } | ||
558 | |||
559 | |||
560 | static EDD_DEVICE_ATTR(raw_data, 0444, edd_show_raw_data, edd_has_edd_info); | ||
561 | static EDD_DEVICE_ATTR(version, 0444, edd_show_version, edd_has_edd_info); | ||
562 | static EDD_DEVICE_ATTR(extensions, 0444, edd_show_extensions, edd_has_edd_info); | ||
563 | static EDD_DEVICE_ATTR(info_flags, 0444, edd_show_info_flags, edd_has_edd_info); | ||
564 | static EDD_DEVICE_ATTR(sectors, 0444, edd_show_sectors, edd_has_edd_info); | ||
565 | static EDD_DEVICE_ATTR(legacy_max_cylinder, 0444, | ||
566 | edd_show_legacy_max_cylinder, | ||
567 | edd_has_legacy_max_cylinder); | ||
568 | static EDD_DEVICE_ATTR(legacy_max_head, 0444, edd_show_legacy_max_head, | ||
569 | edd_has_legacy_max_head); | ||
570 | static EDD_DEVICE_ATTR(legacy_sectors_per_track, 0444, | ||
571 | edd_show_legacy_sectors_per_track, | ||
572 | edd_has_legacy_sectors_per_track); | ||
573 | static EDD_DEVICE_ATTR(default_cylinders, 0444, edd_show_default_cylinders, | ||
574 | edd_has_default_cylinders); | ||
575 | static EDD_DEVICE_ATTR(default_heads, 0444, edd_show_default_heads, | ||
576 | edd_has_default_heads); | ||
577 | static EDD_DEVICE_ATTR(default_sectors_per_track, 0444, | ||
578 | edd_show_default_sectors_per_track, | ||
579 | edd_has_default_sectors_per_track); | ||
580 | static EDD_DEVICE_ATTR(interface, 0444, edd_show_interface, edd_has_edd30); | ||
581 | static EDD_DEVICE_ATTR(host_bus, 0444, edd_show_host_bus, edd_has_edd30); | ||
582 | static EDD_DEVICE_ATTR(mbr_signature, 0444, edd_show_mbr_signature, edd_has_mbr_signature); | ||
583 | |||
584 | |||
585 | /* These are default attributes that are added for every edd | ||
586 | * device discovered. There are none. | ||
587 | */ | ||
588 | static struct attribute * def_attrs[] = { | ||
589 | NULL, | ||
590 | }; | ||
591 | |||
592 | /* These attributes are conditional and only added for some devices. */ | ||
593 | static struct edd_attribute * edd_attrs[] = { | ||
594 | &edd_attr_raw_data, | ||
595 | &edd_attr_version, | ||
596 | &edd_attr_extensions, | ||
597 | &edd_attr_info_flags, | ||
598 | &edd_attr_sectors, | ||
599 | &edd_attr_legacy_max_cylinder, | ||
600 | &edd_attr_legacy_max_head, | ||
601 | &edd_attr_legacy_sectors_per_track, | ||
602 | &edd_attr_default_cylinders, | ||
603 | &edd_attr_default_heads, | ||
604 | &edd_attr_default_sectors_per_track, | ||
605 | &edd_attr_interface, | ||
606 | &edd_attr_host_bus, | ||
607 | &edd_attr_mbr_signature, | ||
608 | NULL, | ||
609 | }; | ||
610 | |||
611 | /** | ||
612 | * edd_release - free edd structure | ||
613 | * @kobj: kobject of edd structure | ||
614 | * | ||
615 | * This is called when the refcount of the edd structure | ||
616 | * reaches 0. This should happen right after we unregister, | ||
617 | * but just in case, we use the release callback anyway. | ||
618 | */ | ||
619 | |||
620 | static void edd_release(struct kobject * kobj) | ||
621 | { | ||
622 | struct edd_device * dev = to_edd_device(kobj); | ||
623 | kfree(dev); | ||
624 | } | ||
625 | |||
626 | static struct kobj_type ktype_edd = { | ||
627 | .release = edd_release, | ||
628 | .sysfs_ops = &edd_attr_ops, | ||
629 | .default_attrs = def_attrs, | ||
630 | }; | ||
631 | |||
632 | static decl_subsys(edd,&ktype_edd,NULL); | ||
633 | |||
634 | |||
635 | /** | ||
636 | * edd_dev_is_type() - is this EDD device a 'type' device? | ||
637 | * @edev | ||
638 | * @type - a host bus or interface identifier string per the EDD spec | ||
639 | * | ||
640 | * Returns 1 (TRUE) if it is a 'type' device, 0 otherwise. | ||
641 | */ | ||
642 | static int | ||
643 | edd_dev_is_type(struct edd_device *edev, const char *type) | ||
644 | { | ||
645 | struct edd_info *info; | ||
646 | if (!edev) | ||
647 | return 0; | ||
648 | info = edd_dev_get_info(edev); | ||
649 | |||
650 | if (type && info) { | ||
651 | if (!strncmp(info->params.host_bus_type, type, strlen(type)) || | ||
652 | !strncmp(info->params.interface_type, type, strlen(type))) | ||
653 | return 1; | ||
654 | } | ||
655 | return 0; | ||
656 | } | ||
657 | |||
658 | /** | ||
659 | * edd_get_pci_dev() - finds pci_dev that matches edev | ||
660 | * @edev - edd_device | ||
661 | * | ||
662 | * Returns pci_dev if found, or NULL | ||
663 | */ | ||
664 | static struct pci_dev * | ||
665 | edd_get_pci_dev(struct edd_device *edev) | ||
666 | { | ||
667 | struct edd_info *info = edd_dev_get_info(edev); | ||
668 | |||
669 | if (edd_dev_is_type(edev, "PCI")) { | ||
670 | return pci_find_slot(info->params.interface_path.pci.bus, | ||
671 | PCI_DEVFN(info->params.interface_path.pci.slot, | ||
672 | info->params.interface_path.pci. | ||
673 | function)); | ||
674 | } | ||
675 | return NULL; | ||
676 | } | ||
677 | |||
678 | static int | ||
679 | edd_create_symlink_to_pcidev(struct edd_device *edev) | ||
680 | { | ||
681 | |||
682 | struct pci_dev *pci_dev = edd_get_pci_dev(edev); | ||
683 | if (!pci_dev) | ||
684 | return 1; | ||
685 | return sysfs_create_link(&edev->kobj,&pci_dev->dev.kobj,"pci_dev"); | ||
686 | } | ||
687 | |||
688 | static inline void | ||
689 | edd_device_unregister(struct edd_device *edev) | ||
690 | { | ||
691 | kobject_unregister(&edev->kobj); | ||
692 | } | ||
693 | |||
694 | static void edd_populate_dir(struct edd_device * edev) | ||
695 | { | ||
696 | struct edd_attribute * attr; | ||
697 | int error = 0; | ||
698 | int i; | ||
699 | |||
700 | for (i = 0; (attr = edd_attrs[i]) && !error; i++) { | ||
701 | if (!attr->test || | ||
702 | (attr->test && attr->test(edev))) | ||
703 | error = sysfs_create_file(&edev->kobj,&attr->attr); | ||
704 | } | ||
705 | |||
706 | if (!error) { | ||
707 | edd_create_symlink_to_pcidev(edev); | ||
708 | } | ||
709 | } | ||
710 | |||
711 | static int | ||
712 | edd_device_register(struct edd_device *edev, int i) | ||
713 | { | ||
714 | int error; | ||
715 | |||
716 | if (!edev) | ||
717 | return 1; | ||
718 | memset(edev, 0, sizeof (*edev)); | ||
719 | edd_dev_set_info(edev, i); | ||
720 | kobject_set_name(&edev->kobj, "int13_dev%02x", | ||
721 | 0x80 + i); | ||
722 | kobj_set_kset_s(edev,edd_subsys); | ||
723 | error = kobject_register(&edev->kobj); | ||
724 | if (!error) | ||
725 | edd_populate_dir(edev); | ||
726 | return error; | ||
727 | } | ||
728 | |||
729 | static inline int edd_num_devices(void) | ||
730 | { | ||
731 | return max_t(unsigned char, | ||
732 | min_t(unsigned char, EDD_MBR_SIG_MAX, edd.mbr_signature_nr), | ||
733 | min_t(unsigned char, EDDMAXNR, edd.edd_info_nr)); | ||
734 | } | ||
735 | |||
736 | /** | ||
737 | * edd_init() - creates sysfs tree of EDD data | ||
738 | */ | ||
739 | static int __init | ||
740 | edd_init(void) | ||
741 | { | ||
742 | unsigned int i; | ||
743 | int rc=0; | ||
744 | struct edd_device *edev; | ||
745 | |||
746 | printk(KERN_INFO "BIOS EDD facility v%s %s, %d devices found\n", | ||
747 | EDD_VERSION, EDD_DATE, edd_num_devices()); | ||
748 | |||
749 | if (!edd_num_devices()) { | ||
750 | printk(KERN_INFO "EDD information not available.\n"); | ||
751 | return 1; | ||
752 | } | ||
753 | |||
754 | rc = firmware_register(&edd_subsys); | ||
755 | if (rc) | ||
756 | return rc; | ||
757 | |||
758 | for (i = 0; i < edd_num_devices() && !rc; i++) { | ||
759 | edev = kmalloc(sizeof (*edev), GFP_KERNEL); | ||
760 | if (!edev) | ||
761 | return -ENOMEM; | ||
762 | |||
763 | rc = edd_device_register(edev, i); | ||
764 | if (rc) { | ||
765 | kfree(edev); | ||
766 | break; | ||
767 | } | ||
768 | edd_devices[i] = edev; | ||
769 | } | ||
770 | |||
771 | if (rc) | ||
772 | firmware_unregister(&edd_subsys); | ||
773 | return rc; | ||
774 | } | ||
775 | |||
776 | static void __exit | ||
777 | edd_exit(void) | ||
778 | { | ||
779 | int i; | ||
780 | struct edd_device *edev; | ||
781 | |||
782 | for (i = 0; i < edd_num_devices(); i++) { | ||
783 | if ((edev = edd_devices[i])) | ||
784 | edd_device_unregister(edev); | ||
785 | } | ||
786 | firmware_unregister(&edd_subsys); | ||
787 | } | ||
788 | |||
789 | late_initcall(edd_init); | ||
790 | module_exit(edd_exit); | ||
diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c new file mode 100644 index 000000000000..0287ff65963b --- /dev/null +++ b/drivers/firmware/efivars.c | |||
@@ -0,0 +1,781 @@ | |||
1 | /* | ||
2 | * EFI Variables - efivars.c | ||
3 | * | ||
4 | * Copyright (C) 2001,2003,2004 Dell <Matt_Domsch@dell.com> | ||
5 | * Copyright (C) 2004 Intel Corporation <matthew.e.tolentino@intel.com> | ||
6 | * | ||
7 | * This code takes all variables accessible from EFI runtime and | ||
8 | * exports them via sysfs | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License as published by | ||
12 | * the Free Software Foundation; either version 2 of the License, or | ||
13 | * (at your option) any later version. | ||
14 | * | ||
15 | * This program is distributed in the hope that it will be useful, | ||
16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
18 | * GNU General Public License for more details. | ||
19 | * | ||
20 | * You should have received a copy of the GNU General Public License | ||
21 | * along with this program; if not, write to the Free Software | ||
22 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
23 | * | ||
24 | * Changelog: | ||
25 | * | ||
26 | * 17 May 2004 - Matt Domsch <Matt_Domsch@dell.com> | ||
27 | * remove check for efi_enabled in exit | ||
28 | * add MODULE_VERSION | ||
29 | * | ||
30 | * 26 Apr 2004 - Matt Domsch <Matt_Domsch@dell.com> | ||
31 | * minor bug fixes | ||
32 | * | ||
33 | * 21 Apr 2004 - Matt Tolentino <matthew.e.tolentino@intel.com) | ||
34 | * converted driver to export variable information via sysfs | ||
35 | * and moved to drivers/firmware directory | ||
36 | * bumped revision number to v0.07 to reflect conversion & move | ||
37 | * | ||
38 | * 10 Dec 2002 - Matt Domsch <Matt_Domsch@dell.com> | ||
39 | * fix locking per Peter Chubb's findings | ||
40 | * | ||
41 | * 25 Mar 2002 - Matt Domsch <Matt_Domsch@dell.com> | ||
42 | * move uuid_unparse() to include/asm-ia64/efi.h:efi_guid_unparse() | ||
43 | * | ||
44 | * 12 Feb 2002 - Matt Domsch <Matt_Domsch@dell.com> | ||
45 | * use list_for_each_safe when deleting vars. | ||
46 | * remove ifdef CONFIG_SMP around include <linux/smp.h> | ||
47 | * v0.04 release to linux-ia64@linuxia64.org | ||
48 | * | ||
49 | * 20 April 2001 - Matt Domsch <Matt_Domsch@dell.com> | ||
50 | * Moved vars from /proc/efi to /proc/efi/vars, and made | ||
51 | * efi.c own the /proc/efi directory. | ||
52 | * v0.03 release to linux-ia64@linuxia64.org | ||
53 | * | ||
54 | * 26 March 2001 - Matt Domsch <Matt_Domsch@dell.com> | ||
55 | * At the request of Stephane, moved ownership of /proc/efi | ||
56 | * to efi.c, and now efivars lives under /proc/efi/vars. | ||
57 | * | ||
58 | * 12 March 2001 - Matt Domsch <Matt_Domsch@dell.com> | ||
59 | * Feedback received from Stephane Eranian incorporated. | ||
60 | * efivar_write() checks copy_from_user() return value. | ||
61 | * efivar_read/write() returns proper errno. | ||
62 | * v0.02 release to linux-ia64@linuxia64.org | ||
63 | * | ||
64 | * 26 February 2001 - Matt Domsch <Matt_Domsch@dell.com> | ||
65 | * v0.01 release to linux-ia64@linuxia64.org | ||
66 | */ | ||
67 | |||
68 | #include <linux/config.h> | ||
69 | #include <linux/types.h> | ||
70 | #include <linux/errno.h> | ||
71 | #include <linux/init.h> | ||
72 | #include <linux/sched.h> /* for capable() */ | ||
73 | #include <linux/mm.h> | ||
74 | #include <linux/module.h> | ||
75 | #include <linux/string.h> | ||
76 | #include <linux/smp.h> | ||
77 | #include <linux/efi.h> | ||
78 | #include <linux/sysfs.h> | ||
79 | #include <linux/kobject.h> | ||
80 | #include <linux/device.h> | ||
81 | |||
82 | #include <asm/uaccess.h> | ||
83 | |||
84 | #define EFIVARS_VERSION "0.08" | ||
85 | #define EFIVARS_DATE "2004-May-17" | ||
86 | |||
87 | MODULE_AUTHOR("Matt Domsch <Matt_Domsch@Dell.com>"); | ||
88 | MODULE_DESCRIPTION("sysfs interface to EFI Variables"); | ||
89 | MODULE_LICENSE("GPL"); | ||
90 | MODULE_VERSION(EFIVARS_VERSION); | ||
91 | |||
92 | /* | ||
93 | * efivars_lock protects two things: | ||
94 | * 1) efivar_list - adds, removals, reads, writes | ||
95 | * 2) efi.[gs]et_variable() calls. | ||
96 | * It must not be held when creating sysfs entries or calling kmalloc. | ||
97 | * efi.get_next_variable() is only called from efivars_init(), | ||
98 | * which is protected by the BKL, so that path is safe. | ||
99 | */ | ||
100 | static DEFINE_SPINLOCK(efivars_lock); | ||
101 | static LIST_HEAD(efivar_list); | ||
102 | |||
103 | /* | ||
104 | * The maximum size of VariableName + Data = 1024 | ||
105 | * Therefore, it's reasonable to save that much | ||
106 | * space in each part of the structure, | ||
107 | * and we use a page for reading/writing. | ||
108 | */ | ||
109 | |||
110 | struct efi_variable { | ||
111 | efi_char16_t VariableName[1024/sizeof(efi_char16_t)]; | ||
112 | efi_guid_t VendorGuid; | ||
113 | unsigned long DataSize; | ||
114 | __u8 Data[1024]; | ||
115 | efi_status_t Status; | ||
116 | __u32 Attributes; | ||
117 | } __attribute__((packed)); | ||
118 | |||
119 | |||
120 | struct efivar_entry { | ||
121 | struct efi_variable var; | ||
122 | struct list_head list; | ||
123 | struct kobject kobj; | ||
124 | }; | ||
125 | |||
126 | #define get_efivar_entry(n) list_entry(n, struct efivar_entry, list) | ||
127 | |||
128 | struct efivar_attribute { | ||
129 | struct attribute attr; | ||
130 | ssize_t (*show) (struct efivar_entry *entry, char *buf); | ||
131 | ssize_t (*store)(struct efivar_entry *entry, const char *buf, size_t count); | ||
132 | }; | ||
133 | |||
134 | |||
135 | #define EFI_ATTR(_name, _mode, _show, _store) \ | ||
136 | struct subsys_attribute efi_attr_##_name = { \ | ||
137 | .attr = {.name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE}, \ | ||
138 | .show = _show, \ | ||
139 | .store = _store, \ | ||
140 | }; | ||
141 | |||
142 | #define EFIVAR_ATTR(_name, _mode, _show, _store) \ | ||
143 | struct efivar_attribute efivar_attr_##_name = { \ | ||
144 | .attr = {.name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE}, \ | ||
145 | .show = _show, \ | ||
146 | .store = _store, \ | ||
147 | }; | ||
148 | |||
149 | #define VAR_SUBSYS_ATTR(_name, _mode, _show, _store) \ | ||
150 | struct subsys_attribute var_subsys_attr_##_name = { \ | ||
151 | .attr = {.name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE}, \ | ||
152 | .show = _show, \ | ||
153 | .store = _store, \ | ||
154 | }; | ||
155 | |||
156 | #define to_efivar_attr(_attr) container_of(_attr, struct efivar_attribute, attr) | ||
157 | #define to_efivar_entry(obj) container_of(obj, struct efivar_entry, kobj) | ||
158 | |||
159 | /* | ||
160 | * Prototype for sysfs creation function | ||
161 | */ | ||
162 | static int | ||
163 | efivar_create_sysfs_entry(unsigned long variable_name_size, | ||
164 | efi_char16_t *variable_name, | ||
165 | efi_guid_t *vendor_guid); | ||
166 | |||
167 | /* Return the number of unicode characters in data */ | ||
168 | static unsigned long | ||
169 | utf8_strlen(efi_char16_t *data, unsigned long maxlength) | ||
170 | { | ||
171 | unsigned long length = 0; | ||
172 | |||
173 | while (*data++ != 0 && length < maxlength) | ||
174 | length++; | ||
175 | return length; | ||
176 | } | ||
177 | |||
178 | /* | ||
179 | * Return the number of bytes is the length of this string | ||
180 | * Note: this is NOT the same as the number of unicode characters | ||
181 | */ | ||
182 | static inline unsigned long | ||
183 | utf8_strsize(efi_char16_t *data, unsigned long maxlength) | ||
184 | { | ||
185 | return utf8_strlen(data, maxlength/sizeof(efi_char16_t)) * sizeof(efi_char16_t); | ||
186 | } | ||
187 | |||
188 | static efi_status_t | ||
189 | get_var_data(struct efi_variable *var) | ||
190 | { | ||
191 | efi_status_t status; | ||
192 | |||
193 | spin_lock(&efivars_lock); | ||
194 | var->DataSize = 1024; | ||
195 | status = efi.get_variable(var->VariableName, | ||
196 | &var->VendorGuid, | ||
197 | &var->Attributes, | ||
198 | &var->DataSize, | ||
199 | var->Data); | ||
200 | spin_unlock(&efivars_lock); | ||
201 | if (status != EFI_SUCCESS) { | ||
202 | printk(KERN_WARNING "efivars: get_variable() failed 0x%lx!\n", | ||
203 | status); | ||
204 | } | ||
205 | return status; | ||
206 | } | ||
207 | |||
208 | static ssize_t | ||
209 | efivar_guid_read(struct efivar_entry *entry, char *buf) | ||
210 | { | ||
211 | struct efi_variable *var = &entry->var; | ||
212 | char *str = buf; | ||
213 | |||
214 | if (!entry || !buf) | ||
215 | return 0; | ||
216 | |||
217 | efi_guid_unparse(&var->VendorGuid, str); | ||
218 | str += strlen(str); | ||
219 | str += sprintf(str, "\n"); | ||
220 | |||
221 | return str - buf; | ||
222 | } | ||
223 | |||
224 | static ssize_t | ||
225 | efivar_attr_read(struct efivar_entry *entry, char *buf) | ||
226 | { | ||
227 | struct efi_variable *var = &entry->var; | ||
228 | char *str = buf; | ||
229 | efi_status_t status; | ||
230 | |||
231 | if (!entry || !buf) | ||
232 | return -EINVAL; | ||
233 | |||
234 | status = get_var_data(var); | ||
235 | if (status != EFI_SUCCESS) | ||
236 | return -EIO; | ||
237 | |||
238 | if (var->Attributes & 0x1) | ||
239 | str += sprintf(str, "EFI_VARIABLE_NON_VOLATILE\n"); | ||
240 | if (var->Attributes & 0x2) | ||
241 | str += sprintf(str, "EFI_VARIABLE_BOOTSERVICE_ACCESS\n"); | ||
242 | if (var->Attributes & 0x4) | ||
243 | str += sprintf(str, "EFI_VARIABLE_RUNTIME_ACCESS\n"); | ||
244 | return str - buf; | ||
245 | } | ||
246 | |||
247 | static ssize_t | ||
248 | efivar_size_read(struct efivar_entry *entry, char *buf) | ||
249 | { | ||
250 | struct efi_variable *var = &entry->var; | ||
251 | char *str = buf; | ||
252 | efi_status_t status; | ||
253 | |||
254 | if (!entry || !buf) | ||
255 | return -EINVAL; | ||
256 | |||
257 | status = get_var_data(var); | ||
258 | if (status != EFI_SUCCESS) | ||
259 | return -EIO; | ||
260 | |||
261 | str += sprintf(str, "0x%lx\n", var->DataSize); | ||
262 | return str - buf; | ||
263 | } | ||
264 | |||
265 | static ssize_t | ||
266 | efivar_data_read(struct efivar_entry *entry, char *buf) | ||
267 | { | ||
268 | struct efi_variable *var = &entry->var; | ||
269 | efi_status_t status; | ||
270 | |||
271 | if (!entry || !buf) | ||
272 | return -EINVAL; | ||
273 | |||
274 | status = get_var_data(var); | ||
275 | if (status != EFI_SUCCESS) | ||
276 | return -EIO; | ||
277 | |||
278 | memcpy(buf, var->Data, var->DataSize); | ||
279 | return var->DataSize; | ||
280 | } | ||
281 | /* | ||
282 | * We allow each variable to be edited via rewriting the | ||
283 | * entire efi variable structure. | ||
284 | */ | ||
285 | static ssize_t | ||
286 | efivar_store_raw(struct efivar_entry *entry, const char *buf, size_t count) | ||
287 | { | ||
288 | struct efi_variable *new_var, *var = &entry->var; | ||
289 | efi_status_t status = EFI_NOT_FOUND; | ||
290 | |||
291 | if (count != sizeof(struct efi_variable)) | ||
292 | return -EINVAL; | ||
293 | |||
294 | new_var = (struct efi_variable *)buf; | ||
295 | /* | ||
296 | * If only updating the variable data, then the name | ||
297 | * and guid should remain the same | ||
298 | */ | ||
299 | if (memcmp(new_var->VariableName, var->VariableName, sizeof(var->VariableName)) || | ||
300 | efi_guidcmp(new_var->VendorGuid, var->VendorGuid)) { | ||
301 | printk(KERN_ERR "efivars: Cannot edit the wrong variable!\n"); | ||
302 | return -EINVAL; | ||
303 | } | ||
304 | |||
305 | if ((new_var->DataSize <= 0) || (new_var->Attributes == 0)){ | ||
306 | printk(KERN_ERR "efivars: DataSize & Attributes must be valid!\n"); | ||
307 | return -EINVAL; | ||
308 | } | ||
309 | |||
310 | spin_lock(&efivars_lock); | ||
311 | status = efi.set_variable(new_var->VariableName, | ||
312 | &new_var->VendorGuid, | ||
313 | new_var->Attributes, | ||
314 | new_var->DataSize, | ||
315 | new_var->Data); | ||
316 | |||
317 | spin_unlock(&efivars_lock); | ||
318 | |||
319 | if (status != EFI_SUCCESS) { | ||
320 | printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n", | ||
321 | status); | ||
322 | return -EIO; | ||
323 | } | ||
324 | |||
325 | memcpy(&entry->var, new_var, count); | ||
326 | return count; | ||
327 | } | ||
328 | |||
329 | static ssize_t | ||
330 | efivar_show_raw(struct efivar_entry *entry, char *buf) | ||
331 | { | ||
332 | struct efi_variable *var = &entry->var; | ||
333 | efi_status_t status; | ||
334 | |||
335 | if (!entry || !buf) | ||
336 | return 0; | ||
337 | |||
338 | status = get_var_data(var); | ||
339 | if (status != EFI_SUCCESS) | ||
340 | return -EIO; | ||
341 | |||
342 | memcpy(buf, var, sizeof(*var)); | ||
343 | return sizeof(*var); | ||
344 | } | ||
345 | |||
346 | /* | ||
347 | * Generic read/write functions that call the specific functions of | ||
348 | * the atttributes... | ||
349 | */ | ||
350 | static ssize_t efivar_attr_show(struct kobject *kobj, struct attribute *attr, | ||
351 | char *buf) | ||
352 | { | ||
353 | struct efivar_entry *var = to_efivar_entry(kobj); | ||
354 | struct efivar_attribute *efivar_attr = to_efivar_attr(attr); | ||
355 | ssize_t ret = 0; | ||
356 | |||
357 | if (!capable(CAP_SYS_ADMIN)) | ||
358 | return -EACCES; | ||
359 | |||
360 | if (efivar_attr->show) { | ||
361 | ret = efivar_attr->show(var, buf); | ||
362 | } | ||
363 | return ret; | ||
364 | } | ||
365 | |||
366 | static ssize_t efivar_attr_store(struct kobject *kobj, struct attribute *attr, | ||
367 | const char *buf, size_t count) | ||
368 | { | ||
369 | struct efivar_entry *var = to_efivar_entry(kobj); | ||
370 | struct efivar_attribute *efivar_attr = to_efivar_attr(attr); | ||
371 | ssize_t ret = 0; | ||
372 | |||
373 | if (!capable(CAP_SYS_ADMIN)) | ||
374 | return -EACCES; | ||
375 | |||
376 | if (efivar_attr->store) | ||
377 | ret = efivar_attr->store(var, buf, count); | ||
378 | |||
379 | return ret; | ||
380 | } | ||
381 | |||
382 | static struct sysfs_ops efivar_attr_ops = { | ||
383 | .show = efivar_attr_show, | ||
384 | .store = efivar_attr_store, | ||
385 | }; | ||
386 | |||
387 | static void efivar_release(struct kobject *kobj) | ||
388 | { | ||
389 | struct efivar_entry *var = container_of(kobj, struct efivar_entry, kobj); | ||
390 | spin_lock(&efivars_lock); | ||
391 | list_del(&var->list); | ||
392 | spin_unlock(&efivars_lock); | ||
393 | kfree(var); | ||
394 | } | ||
395 | |||
396 | static EFIVAR_ATTR(guid, 0400, efivar_guid_read, NULL); | ||
397 | static EFIVAR_ATTR(attributes, 0400, efivar_attr_read, NULL); | ||
398 | static EFIVAR_ATTR(size, 0400, efivar_size_read, NULL); | ||
399 | static EFIVAR_ATTR(data, 0400, efivar_data_read, NULL); | ||
400 | static EFIVAR_ATTR(raw_var, 0600, efivar_show_raw, efivar_store_raw); | ||
401 | |||
402 | static struct attribute *def_attrs[] = { | ||
403 | &efivar_attr_guid.attr, | ||
404 | &efivar_attr_size.attr, | ||
405 | &efivar_attr_attributes.attr, | ||
406 | &efivar_attr_data.attr, | ||
407 | &efivar_attr_raw_var.attr, | ||
408 | NULL, | ||
409 | }; | ||
410 | |||
411 | static struct kobj_type ktype_efivar = { | ||
412 | .release = efivar_release, | ||
413 | .sysfs_ops = &efivar_attr_ops, | ||
414 | .default_attrs = def_attrs, | ||
415 | }; | ||
416 | |||
417 | static ssize_t | ||
418 | dummy(struct subsystem *sub, char *buf) | ||
419 | { | ||
420 | return -ENODEV; | ||
421 | } | ||
422 | |||
423 | static inline void | ||
424 | efivar_unregister(struct efivar_entry *var) | ||
425 | { | ||
426 | kobject_unregister(&var->kobj); | ||
427 | } | ||
428 | |||
429 | |||
430 | static ssize_t | ||
431 | efivar_create(struct subsystem *sub, const char *buf, size_t count) | ||
432 | { | ||
433 | struct efi_variable *new_var = (struct efi_variable *)buf; | ||
434 | struct efivar_entry *search_efivar = NULL; | ||
435 | unsigned long strsize1, strsize2; | ||
436 | struct list_head *pos, *n; | ||
437 | efi_status_t status = EFI_NOT_FOUND; | ||
438 | int found = 0; | ||
439 | |||
440 | if (!capable(CAP_SYS_ADMIN)) | ||
441 | return -EACCES; | ||
442 | |||
443 | spin_lock(&efivars_lock); | ||
444 | |||
445 | /* | ||
446 | * Does this variable already exist? | ||
447 | */ | ||
448 | list_for_each_safe(pos, n, &efivar_list) { | ||
449 | search_efivar = get_efivar_entry(pos); | ||
450 | strsize1 = utf8_strsize(search_efivar->var.VariableName, 1024); | ||
451 | strsize2 = utf8_strsize(new_var->VariableName, 1024); | ||
452 | if (strsize1 == strsize2 && | ||
453 | !memcmp(&(search_efivar->var.VariableName), | ||
454 | new_var->VariableName, strsize1) && | ||
455 | !efi_guidcmp(search_efivar->var.VendorGuid, | ||
456 | new_var->VendorGuid)) { | ||
457 | found = 1; | ||
458 | break; | ||
459 | } | ||
460 | } | ||
461 | if (found) { | ||
462 | spin_unlock(&efivars_lock); | ||
463 | return -EINVAL; | ||
464 | } | ||
465 | |||
466 | /* now *really* create the variable via EFI */ | ||
467 | status = efi.set_variable(new_var->VariableName, | ||
468 | &new_var->VendorGuid, | ||
469 | new_var->Attributes, | ||
470 | new_var->DataSize, | ||
471 | new_var->Data); | ||
472 | |||
473 | if (status != EFI_SUCCESS) { | ||
474 | printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n", | ||
475 | status); | ||
476 | spin_unlock(&efivars_lock); | ||
477 | return -EIO; | ||
478 | } | ||
479 | spin_unlock(&efivars_lock); | ||
480 | |||
481 | /* Create the entry in sysfs. Locking is not required here */ | ||
482 | status = efivar_create_sysfs_entry(utf8_strsize(new_var->VariableName, | ||
483 | 1024), new_var->VariableName, &new_var->VendorGuid); | ||
484 | if (status) { | ||
485 | printk(KERN_WARNING "efivars: variable created, but sysfs entry wasn't.\n"); | ||
486 | } | ||
487 | return count; | ||
488 | } | ||
489 | |||
490 | static ssize_t | ||
491 | efivar_delete(struct subsystem *sub, const char *buf, size_t count) | ||
492 | { | ||
493 | struct efi_variable *del_var = (struct efi_variable *)buf; | ||
494 | struct efivar_entry *search_efivar = NULL; | ||
495 | unsigned long strsize1, strsize2; | ||
496 | struct list_head *pos, *n; | ||
497 | efi_status_t status = EFI_NOT_FOUND; | ||
498 | int found = 0; | ||
499 | |||
500 | if (!capable(CAP_SYS_ADMIN)) | ||
501 | return -EACCES; | ||
502 | |||
503 | spin_lock(&efivars_lock); | ||
504 | |||
505 | /* | ||
506 | * Does this variable already exist? | ||
507 | */ | ||
508 | list_for_each_safe(pos, n, &efivar_list) { | ||
509 | search_efivar = get_efivar_entry(pos); | ||
510 | strsize1 = utf8_strsize(search_efivar->var.VariableName, 1024); | ||
511 | strsize2 = utf8_strsize(del_var->VariableName, 1024); | ||
512 | if (strsize1 == strsize2 && | ||
513 | !memcmp(&(search_efivar->var.VariableName), | ||
514 | del_var->VariableName, strsize1) && | ||
515 | !efi_guidcmp(search_efivar->var.VendorGuid, | ||
516 | del_var->VendorGuid)) { | ||
517 | found = 1; | ||
518 | break; | ||
519 | } | ||
520 | } | ||
521 | if (!found) { | ||
522 | spin_unlock(&efivars_lock); | ||
523 | return -EINVAL; | ||
524 | } | ||
525 | /* force the Attributes/DataSize to 0 to ensure deletion */ | ||
526 | del_var->Attributes = 0; | ||
527 | del_var->DataSize = 0; | ||
528 | |||
529 | status = efi.set_variable(del_var->VariableName, | ||
530 | &del_var->VendorGuid, | ||
531 | del_var->Attributes, | ||
532 | del_var->DataSize, | ||
533 | del_var->Data); | ||
534 | |||
535 | if (status != EFI_SUCCESS) { | ||
536 | printk(KERN_WARNING "efivars: set_variable() failed: status=%lx\n", | ||
537 | status); | ||
538 | spin_unlock(&efivars_lock); | ||
539 | return -EIO; | ||
540 | } | ||
541 | /* We need to release this lock before unregistering. */ | ||
542 | spin_unlock(&efivars_lock); | ||
543 | |||
544 | efivar_unregister(search_efivar); | ||
545 | |||
546 | /* It's dead Jim.... */ | ||
547 | return count; | ||
548 | } | ||
549 | |||
550 | static VAR_SUBSYS_ATTR(new_var, 0200, dummy, efivar_create); | ||
551 | static VAR_SUBSYS_ATTR(del_var, 0200, dummy, efivar_delete); | ||
552 | |||
553 | static struct subsys_attribute *var_subsys_attrs[] = { | ||
554 | &var_subsys_attr_new_var, | ||
555 | &var_subsys_attr_del_var, | ||
556 | NULL, | ||
557 | }; | ||
558 | |||
559 | /* | ||
560 | * Let's not leave out systab information that snuck into | ||
561 | * the efivars driver | ||
562 | */ | ||
563 | static ssize_t | ||
564 | systab_read(struct subsystem *entry, char *buf) | ||
565 | { | ||
566 | char *str = buf; | ||
567 | |||
568 | if (!entry || !buf) | ||
569 | return -EINVAL; | ||
570 | |||
571 | if (efi.mps) | ||
572 | str += sprintf(str, "MPS=0x%lx\n", __pa(efi.mps)); | ||
573 | if (efi.acpi20) | ||
574 | str += sprintf(str, "ACPI20=0x%lx\n", __pa(efi.acpi20)); | ||
575 | if (efi.acpi) | ||
576 | str += sprintf(str, "ACPI=0x%lx\n", __pa(efi.acpi)); | ||
577 | if (efi.smbios) | ||
578 | str += sprintf(str, "SMBIOS=0x%lx\n", __pa(efi.smbios)); | ||
579 | if (efi.hcdp) | ||
580 | str += sprintf(str, "HCDP=0x%lx\n", __pa(efi.hcdp)); | ||
581 | if (efi.boot_info) | ||
582 | str += sprintf(str, "BOOTINFO=0x%lx\n", __pa(efi.boot_info)); | ||
583 | if (efi.uga) | ||
584 | str += sprintf(str, "UGA=0x%lx\n", __pa(efi.uga)); | ||
585 | |||
586 | return str - buf; | ||
587 | } | ||
588 | |||
589 | static EFI_ATTR(systab, 0400, systab_read, NULL); | ||
590 | |||
591 | static struct subsys_attribute *efi_subsys_attrs[] = { | ||
592 | &efi_attr_systab, | ||
593 | NULL, /* maybe more in the future? */ | ||
594 | }; | ||
595 | |||
596 | static decl_subsys(vars, &ktype_efivar, NULL); | ||
597 | static decl_subsys(efi, NULL, NULL); | ||
598 | |||
599 | /* | ||
600 | * efivar_create_sysfs_entry() | ||
601 | * Requires: | ||
602 | * variable_name_size = number of bytes required to hold | ||
603 | * variable_name (not counting the NULL | ||
604 | * character at the end. | ||
605 | * efivars_lock is not held on entry or exit. | ||
606 | * Returns 1 on failure, 0 on success | ||
607 | */ | ||
608 | static int | ||
609 | efivar_create_sysfs_entry(unsigned long variable_name_size, | ||
610 | efi_char16_t *variable_name, | ||
611 | efi_guid_t *vendor_guid) | ||
612 | { | ||
613 | int i, short_name_size = variable_name_size / sizeof(efi_char16_t) + 38; | ||
614 | char *short_name; | ||
615 | struct efivar_entry *new_efivar; | ||
616 | |||
617 | short_name = kmalloc(short_name_size + 1, GFP_KERNEL); | ||
618 | new_efivar = kmalloc(sizeof(struct efivar_entry), GFP_KERNEL); | ||
619 | |||
620 | if (!short_name || !new_efivar) { | ||
621 | if (short_name) kfree(short_name); | ||
622 | if (new_efivar) kfree(new_efivar); | ||
623 | return 1; | ||
624 | } | ||
625 | memset(short_name, 0, short_name_size+1); | ||
626 | memset(new_efivar, 0, sizeof(struct efivar_entry)); | ||
627 | |||
628 | memcpy(new_efivar->var.VariableName, variable_name, | ||
629 | variable_name_size); | ||
630 | memcpy(&(new_efivar->var.VendorGuid), vendor_guid, sizeof(efi_guid_t)); | ||
631 | |||
632 | /* Convert Unicode to normal chars (assume top bits are 0), | ||
633 | ala UTF-8 */ | ||
634 | for (i=0; i < (int)(variable_name_size / sizeof(efi_char16_t)); i++) { | ||
635 | short_name[i] = variable_name[i] & 0xFF; | ||
636 | } | ||
637 | /* This is ugly, but necessary to separate one vendor's | ||
638 | private variables from another's. */ | ||
639 | |||
640 | *(short_name + strlen(short_name)) = '-'; | ||
641 | efi_guid_unparse(vendor_guid, short_name + strlen(short_name)); | ||
642 | |||
643 | kobject_set_name(&new_efivar->kobj, "%s", short_name); | ||
644 | kobj_set_kset_s(new_efivar, vars_subsys); | ||
645 | kobject_register(&new_efivar->kobj); | ||
646 | |||
647 | kfree(short_name); short_name = NULL; | ||
648 | |||
649 | spin_lock(&efivars_lock); | ||
650 | list_add(&new_efivar->list, &efivar_list); | ||
651 | spin_unlock(&efivars_lock); | ||
652 | |||
653 | return 0; | ||
654 | } | ||
655 | /* | ||
656 | * For now we register the efi subsystem with the firmware subsystem | ||
657 | * and the vars subsystem with the efi subsystem. In the future, it | ||
658 | * might make sense to split off the efi subsystem into its own | ||
659 | * driver, but for now only efivars will register with it, so just | ||
660 | * include it here. | ||
661 | */ | ||
662 | |||
663 | static int __init | ||
664 | efivars_init(void) | ||
665 | { | ||
666 | efi_status_t status = EFI_NOT_FOUND; | ||
667 | efi_guid_t vendor_guid; | ||
668 | efi_char16_t *variable_name; | ||
669 | struct subsys_attribute *attr; | ||
670 | unsigned long variable_name_size = 1024; | ||
671 | int i, error = 0; | ||
672 | |||
673 | if (!efi_enabled) | ||
674 | return -ENODEV; | ||
675 | |||
676 | variable_name = kmalloc(variable_name_size, GFP_KERNEL); | ||
677 | if (!variable_name) { | ||
678 | printk(KERN_ERR "efivars: Memory allocation failed.\n"); | ||
679 | return -ENOMEM; | ||
680 | } | ||
681 | |||
682 | memset(variable_name, 0, variable_name_size); | ||
683 | |||
684 | printk(KERN_INFO "EFI Variables Facility v%s %s\n", EFIVARS_VERSION, | ||
685 | EFIVARS_DATE); | ||
686 | |||
687 | /* | ||
688 | * For now we'll register the efi subsys within this driver | ||
689 | */ | ||
690 | |||
691 | error = firmware_register(&efi_subsys); | ||
692 | |||
693 | if (error) { | ||
694 | printk(KERN_ERR "efivars: Firmware registration failed with error %d.\n", error); | ||
695 | goto out_free; | ||
696 | } | ||
697 | |||
698 | kset_set_kset_s(&vars_subsys, efi_subsys); | ||
699 | |||
700 | error = subsystem_register(&vars_subsys); | ||
701 | |||
702 | if (error) { | ||
703 | printk(KERN_ERR "efivars: Subsystem registration failed with error %d.\n", error); | ||
704 | goto out_firmware_unregister; | ||
705 | } | ||
706 | |||
707 | /* | ||
708 | * Per EFI spec, the maximum storage allocated for both | ||
709 | * the variable name and variable data is 1024 bytes. | ||
710 | */ | ||
711 | |||
712 | do { | ||
713 | variable_name_size = 1024; | ||
714 | |||
715 | status = efi.get_next_variable(&variable_name_size, | ||
716 | variable_name, | ||
717 | &vendor_guid); | ||
718 | switch (status) { | ||
719 | case EFI_SUCCESS: | ||
720 | efivar_create_sysfs_entry(variable_name_size, | ||
721 | variable_name, | ||
722 | &vendor_guid); | ||
723 | break; | ||
724 | case EFI_NOT_FOUND: | ||
725 | break; | ||
726 | default: | ||
727 | printk(KERN_WARNING "efivars: get_next_variable: status=%lx\n", | ||
728 | status); | ||
729 | status = EFI_NOT_FOUND; | ||
730 | break; | ||
731 | } | ||
732 | } while (status != EFI_NOT_FOUND); | ||
733 | |||
734 | /* | ||
735 | * Now add attributes to allow creation of new vars | ||
736 | * and deletion of existing ones... | ||
737 | */ | ||
738 | |||
739 | for (i = 0; (attr = var_subsys_attrs[i]) && !error; i++) { | ||
740 | if (attr->show && attr->store) | ||
741 | error = subsys_create_file(&vars_subsys, attr); | ||
742 | } | ||
743 | |||
744 | /* Don't forget the systab entry */ | ||
745 | |||
746 | for (i = 0; (attr = efi_subsys_attrs[i]) && !error; i++) { | ||
747 | if (attr->show) | ||
748 | error = subsys_create_file(&efi_subsys, attr); | ||
749 | } | ||
750 | |||
751 | if (error) | ||
752 | printk(KERN_ERR "efivars: Sysfs attribute export failed with error %d.\n", error); | ||
753 | else | ||
754 | goto out_free; | ||
755 | |||
756 | subsystem_unregister(&vars_subsys); | ||
757 | |||
758 | out_firmware_unregister: | ||
759 | firmware_unregister(&efi_subsys); | ||
760 | |||
761 | out_free: | ||
762 | kfree(variable_name); | ||
763 | |||
764 | return error; | ||
765 | } | ||
766 | |||
767 | static void __exit | ||
768 | efivars_exit(void) | ||
769 | { | ||
770 | struct list_head *pos, *n; | ||
771 | |||
772 | list_for_each_safe(pos, n, &efivar_list) | ||
773 | efivar_unregister(get_efivar_entry(pos)); | ||
774 | |||
775 | subsystem_unregister(&vars_subsys); | ||
776 | firmware_unregister(&efi_subsys); | ||
777 | } | ||
778 | |||
779 | module_init(efivars_init); | ||
780 | module_exit(efivars_exit); | ||
781 | |||
diff --git a/drivers/firmware/pcdp.c b/drivers/firmware/pcdp.c new file mode 100644 index 000000000000..6d5df6c2efa2 --- /dev/null +++ b/drivers/firmware/pcdp.c | |||
@@ -0,0 +1,100 @@ | |||
1 | /* | ||
2 | * Parse the EFI PCDP table to locate the console device. | ||
3 | * | ||
4 | * (c) Copyright 2002, 2003, 2004 Hewlett-Packard Development Company, L.P. | ||
5 | * Khalid Aziz <khalid.aziz@hp.com> | ||
6 | * Alex Williamson <alex.williamson@hp.com> | ||
7 | * Bjorn Helgaas <bjorn.helgaas@hp.com> | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | |||
14 | #include <linux/acpi.h> | ||
15 | #include <linux/console.h> | ||
16 | #include <linux/efi.h> | ||
17 | #include <linux/serial.h> | ||
18 | #include "pcdp.h" | ||
19 | |||
20 | static int __init | ||
21 | setup_serial_console(struct pcdp_uart *uart) | ||
22 | { | ||
23 | #ifdef CONFIG_SERIAL_8250_CONSOLE | ||
24 | int mmio; | ||
25 | static char options[64]; | ||
26 | |||
27 | mmio = (uart->addr.address_space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY); | ||
28 | snprintf(options, sizeof(options), "console=uart,%s,0x%lx,%lun%d", | ||
29 | mmio ? "mmio" : "io", uart->addr.address, uart->baud, | ||
30 | uart->bits ? uart->bits : 8); | ||
31 | |||
32 | return early_serial_console_init(options); | ||
33 | #else | ||
34 | return -ENODEV; | ||
35 | #endif | ||
36 | } | ||
37 | |||
38 | static int __init | ||
39 | setup_vga_console(struct pcdp_vga *vga) | ||
40 | { | ||
41 | #if defined(CONFIG_VT) && defined(CONFIG_VGA_CONSOLE) | ||
42 | if (efi_mem_type(0xA0000) == EFI_CONVENTIONAL_MEMORY) { | ||
43 | printk(KERN_ERR "PCDP: VGA selected, but frame buffer is not MMIO!\n"); | ||
44 | return -ENODEV; | ||
45 | } | ||
46 | |||
47 | conswitchp = &vga_con; | ||
48 | printk(KERN_INFO "PCDP: VGA console\n"); | ||
49 | return 0; | ||
50 | #else | ||
51 | return -ENODEV; | ||
52 | #endif | ||
53 | } | ||
54 | |||
55 | int __init | ||
56 | efi_setup_pcdp_console(char *cmdline) | ||
57 | { | ||
58 | struct pcdp *pcdp; | ||
59 | struct pcdp_uart *uart; | ||
60 | struct pcdp_device *dev, *end; | ||
61 | int i, serial = 0; | ||
62 | |||
63 | pcdp = efi.hcdp; | ||
64 | if (!pcdp) | ||
65 | return -ENODEV; | ||
66 | |||
67 | printk(KERN_INFO "PCDP: v%d at 0x%lx\n", pcdp->rev, __pa(pcdp)); | ||
68 | |||
69 | if (strstr(cmdline, "console=hcdp")) { | ||
70 | if (pcdp->rev < 3) | ||
71 | serial = 1; | ||
72 | } else if (strstr(cmdline, "console=")) { | ||
73 | printk(KERN_INFO "Explicit \"console=\"; ignoring PCDP\n"); | ||
74 | return -ENODEV; | ||
75 | } | ||
76 | |||
77 | if (pcdp->rev < 3 && efi_uart_console_only()) | ||
78 | serial = 1; | ||
79 | |||
80 | for (i = 0, uart = pcdp->uart; i < pcdp->num_uarts; i++, uart++) { | ||
81 | if (uart->flags & PCDP_UART_PRIMARY_CONSOLE || serial) { | ||
82 | if (uart->type == PCDP_CONSOLE_UART) { | ||
83 | return setup_serial_console(uart); | ||
84 | } | ||
85 | } | ||
86 | } | ||
87 | |||
88 | end = (struct pcdp_device *) ((u8 *) pcdp + pcdp->length); | ||
89 | for (dev = (struct pcdp_device *) (pcdp->uart + pcdp->num_uarts); | ||
90 | dev < end; | ||
91 | dev = (struct pcdp_device *) ((u8 *) dev + dev->length)) { | ||
92 | if (dev->flags & PCDP_PRIMARY_CONSOLE) { | ||
93 | if (dev->type == PCDP_CONSOLE_VGA) { | ||
94 | return setup_vga_console((struct pcdp_vga *) dev); | ||
95 | } | ||
96 | } | ||
97 | } | ||
98 | |||
99 | return -ENODEV; | ||
100 | } | ||
diff --git a/drivers/firmware/pcdp.h b/drivers/firmware/pcdp.h new file mode 100644 index 000000000000..863bb6f768c3 --- /dev/null +++ b/drivers/firmware/pcdp.h | |||
@@ -0,0 +1,84 @@ | |||
1 | /* | ||
2 | * Definitions for PCDP-defined console devices | ||
3 | * | ||
4 | * v1.0a: http://www.dig64.org/specifications/DIG64_HCDPv10a_01.pdf | ||
5 | * v2.0: http://www.dig64.org/specifications/DIG64_HCDPv20_042804.pdf | ||
6 | * | ||
7 | * (c) Copyright 2002, 2004 Hewlett-Packard Development Company, L.P. | ||
8 | * Khalid Aziz <khalid.aziz@hp.com> | ||
9 | * Bjorn Helgaas <bjorn.helgaas@hp.com> | ||
10 | * | ||
11 | * This program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the GNU General Public License version 2 as | ||
13 | * published by the Free Software Foundation. | ||
14 | */ | ||
15 | |||
16 | #define PCDP_CONSOLE 0 | ||
17 | #define PCDP_DEBUG 1 | ||
18 | #define PCDP_CONSOLE_OUTPUT 2 | ||
19 | #define PCDP_CONSOLE_INPUT 3 | ||
20 | |||
21 | #define PCDP_UART (0 << 3) | ||
22 | #define PCDP_VGA (1 << 3) | ||
23 | #define PCDP_USB (2 << 3) | ||
24 | |||
25 | /* pcdp_uart.type and pcdp_device.type */ | ||
26 | #define PCDP_CONSOLE_UART (PCDP_UART | PCDP_CONSOLE) | ||
27 | #define PCDP_DEBUG_UART (PCDP_UART | PCDP_DEBUG) | ||
28 | #define PCDP_CONSOLE_VGA (PCDP_VGA | PCDP_CONSOLE_OUTPUT) | ||
29 | #define PCDP_CONSOLE_USB (PCDP_USB | PCDP_CONSOLE_INPUT) | ||
30 | |||
31 | /* pcdp_uart.flags */ | ||
32 | #define PCDP_UART_EDGE_SENSITIVE (1 << 0) | ||
33 | #define PCDP_UART_ACTIVE_LOW (1 << 1) | ||
34 | #define PCDP_UART_PRIMARY_CONSOLE (1 << 2) | ||
35 | #define PCDP_UART_IRQ (1 << 6) /* in pci_func for rev < 3 */ | ||
36 | #define PCDP_UART_PCI (1 << 7) /* in pci_func for rev < 3 */ | ||
37 | |||
38 | struct pcdp_uart { | ||
39 | u8 type; | ||
40 | u8 bits; | ||
41 | u8 parity; | ||
42 | u8 stop_bits; | ||
43 | u8 pci_seg; | ||
44 | u8 pci_bus; | ||
45 | u8 pci_dev; | ||
46 | u8 pci_func; | ||
47 | u64 baud; | ||
48 | struct acpi_generic_address addr; | ||
49 | u16 pci_dev_id; | ||
50 | u16 pci_vendor_id; | ||
51 | u32 gsi; | ||
52 | u32 clock_rate; | ||
53 | u8 pci_prog_intfc; | ||
54 | u8 flags; | ||
55 | }; | ||
56 | |||
57 | struct pcdp_vga { | ||
58 | u8 count; /* address space descriptors */ | ||
59 | }; | ||
60 | |||
61 | /* pcdp_device.flags */ | ||
62 | #define PCDP_PRIMARY_CONSOLE 1 | ||
63 | |||
64 | struct pcdp_device { | ||
65 | u8 type; | ||
66 | u8 flags; | ||
67 | u16 length; | ||
68 | u16 efi_index; | ||
69 | }; | ||
70 | |||
71 | struct pcdp { | ||
72 | u8 signature[4]; | ||
73 | u32 length; | ||
74 | u8 rev; /* PCDP v2.0 is rev 3 */ | ||
75 | u8 chksum; | ||
76 | u8 oemid[6]; | ||
77 | u8 oem_tabid[8]; | ||
78 | u32 oem_rev; | ||
79 | u8 creator_id[4]; | ||
80 | u32 creator_rev; | ||
81 | u32 num_uarts; | ||
82 | struct pcdp_uart uart[0]; /* actual size is num_uarts */ | ||
83 | /* remainder of table is pcdp_device structures */ | ||
84 | }; | ||