1 // SPDX-License-Identifier: GPL-2.0
3 * apple-properties.c - EFI device properties on Macs
4 * Copyright (C) 2016 Lukas Wunner <lukas@wunner.de>
6 * Note, all properties are considered as u8 arrays.
7 * To get a value of any of them the caller must use device_property_read_u8_array().
10 #define pr_fmt(fmt) "apple-properties: " fmt
12 #include <linux/memblock.h>
13 #include <linux/efi.h>
15 #include <linux/platform_data/x86/apple.h>
16 #include <linux/property.h>
17 #include <linux/slab.h>
18 #include <linux/ucs2_string.h>
19 #include <asm/setup.h>
21 static bool dump_properties __initdata
;
23 static int __init
dump_properties_enable(char *arg
)
25 dump_properties
= true;
29 __setup("dump_apple_properties", dump_properties_enable
);
34 struct efi_dev_path path
[0];
36 * followed by key/value pairs, each key and value preceded by u32 len,
37 * len includes itself, value may be empty (in which case its len is 4)
41 struct properties_header
{
45 struct dev_header dev_header
[0];
48 static void __init
unmarshal_key_value_pairs(struct dev_header
*dev_header
,
49 struct device
*dev
, void *ptr
,
50 struct property_entry entry
[])
54 for (i
= 0; i
< dev_header
->prop_count
; i
++) {
55 int remaining
= dev_header
->len
- (ptr
- (void *)dev_header
);
59 if (sizeof(key_len
) > remaining
)
62 key_len
= *(typeof(key_len
) *)ptr
;
63 if (key_len
+ sizeof(val_len
) > remaining
||
64 key_len
< sizeof(key_len
) + sizeof(efi_char16_t
) ||
65 *(efi_char16_t
*)(ptr
+ sizeof(key_len
)) == 0) {
66 dev_err(dev
, "invalid property name len at %#zx\n",
67 ptr
- (void *)dev_header
);
71 val_len
= *(typeof(val_len
) *)(ptr
+ key_len
);
72 if (key_len
+ val_len
> remaining
||
73 val_len
< sizeof(val_len
)) {
74 dev_err(dev
, "invalid property val len at %#zx\n",
75 ptr
- (void *)dev_header
+ key_len
);
79 /* 4 bytes to accommodate UTF-8 code points + null byte */
80 key
= kzalloc((key_len
- sizeof(key_len
)) * 4 + 1, GFP_KERNEL
);
82 dev_err(dev
, "cannot allocate property name\n");
85 ucs2_as_utf8(key
, ptr
+ sizeof(key_len
),
86 key_len
- sizeof(key_len
));
89 entry
[i
].length
= val_len
- sizeof(val_len
);
90 entry
[i
].is_array
= !!entry
[i
].length
;
91 entry
[i
].type
= DEV_PROP_U8
;
92 entry
[i
].pointer
.u8_data
= ptr
+ key_len
+ sizeof(val_len
);
94 if (dump_properties
) {
95 dev_info(dev
, "property: %s\n", entry
[i
].name
);
96 print_hex_dump(KERN_INFO
, pr_fmt(), DUMP_PREFIX_OFFSET
,
97 16, 1, entry
[i
].pointer
.u8_data
,
98 entry
[i
].length
, true);
101 ptr
+= key_len
+ val_len
;
104 if (i
!= dev_header
->prop_count
) {
105 dev_err(dev
, "got %d device properties, expected %u\n", i
,
106 dev_header
->prop_count
);
107 print_hex_dump(KERN_ERR
, pr_fmt(), DUMP_PREFIX_OFFSET
,
108 16, 1, dev_header
, dev_header
->len
, true);
112 dev_info(dev
, "assigning %d device properties\n", i
);
115 static int __init
unmarshal_devices(struct properties_header
*properties
)
117 size_t offset
= offsetof(struct properties_header
, dev_header
[0]);
119 while (offset
+ sizeof(struct dev_header
) < properties
->len
) {
120 struct dev_header
*dev_header
= (void *)properties
+ offset
;
121 struct property_entry
*entry
= NULL
;
127 if (offset
+ dev_header
->len
> properties
->len
||
128 dev_header
->len
<= sizeof(*dev_header
)) {
129 pr_err("invalid len in dev_header at %#zx\n", offset
);
133 ptr
= dev_header
->path
;
134 len
= dev_header
->len
- sizeof(*dev_header
);
136 dev
= efi_get_device_by_path((struct efi_dev_path
**)&ptr
, &len
);
138 pr_err("device path parse error %ld at %#zx:\n",
139 PTR_ERR(dev
), ptr
- (void *)dev_header
);
140 print_hex_dump(KERN_ERR
, pr_fmt(), DUMP_PREFIX_OFFSET
,
141 16, 1, dev_header
, dev_header
->len
, true);
146 entry
= kcalloc(dev_header
->prop_count
+ 1, sizeof(*entry
),
149 dev_err(dev
, "cannot allocate properties\n");
153 unmarshal_key_value_pairs(dev_header
, dev
, ptr
, entry
);
157 ret
= device_add_properties(dev
, entry
); /* makes deep copy */
159 dev_err(dev
, "error %d assigning properties\n", ret
);
161 for (i
= 0; entry
[i
].name
; i
++)
162 kfree(entry
[i
].name
);
167 offset
+= dev_header
->len
;
173 static int __init
map_properties(void)
175 struct properties_header
*properties
;
176 struct setup_data
*data
;
181 if (!x86_apple_machine
)
184 pa_data
= boot_params
.hdr
.setup_data
;
186 data
= memremap(pa_data
, sizeof(*data
), MEMREMAP_WB
);
188 pr_err("cannot map setup_data header\n");
192 if (data
->type
!= SETUP_APPLE_PROPERTIES
) {
193 pa_data
= data
->next
;
198 data_len
= data
->len
;
201 data
= memremap(pa_data
, sizeof(*data
) + data_len
, MEMREMAP_WB
);
203 pr_err("cannot map setup_data payload\n");
207 properties
= (struct properties_header
*)data
->data
;
208 if (properties
->version
!= 1) {
209 pr_err("unsupported version:\n");
210 print_hex_dump(KERN_ERR
, pr_fmt(), DUMP_PREFIX_OFFSET
,
211 16, 1, properties
, data_len
, true);
213 } else if (properties
->len
!= data_len
) {
214 pr_err("length mismatch, expected %u\n", data_len
);
215 print_hex_dump(KERN_ERR
, pr_fmt(), DUMP_PREFIX_OFFSET
,
216 16, 1, properties
, data_len
, true);
219 ret
= unmarshal_devices(properties
);
222 * Can only free the setup_data payload but not its header
223 * to avoid breaking the chain of ->next pointers.
227 memblock_free_late(pa_data
+ sizeof(*data
), data_len
);
234 fs_initcall(map_properties
);