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/>.
17 * Note, all properties are considered as u8 arrays.
18 * To get a value of any of them the caller must use device_property_read_u8_array().
21 #define pr_fmt(fmt) "apple-properties: " fmt
23 #include <linux/bootmem.h>
24 #include <linux/efi.h>
26 #include <linux/platform_data/x86/apple.h>
27 #include <linux/property.h>
28 #include <linux/slab.h>
29 #include <linux/ucs2_string.h>
30 #include <asm/setup.h>
32 static bool dump_properties __initdata
;
34 static int __init
dump_properties_enable(char *arg
)
36 dump_properties
= true;
40 __setup("dump_apple_properties", dump_properties_enable
);
45 struct efi_dev_path path
[0];
47 * followed by key/value pairs, each key and value preceded by u32 len,
48 * len includes itself, value may be empty (in which case its len is 4)
52 struct properties_header
{
56 struct dev_header dev_header
[0];
59 static void __init
unmarshal_key_value_pairs(struct dev_header
*dev_header
,
60 struct device
*dev
, void *ptr
,
61 struct property_entry entry
[])
65 for (i
= 0; i
< dev_header
->prop_count
; i
++) {
66 int remaining
= dev_header
->len
- (ptr
- (void *)dev_header
);
70 if (sizeof(key_len
) > remaining
)
73 key_len
= *(typeof(key_len
) *)ptr
;
74 if (key_len
+ sizeof(val_len
) > remaining
||
75 key_len
< sizeof(key_len
) + sizeof(efi_char16_t
) ||
76 *(efi_char16_t
*)(ptr
+ sizeof(key_len
)) == 0) {
77 dev_err(dev
, "invalid property name len at %#zx\n",
78 ptr
- (void *)dev_header
);
82 val_len
= *(typeof(val_len
) *)(ptr
+ key_len
);
83 if (key_len
+ val_len
> remaining
||
84 val_len
< sizeof(val_len
)) {
85 dev_err(dev
, "invalid property val len at %#zx\n",
86 ptr
- (void *)dev_header
+ key_len
);
90 /* 4 bytes to accommodate UTF-8 code points + null byte */
91 key
= kzalloc((key_len
- sizeof(key_len
)) * 4 + 1, GFP_KERNEL
);
93 dev_err(dev
, "cannot allocate property name\n");
96 ucs2_as_utf8(key
, ptr
+ sizeof(key_len
),
97 key_len
- sizeof(key_len
));
100 entry
[i
].length
= val_len
- sizeof(val_len
);
101 entry
[i
].is_array
= !!entry
[i
].length
;
102 entry
[i
].type
= DEV_PROP_U8
;
103 entry
[i
].pointer
.u8_data
= ptr
+ key_len
+ sizeof(val_len
);
105 if (dump_properties
) {
106 dev_info(dev
, "property: %s\n", entry
[i
].name
);
107 print_hex_dump(KERN_INFO
, pr_fmt(), DUMP_PREFIX_OFFSET
,
108 16, 1, entry
[i
].pointer
.u8_data
,
109 entry
[i
].length
, true);
112 ptr
+= key_len
+ val_len
;
115 if (i
!= dev_header
->prop_count
) {
116 dev_err(dev
, "got %d device properties, expected %u\n", i
,
117 dev_header
->prop_count
);
118 print_hex_dump(KERN_ERR
, pr_fmt(), DUMP_PREFIX_OFFSET
,
119 16, 1, dev_header
, dev_header
->len
, true);
123 dev_info(dev
, "assigning %d device properties\n", i
);
126 static int __init
unmarshal_devices(struct properties_header
*properties
)
128 size_t offset
= offsetof(struct properties_header
, dev_header
[0]);
130 while (offset
+ sizeof(struct dev_header
) < properties
->len
) {
131 struct dev_header
*dev_header
= (void *)properties
+ offset
;
132 struct property_entry
*entry
= NULL
;
138 if (offset
+ dev_header
->len
> properties
->len
||
139 dev_header
->len
<= sizeof(*dev_header
)) {
140 pr_err("invalid len in dev_header at %#zx\n", offset
);
144 ptr
= dev_header
->path
;
145 len
= dev_header
->len
- sizeof(*dev_header
);
147 dev
= efi_get_device_by_path((struct efi_dev_path
**)&ptr
, &len
);
149 pr_err("device path parse error %ld at %#zx:\n",
150 PTR_ERR(dev
), ptr
- (void *)dev_header
);
151 print_hex_dump(KERN_ERR
, pr_fmt(), DUMP_PREFIX_OFFSET
,
152 16, 1, dev_header
, dev_header
->len
, true);
157 entry
= kcalloc(dev_header
->prop_count
+ 1, sizeof(*entry
),
160 dev_err(dev
, "cannot allocate properties\n");
164 unmarshal_key_value_pairs(dev_header
, dev
, ptr
, entry
);
168 ret
= device_add_properties(dev
, entry
); /* makes deep copy */
170 dev_err(dev
, "error %d assigning properties\n", ret
);
172 for (i
= 0; entry
[i
].name
; i
++)
173 kfree(entry
[i
].name
);
178 offset
+= dev_header
->len
;
184 static int __init
map_properties(void)
186 struct properties_header
*properties
;
187 struct setup_data
*data
;
192 if (!x86_apple_machine
)
195 pa_data
= boot_params
.hdr
.setup_data
;
197 data
= memremap(pa_data
, sizeof(*data
), MEMREMAP_WB
);
199 pr_err("cannot map setup_data header\n");
203 if (data
->type
!= SETUP_APPLE_PROPERTIES
) {
204 pa_data
= data
->next
;
209 data_len
= data
->len
;
212 data
= memremap(pa_data
, sizeof(*data
) + data_len
, MEMREMAP_WB
);
214 pr_err("cannot map setup_data payload\n");
218 properties
= (struct properties_header
*)data
->data
;
219 if (properties
->version
!= 1) {
220 pr_err("unsupported version:\n");
221 print_hex_dump(KERN_ERR
, pr_fmt(), DUMP_PREFIX_OFFSET
,
222 16, 1, properties
, data_len
, true);
224 } else if (properties
->len
!= data_len
) {
225 pr_err("length mismatch, expected %u\n", data_len
);
226 print_hex_dump(KERN_ERR
, pr_fmt(), DUMP_PREFIX_OFFSET
,
227 16, 1, properties
, data_len
, true);
230 ret
= unmarshal_devices(properties
);
233 * Can only free the setup_data payload but not its header
234 * to avoid breaking the chain of ->next pointers.
238 free_bootmem_late(pa_data
+ sizeof(*data
), data_len
);
245 fs_initcall(map_properties
);