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 * Properties are stored either as:
7 * u8 arrays which can be retrieved with device_property_read_u8_array() or
8 * booleans which can be queried with device_property_present().
11 #define pr_fmt(fmt) "apple-properties: " fmt
13 #include <linux/memblock.h>
14 #include <linux/efi.h>
16 #include <linux/platform_data/x86/apple.h>
17 #include <linux/property.h>
18 #include <linux/slab.h>
19 #include <linux/ucs2_string.h>
20 #include <asm/setup.h>
22 static bool dump_properties __initdata
;
24 static int __init
dump_properties_enable(char *arg
)
26 dump_properties
= true;
30 __setup("dump_apple_properties", dump_properties_enable
);
35 struct efi_dev_path path
[];
37 * followed by key/value pairs, each key and value preceded by u32 len,
38 * len includes itself, value may be empty (in which case its len is 4)
42 struct properties_header
{
46 struct dev_header dev_header
[];
49 static void __init
unmarshal_key_value_pairs(struct dev_header
*dev_header
,
50 struct device
*dev
, const void *ptr
,
51 struct property_entry entry
[])
55 for (i
= 0; i
< dev_header
->prop_count
; i
++) {
56 int remaining
= dev_header
->len
- (ptr
- (void *)dev_header
);
57 u32 key_len
, val_len
, entry_len
;
61 if (sizeof(key_len
) > remaining
)
64 key_len
= *(typeof(key_len
) *)ptr
;
65 if (key_len
+ sizeof(val_len
) > remaining
||
66 key_len
< sizeof(key_len
) + sizeof(efi_char16_t
) ||
67 *(efi_char16_t
*)(ptr
+ sizeof(key_len
)) == 0) {
68 dev_err(dev
, "invalid property name len at %#zx\n",
69 ptr
- (void *)dev_header
);
73 val_len
= *(typeof(val_len
) *)(ptr
+ key_len
);
74 if (key_len
+ val_len
> remaining
||
75 val_len
< sizeof(val_len
)) {
76 dev_err(dev
, "invalid property val len at %#zx\n",
77 ptr
- (void *)dev_header
+ key_len
);
81 /* 4 bytes to accommodate UTF-8 code points + null byte */
82 key
= kzalloc((key_len
- sizeof(key_len
)) * 4 + 1, GFP_KERNEL
);
84 dev_err(dev
, "cannot allocate property name\n");
87 ucs2_as_utf8(key
, ptr
+ sizeof(key_len
),
88 key_len
- sizeof(key_len
));
90 entry_data
= ptr
+ key_len
+ sizeof(val_len
);
91 entry_len
= val_len
- sizeof(val_len
);
93 entry
[i
] = PROPERTY_ENTRY_U8_ARRAY_LEN(key
, entry_data
,
96 entry
[i
] = PROPERTY_ENTRY_BOOL(key
);
98 if (dump_properties
) {
99 dev_info(dev
, "property: %s\n", key
);
100 print_hex_dump(KERN_INFO
, pr_fmt(), DUMP_PREFIX_OFFSET
,
101 16, 1, entry_data
, entry_len
, true);
104 ptr
+= key_len
+ val_len
;
107 if (i
!= dev_header
->prop_count
) {
108 dev_err(dev
, "got %d device properties, expected %u\n", i
,
109 dev_header
->prop_count
);
110 print_hex_dump(KERN_ERR
, pr_fmt(), DUMP_PREFIX_OFFSET
,
111 16, 1, dev_header
, dev_header
->len
, true);
115 dev_info(dev
, "assigning %d device properties\n", i
);
118 static int __init
unmarshal_devices(struct properties_header
*properties
)
120 size_t offset
= offsetof(struct properties_header
, dev_header
[0]);
122 while (offset
+ sizeof(struct dev_header
) < properties
->len
) {
123 struct dev_header
*dev_header
= (void *)properties
+ offset
;
124 struct property_entry
*entry
= NULL
;
125 const struct efi_dev_path
*ptr
;
130 if (offset
+ dev_header
->len
> properties
->len
||
131 dev_header
->len
<= sizeof(*dev_header
)) {
132 pr_err("invalid len in dev_header at %#zx\n", offset
);
136 ptr
= dev_header
->path
;
137 len
= dev_header
->len
- sizeof(*dev_header
);
139 dev
= efi_get_device_by_path(&ptr
, &len
);
141 pr_err("device path parse error %ld at %#zx:\n",
142 PTR_ERR(dev
), (void *)ptr
- (void *)dev_header
);
143 print_hex_dump(KERN_ERR
, pr_fmt(), DUMP_PREFIX_OFFSET
,
144 16, 1, dev_header
, dev_header
->len
, true);
149 entry
= kcalloc(dev_header
->prop_count
+ 1, sizeof(*entry
),
152 dev_err(dev
, "cannot allocate properties\n");
156 unmarshal_key_value_pairs(dev_header
, dev
, ptr
, entry
);
160 ret
= device_create_managed_software_node(dev
, entry
, NULL
);
162 dev_err(dev
, "error %d assigning properties\n", ret
);
164 for (i
= 0; entry
[i
].name
; i
++)
165 kfree(entry
[i
].name
);
170 offset
+= dev_header
->len
;
176 static int __init
map_properties(void)
178 struct properties_header
*properties
;
179 struct setup_data
*data
;
184 if (!x86_apple_machine
)
187 pa_data
= boot_params
.hdr
.setup_data
;
189 data
= memremap(pa_data
, sizeof(*data
), MEMREMAP_WB
);
191 pr_err("cannot map setup_data header\n");
195 if (data
->type
!= SETUP_APPLE_PROPERTIES
) {
196 pa_data
= data
->next
;
201 data_len
= data
->len
;
204 data
= memremap(pa_data
, sizeof(*data
) + data_len
, MEMREMAP_WB
);
206 pr_err("cannot map setup_data payload\n");
210 properties
= (struct properties_header
*)data
->data
;
211 if (properties
->version
!= 1) {
212 pr_err("unsupported version:\n");
213 print_hex_dump(KERN_ERR
, pr_fmt(), DUMP_PREFIX_OFFSET
,
214 16, 1, properties
, data_len
, true);
216 } else if (properties
->len
!= data_len
) {
217 pr_err("length mismatch, expected %u\n", data_len
);
218 print_hex_dump(KERN_ERR
, pr_fmt(), DUMP_PREFIX_OFFSET
,
219 16, 1, properties
, data_len
, true);
222 ret
= unmarshal_devices(properties
);
225 * Can only free the setup_data payload but not its header
226 * to avoid breaking the chain of ->next pointers.
230 memblock_free_late(pa_data
+ sizeof(*data
), data_len
);
237 fs_initcall(map_properties
);