1*58c5475aSLukas Wunner /* 2*58c5475aSLukas Wunner * apple-properties.c - EFI device properties on Macs 3*58c5475aSLukas Wunner * Copyright (C) 2016 Lukas Wunner <lukas@wunner.de> 4*58c5475aSLukas Wunner * 5*58c5475aSLukas Wunner * This program is free software; you can redistribute it and/or modify 6*58c5475aSLukas Wunner * it under the terms of the GNU General Public License (version 2) as 7*58c5475aSLukas Wunner * published by the Free Software Foundation. 8*58c5475aSLukas Wunner * 9*58c5475aSLukas Wunner * This program is distributed in the hope that it will be useful, 10*58c5475aSLukas Wunner * but WITHOUT ANY WARRANTY; without even the implied warranty of 11*58c5475aSLukas Wunner * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12*58c5475aSLukas Wunner * GNU General Public License for more details. 13*58c5475aSLukas Wunner * 14*58c5475aSLukas Wunner * You should have received a copy of the GNU General Public License 15*58c5475aSLukas Wunner * along with this program; if not, see <http://www.gnu.org/licenses/>. 16*58c5475aSLukas Wunner */ 17*58c5475aSLukas Wunner 18*58c5475aSLukas Wunner #define pr_fmt(fmt) "apple-properties: " fmt 19*58c5475aSLukas Wunner 20*58c5475aSLukas Wunner #include <linux/bootmem.h> 21*58c5475aSLukas Wunner #include <linux/dmi.h> 22*58c5475aSLukas Wunner #include <linux/efi.h> 23*58c5475aSLukas Wunner #include <linux/property.h> 24*58c5475aSLukas Wunner #include <linux/slab.h> 25*58c5475aSLukas Wunner #include <linux/ucs2_string.h> 26*58c5475aSLukas Wunner #include <asm/setup.h> 27*58c5475aSLukas Wunner 28*58c5475aSLukas Wunner static bool dump_properties __initdata; 29*58c5475aSLukas Wunner 30*58c5475aSLukas Wunner static int __init dump_properties_enable(char *arg) 31*58c5475aSLukas Wunner { 32*58c5475aSLukas Wunner dump_properties = true; 33*58c5475aSLukas Wunner return 0; 34*58c5475aSLukas Wunner } 35*58c5475aSLukas Wunner 36*58c5475aSLukas Wunner __setup("dump_apple_properties", dump_properties_enable); 37*58c5475aSLukas Wunner 38*58c5475aSLukas Wunner struct dev_header { 39*58c5475aSLukas Wunner u32 len; 40*58c5475aSLukas Wunner u32 prop_count; 41*58c5475aSLukas Wunner struct efi_dev_path path[0]; 42*58c5475aSLukas Wunner /* 43*58c5475aSLukas Wunner * followed by key/value pairs, each key and value preceded by u32 len, 44*58c5475aSLukas Wunner * len includes itself, value may be empty (in which case its len is 4) 45*58c5475aSLukas Wunner */ 46*58c5475aSLukas Wunner }; 47*58c5475aSLukas Wunner 48*58c5475aSLukas Wunner struct properties_header { 49*58c5475aSLukas Wunner u32 len; 50*58c5475aSLukas Wunner u32 version; 51*58c5475aSLukas Wunner u32 dev_count; 52*58c5475aSLukas Wunner struct dev_header dev_header[0]; 53*58c5475aSLukas Wunner }; 54*58c5475aSLukas Wunner 55*58c5475aSLukas Wunner static u8 one __initdata = 1; 56*58c5475aSLukas Wunner 57*58c5475aSLukas Wunner static void __init unmarshal_key_value_pairs(struct dev_header *dev_header, 58*58c5475aSLukas Wunner struct device *dev, void *ptr, 59*58c5475aSLukas Wunner struct property_entry entry[]) 60*58c5475aSLukas Wunner { 61*58c5475aSLukas Wunner int i; 62*58c5475aSLukas Wunner 63*58c5475aSLukas Wunner for (i = 0; i < dev_header->prop_count; i++) { 64*58c5475aSLukas Wunner int remaining = dev_header->len - (ptr - (void *)dev_header); 65*58c5475aSLukas Wunner u32 key_len, val_len; 66*58c5475aSLukas Wunner char *key; 67*58c5475aSLukas Wunner 68*58c5475aSLukas Wunner if (sizeof(key_len) > remaining) 69*58c5475aSLukas Wunner break; 70*58c5475aSLukas Wunner 71*58c5475aSLukas Wunner key_len = *(typeof(key_len) *)ptr; 72*58c5475aSLukas Wunner if (key_len + sizeof(val_len) > remaining || 73*58c5475aSLukas Wunner key_len < sizeof(key_len) + sizeof(efi_char16_t) || 74*58c5475aSLukas Wunner *(efi_char16_t *)(ptr + sizeof(key_len)) == 0) { 75*58c5475aSLukas Wunner dev_err(dev, "invalid property name len at %#zx\n", 76*58c5475aSLukas Wunner ptr - (void *)dev_header); 77*58c5475aSLukas Wunner break; 78*58c5475aSLukas Wunner } 79*58c5475aSLukas Wunner 80*58c5475aSLukas Wunner val_len = *(typeof(val_len) *)(ptr + key_len); 81*58c5475aSLukas Wunner if (key_len + val_len > remaining || 82*58c5475aSLukas Wunner val_len < sizeof(val_len)) { 83*58c5475aSLukas Wunner dev_err(dev, "invalid property val len at %#zx\n", 84*58c5475aSLukas Wunner ptr - (void *)dev_header + key_len); 85*58c5475aSLukas Wunner break; 86*58c5475aSLukas Wunner } 87*58c5475aSLukas Wunner 88*58c5475aSLukas Wunner /* 4 bytes to accommodate UTF-8 code points + null byte */ 89*58c5475aSLukas Wunner key = kzalloc((key_len - sizeof(key_len)) * 4 + 1, GFP_KERNEL); 90*58c5475aSLukas Wunner if (!key) { 91*58c5475aSLukas Wunner dev_err(dev, "cannot allocate property name\n"); 92*58c5475aSLukas Wunner break; 93*58c5475aSLukas Wunner } 94*58c5475aSLukas Wunner ucs2_as_utf8(key, ptr + sizeof(key_len), 95*58c5475aSLukas Wunner key_len - sizeof(key_len)); 96*58c5475aSLukas Wunner 97*58c5475aSLukas Wunner entry[i].name = key; 98*58c5475aSLukas Wunner entry[i].is_array = true; 99*58c5475aSLukas Wunner entry[i].length = val_len - sizeof(val_len); 100*58c5475aSLukas Wunner entry[i].pointer.raw_data = ptr + key_len + sizeof(val_len); 101*58c5475aSLukas Wunner if (!entry[i].length) { 102*58c5475aSLukas Wunner /* driver core doesn't accept empty properties */ 103*58c5475aSLukas Wunner entry[i].length = 1; 104*58c5475aSLukas Wunner entry[i].pointer.raw_data = &one; 105*58c5475aSLukas Wunner } 106*58c5475aSLukas Wunner 107*58c5475aSLukas Wunner if (dump_properties) { 108*58c5475aSLukas Wunner dev_info(dev, "property: %s\n", entry[i].name); 109*58c5475aSLukas Wunner print_hex_dump(KERN_INFO, pr_fmt(), DUMP_PREFIX_OFFSET, 110*58c5475aSLukas Wunner 16, 1, entry[i].pointer.raw_data, 111*58c5475aSLukas Wunner entry[i].length, true); 112*58c5475aSLukas Wunner } 113*58c5475aSLukas Wunner 114*58c5475aSLukas Wunner ptr += key_len + val_len; 115*58c5475aSLukas Wunner } 116*58c5475aSLukas Wunner 117*58c5475aSLukas Wunner if (i != dev_header->prop_count) { 118*58c5475aSLukas Wunner dev_err(dev, "got %d device properties, expected %u\n", i, 119*58c5475aSLukas Wunner dev_header->prop_count); 120*58c5475aSLukas Wunner print_hex_dump(KERN_ERR, pr_fmt(), DUMP_PREFIX_OFFSET, 121*58c5475aSLukas Wunner 16, 1, dev_header, dev_header->len, true); 122*58c5475aSLukas Wunner return; 123*58c5475aSLukas Wunner } 124*58c5475aSLukas Wunner 125*58c5475aSLukas Wunner dev_info(dev, "assigning %d device properties\n", i); 126*58c5475aSLukas Wunner } 127*58c5475aSLukas Wunner 128*58c5475aSLukas Wunner static int __init unmarshal_devices(struct properties_header *properties) 129*58c5475aSLukas Wunner { 130*58c5475aSLukas Wunner size_t offset = offsetof(struct properties_header, dev_header[0]); 131*58c5475aSLukas Wunner 132*58c5475aSLukas Wunner while (offset + sizeof(struct dev_header) < properties->len) { 133*58c5475aSLukas Wunner struct dev_header *dev_header = (void *)properties + offset; 134*58c5475aSLukas Wunner struct property_entry *entry = NULL; 135*58c5475aSLukas Wunner struct device *dev; 136*58c5475aSLukas Wunner size_t len; 137*58c5475aSLukas Wunner int ret, i; 138*58c5475aSLukas Wunner void *ptr; 139*58c5475aSLukas Wunner 140*58c5475aSLukas Wunner if (offset + dev_header->len > properties->len || 141*58c5475aSLukas Wunner dev_header->len <= sizeof(*dev_header)) { 142*58c5475aSLukas Wunner pr_err("invalid len in dev_header at %#zx\n", offset); 143*58c5475aSLukas Wunner return -EINVAL; 144*58c5475aSLukas Wunner } 145*58c5475aSLukas Wunner 146*58c5475aSLukas Wunner ptr = dev_header->path; 147*58c5475aSLukas Wunner len = dev_header->len - sizeof(*dev_header); 148*58c5475aSLukas Wunner 149*58c5475aSLukas Wunner dev = efi_get_device_by_path((struct efi_dev_path **)&ptr, &len); 150*58c5475aSLukas Wunner if (IS_ERR(dev)) { 151*58c5475aSLukas Wunner pr_err("device path parse error %ld at %#zx:\n", 152*58c5475aSLukas Wunner PTR_ERR(dev), ptr - (void *)dev_header); 153*58c5475aSLukas Wunner print_hex_dump(KERN_ERR, pr_fmt(), DUMP_PREFIX_OFFSET, 154*58c5475aSLukas Wunner 16, 1, dev_header, dev_header->len, true); 155*58c5475aSLukas Wunner dev = NULL; 156*58c5475aSLukas Wunner goto skip_device; 157*58c5475aSLukas Wunner } 158*58c5475aSLukas Wunner 159*58c5475aSLukas Wunner entry = kcalloc(dev_header->prop_count + 1, sizeof(*entry), 160*58c5475aSLukas Wunner GFP_KERNEL); 161*58c5475aSLukas Wunner if (!entry) { 162*58c5475aSLukas Wunner dev_err(dev, "cannot allocate properties\n"); 163*58c5475aSLukas Wunner goto skip_device; 164*58c5475aSLukas Wunner } 165*58c5475aSLukas Wunner 166*58c5475aSLukas Wunner unmarshal_key_value_pairs(dev_header, dev, ptr, entry); 167*58c5475aSLukas Wunner if (!entry[0].name) 168*58c5475aSLukas Wunner goto skip_device; 169*58c5475aSLukas Wunner 170*58c5475aSLukas Wunner ret = device_add_properties(dev, entry); /* makes deep copy */ 171*58c5475aSLukas Wunner if (ret) 172*58c5475aSLukas Wunner dev_err(dev, "error %d assigning properties\n", ret); 173*58c5475aSLukas Wunner 174*58c5475aSLukas Wunner for (i = 0; entry[i].name; i++) 175*58c5475aSLukas Wunner kfree(entry[i].name); 176*58c5475aSLukas Wunner 177*58c5475aSLukas Wunner skip_device: 178*58c5475aSLukas Wunner kfree(entry); 179*58c5475aSLukas Wunner put_device(dev); 180*58c5475aSLukas Wunner offset += dev_header->len; 181*58c5475aSLukas Wunner } 182*58c5475aSLukas Wunner 183*58c5475aSLukas Wunner return 0; 184*58c5475aSLukas Wunner } 185*58c5475aSLukas Wunner 186*58c5475aSLukas Wunner static int __init map_properties(void) 187*58c5475aSLukas Wunner { 188*58c5475aSLukas Wunner struct properties_header *properties; 189*58c5475aSLukas Wunner struct setup_data *data; 190*58c5475aSLukas Wunner u32 data_len; 191*58c5475aSLukas Wunner u64 pa_data; 192*58c5475aSLukas Wunner int ret; 193*58c5475aSLukas Wunner 194*58c5475aSLukas Wunner if (!dmi_match(DMI_SYS_VENDOR, "Apple Inc.") && 195*58c5475aSLukas Wunner !dmi_match(DMI_SYS_VENDOR, "Apple Computer, Inc.")) 196*58c5475aSLukas Wunner return 0; 197*58c5475aSLukas Wunner 198*58c5475aSLukas Wunner pa_data = boot_params.hdr.setup_data; 199*58c5475aSLukas Wunner while (pa_data) { 200*58c5475aSLukas Wunner data = ioremap(pa_data, sizeof(*data)); 201*58c5475aSLukas Wunner if (!data) { 202*58c5475aSLukas Wunner pr_err("cannot map setup_data header\n"); 203*58c5475aSLukas Wunner return -ENOMEM; 204*58c5475aSLukas Wunner } 205*58c5475aSLukas Wunner 206*58c5475aSLukas Wunner if (data->type != SETUP_APPLE_PROPERTIES) { 207*58c5475aSLukas Wunner pa_data = data->next; 208*58c5475aSLukas Wunner iounmap(data); 209*58c5475aSLukas Wunner continue; 210*58c5475aSLukas Wunner } 211*58c5475aSLukas Wunner 212*58c5475aSLukas Wunner data_len = data->len; 213*58c5475aSLukas Wunner iounmap(data); 214*58c5475aSLukas Wunner 215*58c5475aSLukas Wunner data = ioremap(pa_data, sizeof(*data) + data_len); 216*58c5475aSLukas Wunner if (!data) { 217*58c5475aSLukas Wunner pr_err("cannot map setup_data payload\n"); 218*58c5475aSLukas Wunner return -ENOMEM; 219*58c5475aSLukas Wunner } 220*58c5475aSLukas Wunner 221*58c5475aSLukas Wunner properties = (struct properties_header *)data->data; 222*58c5475aSLukas Wunner if (properties->version != 1) { 223*58c5475aSLukas Wunner pr_err("unsupported version:\n"); 224*58c5475aSLukas Wunner print_hex_dump(KERN_ERR, pr_fmt(), DUMP_PREFIX_OFFSET, 225*58c5475aSLukas Wunner 16, 1, properties, data_len, true); 226*58c5475aSLukas Wunner ret = -ENOTSUPP; 227*58c5475aSLukas Wunner } else if (properties->len != data_len) { 228*58c5475aSLukas Wunner pr_err("length mismatch, expected %u\n", data_len); 229*58c5475aSLukas Wunner print_hex_dump(KERN_ERR, pr_fmt(), DUMP_PREFIX_OFFSET, 230*58c5475aSLukas Wunner 16, 1, properties, data_len, true); 231*58c5475aSLukas Wunner ret = -EINVAL; 232*58c5475aSLukas Wunner } else 233*58c5475aSLukas Wunner ret = unmarshal_devices(properties); 234*58c5475aSLukas Wunner 235*58c5475aSLukas Wunner /* 236*58c5475aSLukas Wunner * Can only free the setup_data payload but not its header 237*58c5475aSLukas Wunner * to avoid breaking the chain of ->next pointers. 238*58c5475aSLukas Wunner */ 239*58c5475aSLukas Wunner data->len = 0; 240*58c5475aSLukas Wunner iounmap(data); 241*58c5475aSLukas Wunner free_bootmem_late(pa_data + sizeof(*data), data_len); 242*58c5475aSLukas Wunner 243*58c5475aSLukas Wunner return ret; 244*58c5475aSLukas Wunner } 245*58c5475aSLukas Wunner return 0; 246*58c5475aSLukas Wunner } 247*58c5475aSLukas Wunner 248*58c5475aSLukas Wunner fs_initcall(map_properties); 249