1 /* 2 * apple-properties.c - EFI device properties on Macs 3 * Copyright (C) 2016 Lukas Wunner <lukas@wunner.de> 4 * 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. 8 * 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. 13 * 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/>. 16 */ 17 18 #define pr_fmt(fmt) "apple-properties: " fmt 19 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> 27 28 static bool dump_properties __initdata; 29 30 static int __init dump_properties_enable(char *arg) 31 { 32 dump_properties = true; 33 return 0; 34 } 35 36 __setup("dump_apple_properties", dump_properties_enable); 37 38 struct dev_header { 39 u32 len; 40 u32 prop_count; 41 struct efi_dev_path path[0]; 42 /* 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) 45 */ 46 }; 47 48 struct properties_header { 49 u32 len; 50 u32 version; 51 u32 dev_count; 52 struct dev_header dev_header[0]; 53 }; 54 55 static u8 one __initdata = 1; 56 57 static void __init unmarshal_key_value_pairs(struct dev_header *dev_header, 58 struct device *dev, void *ptr, 59 struct property_entry entry[]) 60 { 61 int i; 62 63 for (i = 0; i < dev_header->prop_count; i++) { 64 int remaining = dev_header->len - (ptr - (void *)dev_header); 65 u32 key_len, val_len; 66 char *key; 67 68 if (sizeof(key_len) > remaining) 69 break; 70 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); 77 break; 78 } 79 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); 85 break; 86 } 87 88 /* 4 bytes to accommodate UTF-8 code points + null byte */ 89 key = kzalloc((key_len - sizeof(key_len)) * 4 + 1, GFP_KERNEL); 90 if (!key) { 91 dev_err(dev, "cannot allocate property name\n"); 92 break; 93 } 94 ucs2_as_utf8(key, ptr + sizeof(key_len), 95 key_len - sizeof(key_len)); 96 97 entry[i].name = key; 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 */ 103 entry[i].length = 1; 104 entry[i].pointer.raw_data = &one; 105 } 106 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); 112 } 113 114 ptr += key_len + val_len; 115 } 116 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); 122 return; 123 } 124 125 dev_info(dev, "assigning %d device properties\n", i); 126 } 127 128 static int __init unmarshal_devices(struct properties_header *properties) 129 { 130 size_t offset = offsetof(struct properties_header, dev_header[0]); 131 132 while (offset + sizeof(struct dev_header) < properties->len) { 133 struct dev_header *dev_header = (void *)properties + offset; 134 struct property_entry *entry = NULL; 135 struct device *dev; 136 size_t len; 137 int ret, i; 138 void *ptr; 139 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); 143 return -EINVAL; 144 } 145 146 ptr = dev_header->path; 147 len = dev_header->len - sizeof(*dev_header); 148 149 dev = efi_get_device_by_path((struct efi_dev_path **)&ptr, &len); 150 if (IS_ERR(dev)) { 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); 155 dev = NULL; 156 goto skip_device; 157 } 158 159 entry = kcalloc(dev_header->prop_count + 1, sizeof(*entry), 160 GFP_KERNEL); 161 if (!entry) { 162 dev_err(dev, "cannot allocate properties\n"); 163 goto skip_device; 164 } 165 166 unmarshal_key_value_pairs(dev_header, dev, ptr, entry); 167 if (!entry[0].name) 168 goto skip_device; 169 170 ret = device_add_properties(dev, entry); /* makes deep copy */ 171 if (ret) 172 dev_err(dev, "error %d assigning properties\n", ret); 173 174 for (i = 0; entry[i].name; i++) 175 kfree(entry[i].name); 176 177 skip_device: 178 kfree(entry); 179 put_device(dev); 180 offset += dev_header->len; 181 } 182 183 return 0; 184 } 185 186 static int __init map_properties(void) 187 { 188 struct properties_header *properties; 189 struct setup_data *data; 190 u32 data_len; 191 u64 pa_data; 192 int ret; 193 194 if (!x86_apple_machine) 195 return 0; 196 197 pa_data = boot_params.hdr.setup_data; 198 while (pa_data) { 199 data = ioremap(pa_data, sizeof(*data)); 200 if (!data) { 201 pr_err("cannot map setup_data header\n"); 202 return -ENOMEM; 203 } 204 205 if (data->type != SETUP_APPLE_PROPERTIES) { 206 pa_data = data->next; 207 iounmap(data); 208 continue; 209 } 210 211 data_len = data->len; 212 iounmap(data); 213 214 data = ioremap(pa_data, sizeof(*data) + data_len); 215 if (!data) { 216 pr_err("cannot map setup_data payload\n"); 217 return -ENOMEM; 218 } 219 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); 225 ret = -ENOTSUPP; 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); 230 ret = -EINVAL; 231 } else 232 ret = unmarshal_devices(properties); 233 234 /* 235 * Can only free the setup_data payload but not its header 236 * to avoid breaking the chain of ->next pointers. 237 */ 238 data->len = 0; 239 iounmap(data); 240 free_bootmem_late(pa_data + sizeof(*data), data_len); 241 242 return ret; 243 } 244 return 0; 245 } 246 247 fs_initcall(map_properties); 248