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
[];
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
[];
48 static void __init
unmarshal_key_value_pairs(struct dev_header
*dev_header
,
49 struct device
*dev
, const 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
);
56 u32 key_len
, val_len
, entry_len
;
60 if (sizeof(key_len
) > remaining
)
63 key_len
= *(typeof(key_len
) *)ptr
;
64 if (key_len
+ sizeof(val_len
) > remaining
||
65 key_len
< sizeof(key_len
) + sizeof(efi_char16_t
) ||
66 *(efi_char16_t
*)(ptr
+ sizeof(key_len
)) == 0) {
67 dev_err(dev
, "invalid property name len at %#zx\n",
68 ptr
- (void *)dev_header
);
72 val_len
= *(typeof(val_len
) *)(ptr
+ key_len
);
73 if (key_len
+ val_len
> remaining
||
74 val_len
< sizeof(val_len
)) {
75 dev_err(dev
, "invalid property val len at %#zx\n",
76 ptr
- (void *)dev_header
+ key_len
);
80 /* 4 bytes to accommodate UTF-8 code points + null byte */
81 key
= kzalloc((key_len
- sizeof(key_len
)) * 4 + 1, GFP_KERNEL
);
83 dev_err(dev
, "cannot allocate property name\n");
86 ucs2_as_utf8(key
, ptr
+ sizeof(key_len
),
87 key_len
- sizeof(key_len
));
89 entry_data
= ptr
+ key_len
+ sizeof(val_len
);
90 entry_len
= val_len
- sizeof(val_len
);
91 entry
[i
] = PROPERTY_ENTRY_U8_ARRAY_LEN(key
, entry_data
,
93 if (dump_properties
) {
94 dev_info(dev
, "property: %s\n", key
);
95 print_hex_dump(KERN_INFO
, pr_fmt(), DUMP_PREFIX_OFFSET
,
96 16, 1, entry_data
, entry_len
, true);
99 ptr
+= key_len
+ val_len
;
102 if (i
!= dev_header
->prop_count
) {
103 dev_err(dev
, "got %d device properties, expected %u\n", i
,
104 dev_header
->prop_count
);
105 print_hex_dump(KERN_ERR
, pr_fmt(), DUMP_PREFIX_OFFSET
,
106 16, 1, dev_header
, dev_header
->len
, true);
110 dev_info(dev
, "assigning %d device properties\n", i
);
113 static int __init
unmarshal_devices(struct properties_header
*properties
)
115 size_t offset
= offsetof(struct properties_header
, dev_header
[0]);
117 while (offset
+ sizeof(struct dev_header
) < properties
->len
) {
118 struct dev_header
*dev_header
= (void *)properties
+ offset
;
119 struct property_entry
*entry
= NULL
;
120 const struct efi_dev_path
*ptr
;
125 if (offset
+ dev_header
->len
> properties
->len
||
126 dev_header
->len
<= sizeof(*dev_header
)) {
127 pr_err("invalid len in dev_header at %#zx\n", offset
);
131 ptr
= dev_header
->path
;
132 len
= dev_header
->len
- sizeof(*dev_header
);
134 dev
= efi_get_device_by_path(&ptr
, &len
);
136 pr_err("device path parse error %ld at %#zx:\n",
137 PTR_ERR(dev
), (void *)ptr
- (void *)dev_header
);
138 print_hex_dump(KERN_ERR
, pr_fmt(), DUMP_PREFIX_OFFSET
,
139 16, 1, dev_header
, dev_header
->len
, true);
144 entry
= kcalloc(dev_header
->prop_count
+ 1, sizeof(*entry
),
147 dev_err(dev
, "cannot allocate properties\n");
151 unmarshal_key_value_pairs(dev_header
, dev
, ptr
, entry
);
155 ret
= device_add_properties(dev
, entry
); /* makes deep copy */
157 dev_err(dev
, "error %d assigning properties\n", ret
);
159 for (i
= 0; entry
[i
].name
; i
++)
160 kfree(entry
[i
].name
);
165 offset
+= dev_header
->len
;
171 static int __init
map_properties(void)
173 struct properties_header
*properties
;
174 struct setup_data
*data
;
179 if (!x86_apple_machine
)
182 pa_data
= boot_params
.hdr
.setup_data
;
184 data
= memremap(pa_data
, sizeof(*data
), MEMREMAP_WB
);
186 pr_err("cannot map setup_data header\n");
190 if (data
->type
!= SETUP_APPLE_PROPERTIES
) {
191 pa_data
= data
->next
;
196 data_len
= data
->len
;
199 data
= memremap(pa_data
, sizeof(*data
) + data_len
, MEMREMAP_WB
);
201 pr_err("cannot map setup_data payload\n");
205 properties
= (struct properties_header
*)data
->data
;
206 if (properties
->version
!= 1) {
207 pr_err("unsupported version:\n");
208 print_hex_dump(KERN_ERR
, pr_fmt(), DUMP_PREFIX_OFFSET
,
209 16, 1, properties
, data_len
, true);
211 } else if (properties
->len
!= data_len
) {
212 pr_err("length mismatch, expected %u\n", data_len
);
213 print_hex_dump(KERN_ERR
, pr_fmt(), DUMP_PREFIX_OFFSET
,
214 16, 1, properties
, data_len
, true);
217 ret
= unmarshal_devices(properties
);
220 * Can only free the setup_data payload but not its header
221 * to avoid breaking the chain of ->next pointers.
225 memblock_free_late(pa_data
+ sizeof(*data
), data_len
);
232 fs_initcall(map_properties
);