2 * apple-properties.c - EFI device properties on Macs
3 * Copyright (C) 2016 Lukas Wunner <lukas@wunner.de>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License (version 2) as
7 * published by the Free Software Foundation.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, see <http://www.gnu.org/licenses/>.
18 #define pr_fmt(fmt) "apple-properties: " fmt
20 #include <linux/bootmem.h>
21 #include <linux/efi.h>
22 #include <linux/platform_data/x86/apple.h>
23 #include <linux/property.h>
24 #include <linux/slab.h>
25 #include <linux/ucs2_string.h>
26 #include <asm/setup.h>
28 static bool dump_properties __initdata
;
30 static int __init
dump_properties_enable(char *arg
)
32 dump_properties
= true;
36 __setup("dump_apple_properties", dump_properties_enable
);
41 struct efi_dev_path path
[0];
43 * followed by key/value pairs, each key and value preceded by u32 len,
44 * len includes itself, value may be empty (in which case its len is 4)
48 struct properties_header
{
52 struct dev_header dev_header
[0];
55 static u8 one __initdata
= 1;
57 static void __init
unmarshal_key_value_pairs(struct dev_header
*dev_header
,
58 struct device
*dev
, void *ptr
,
59 struct property_entry entry
[])
63 for (i
= 0; i
< dev_header
->prop_count
; i
++) {
64 int remaining
= dev_header
->len
- (ptr
- (void *)dev_header
);
68 if (sizeof(key_len
) > remaining
)
71 key_len
= *(typeof(key_len
) *)ptr
;
72 if (key_len
+ sizeof(val_len
) > remaining
||
73 key_len
< sizeof(key_len
) + sizeof(efi_char16_t
) ||
74 *(efi_char16_t
*)(ptr
+ sizeof(key_len
)) == 0) {
75 dev_err(dev
, "invalid property name len at %#zx\n",
76 ptr
- (void *)dev_header
);
80 val_len
= *(typeof(val_len
) *)(ptr
+ key_len
);
81 if (key_len
+ val_len
> remaining
||
82 val_len
< sizeof(val_len
)) {
83 dev_err(dev
, "invalid property val len at %#zx\n",
84 ptr
- (void *)dev_header
+ key_len
);
88 /* 4 bytes to accommodate UTF-8 code points + null byte */
89 key
= kzalloc((key_len
- sizeof(key_len
)) * 4 + 1, GFP_KERNEL
);
91 dev_err(dev
, "cannot allocate property name\n");
94 ucs2_as_utf8(key
, ptr
+ sizeof(key_len
),
95 key_len
- sizeof(key_len
));
98 entry
[i
].is_array
= true;
99 entry
[i
].length
= val_len
- sizeof(val_len
);
100 entry
[i
].pointer
.raw_data
= ptr
+ key_len
+ sizeof(val_len
);
101 if (!entry
[i
].length
) {
102 /* driver core doesn't accept empty properties */
104 entry
[i
].pointer
.raw_data
= &one
;
107 if (dump_properties
) {
108 dev_info(dev
, "property: %s\n", entry
[i
].name
);
109 print_hex_dump(KERN_INFO
, pr_fmt(), DUMP_PREFIX_OFFSET
,
110 16, 1, entry
[i
].pointer
.raw_data
,
111 entry
[i
].length
, true);
114 ptr
+= key_len
+ val_len
;
117 if (i
!= dev_header
->prop_count
) {
118 dev_err(dev
, "got %d device properties, expected %u\n", i
,
119 dev_header
->prop_count
);
120 print_hex_dump(KERN_ERR
, pr_fmt(), DUMP_PREFIX_OFFSET
,
121 16, 1, dev_header
, dev_header
->len
, true);
125 dev_info(dev
, "assigning %d device properties\n", i
);
128 static int __init
unmarshal_devices(struct properties_header
*properties
)
130 size_t offset
= offsetof(struct properties_header
, dev_header
[0]);
132 while (offset
+ sizeof(struct dev_header
) < properties
->len
) {
133 struct dev_header
*dev_header
= (void *)properties
+ offset
;
134 struct property_entry
*entry
= NULL
;
140 if (offset
+ dev_header
->len
> properties
->len
||
141 dev_header
->len
<= sizeof(*dev_header
)) {
142 pr_err("invalid len in dev_header at %#zx\n", offset
);
146 ptr
= dev_header
->path
;
147 len
= dev_header
->len
- sizeof(*dev_header
);
149 dev
= efi_get_device_by_path((struct efi_dev_path
**)&ptr
, &len
);
151 pr_err("device path parse error %ld at %#zx:\n",
152 PTR_ERR(dev
), ptr
- (void *)dev_header
);
153 print_hex_dump(KERN_ERR
, pr_fmt(), DUMP_PREFIX_OFFSET
,
154 16, 1, dev_header
, dev_header
->len
, true);
159 entry
= kcalloc(dev_header
->prop_count
+ 1, sizeof(*entry
),
162 dev_err(dev
, "cannot allocate properties\n");
166 unmarshal_key_value_pairs(dev_header
, dev
, ptr
, entry
);
170 ret
= device_add_properties(dev
, entry
); /* makes deep copy */
172 dev_err(dev
, "error %d assigning properties\n", ret
);
174 for (i
= 0; entry
[i
].name
; i
++)
175 kfree(entry
[i
].name
);
180 offset
+= dev_header
->len
;
186 static int __init
map_properties(void)
188 struct properties_header
*properties
;
189 struct setup_data
*data
;
194 if (!x86_apple_machine
)
197 pa_data
= boot_params
.hdr
.setup_data
;
199 data
= ioremap(pa_data
, sizeof(*data
));
201 pr_err("cannot map setup_data header\n");
205 if (data
->type
!= SETUP_APPLE_PROPERTIES
) {
206 pa_data
= data
->next
;
211 data_len
= data
->len
;
214 data
= ioremap(pa_data
, sizeof(*data
) + data_len
);
216 pr_err("cannot map setup_data payload\n");
220 properties
= (struct properties_header
*)data
->data
;
221 if (properties
->version
!= 1) {
222 pr_err("unsupported version:\n");
223 print_hex_dump(KERN_ERR
, pr_fmt(), DUMP_PREFIX_OFFSET
,
224 16, 1, properties
, data_len
, true);
226 } else if (properties
->len
!= data_len
) {
227 pr_err("length mismatch, expected %u\n", data_len
);
228 print_hex_dump(KERN_ERR
, pr_fmt(), DUMP_PREFIX_OFFSET
,
229 16, 1, properties
, data_len
, true);
232 ret
= unmarshal_devices(properties
);
235 * Can only free the setup_data payload but not its header
236 * to avoid breaking the chain of ->next pointers.
240 free_bootmem_late(pa_data
+ sizeof(*data
), data_len
);
247 fs_initcall(map_properties
);