Path: blob/master/drivers/firmware/efi/apple-properties.c
26428 views
// SPDX-License-Identifier: GPL-2.01/*2* apple-properties.c - EFI device properties on Macs3* Copyright (C) 2016 Lukas Wunner <[email protected]>4*5* Properties are stored either as:6* u8 arrays which can be retrieved with device_property_read_u8_array() or7* booleans which can be queried with device_property_present().8*/910#define pr_fmt(fmt) "apple-properties: " fmt1112#include <linux/memblock.h>13#include <linux/efi.h>14#include <linux/io.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>2021static bool dump_properties __initdata;2223static int __init dump_properties_enable(char *arg)24{25dump_properties = true;26return 1;27}2829__setup("dump_apple_properties", dump_properties_enable);3031struct dev_header {32u32 len;33u32 prop_count;34struct efi_dev_path path[];35/*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)38*/39};4041struct properties_header {42u32 len;43u32 version;44u32 dev_count;45struct dev_header dev_header[];46};4748static void __init unmarshal_key_value_pairs(struct dev_header *dev_header,49struct device *dev, const void *ptr,50struct property_entry entry[])51{52int i;5354for (i = 0; i < dev_header->prop_count; i++) {55int remaining = dev_header->len - (ptr - (void *)dev_header);56u32 key_len, val_len, entry_len;57const u8 *entry_data;58char *key;5960if (sizeof(key_len) > remaining)61break;6263key_len = *(typeof(key_len) *)ptr;64if (key_len + sizeof(val_len) > remaining ||65key_len < sizeof(key_len) + sizeof(efi_char16_t) ||66*(efi_char16_t *)(ptr + sizeof(key_len)) == 0) {67dev_err(dev, "invalid property name len at %#zx\n",68ptr - (void *)dev_header);69break;70}7172val_len = *(typeof(val_len) *)(ptr + key_len);73if (key_len + val_len > remaining ||74val_len < sizeof(val_len)) {75dev_err(dev, "invalid property val len at %#zx\n",76ptr - (void *)dev_header + key_len);77break;78}7980/* 4 bytes to accommodate UTF-8 code points + null byte */81key = kzalloc((key_len - sizeof(key_len)) * 4 + 1, GFP_KERNEL);82if (!key) {83dev_err(dev, "cannot allocate property name\n");84break;85}86ucs2_as_utf8(key, ptr + sizeof(key_len),87key_len - sizeof(key_len));8889entry_data = ptr + key_len + sizeof(val_len);90entry_len = val_len - sizeof(val_len);91if (entry_len)92entry[i] = PROPERTY_ENTRY_U8_ARRAY_LEN(key, entry_data,93entry_len);94else95entry[i] = PROPERTY_ENTRY_BOOL(key);9697if (dump_properties) {98dev_info(dev, "property: %s\n", key);99print_hex_dump(KERN_INFO, pr_fmt(), DUMP_PREFIX_OFFSET,10016, 1, entry_data, entry_len, true);101}102103ptr += key_len + val_len;104}105106if (i != dev_header->prop_count) {107dev_err(dev, "got %d device properties, expected %u\n", i,108dev_header->prop_count);109print_hex_dump(KERN_ERR, pr_fmt(), DUMP_PREFIX_OFFSET,11016, 1, dev_header, dev_header->len, true);111return;112}113114dev_info(dev, "assigning %d device properties\n", i);115}116117static int __init unmarshal_devices(struct properties_header *properties)118{119size_t offset = offsetof(struct properties_header, dev_header[0]);120121while (offset + sizeof(struct dev_header) < properties->len) {122struct dev_header *dev_header = (void *)properties + offset;123struct property_entry *entry = NULL;124const struct efi_dev_path *ptr;125struct device *dev;126size_t len;127int ret, i;128129if (offset + dev_header->len > properties->len ||130dev_header->len <= sizeof(*dev_header)) {131pr_err("invalid len in dev_header at %#zx\n", offset);132return -EINVAL;133}134135ptr = dev_header->path;136len = dev_header->len - sizeof(*dev_header);137138dev = efi_get_device_by_path(&ptr, &len);139if (IS_ERR(dev)) {140pr_err("device path parse error %ld at %#zx:\n",141PTR_ERR(dev), (void *)ptr - (void *)dev_header);142print_hex_dump(KERN_ERR, pr_fmt(), DUMP_PREFIX_OFFSET,14316, 1, dev_header, dev_header->len, true);144dev = NULL;145goto skip_device;146}147148entry = kcalloc(dev_header->prop_count + 1, sizeof(*entry),149GFP_KERNEL);150if (!entry) {151dev_err(dev, "cannot allocate properties\n");152goto skip_device;153}154155unmarshal_key_value_pairs(dev_header, dev, ptr, entry);156if (!entry[0].name)157goto skip_device;158159ret = device_create_managed_software_node(dev, entry, NULL);160if (ret)161dev_err(dev, "error %d assigning properties\n", ret);162163for (i = 0; entry[i].name; i++)164kfree(entry[i].name);165166skip_device:167kfree(entry);168put_device(dev);169offset += dev_header->len;170}171172return 0;173}174175static int __init map_properties(void)176{177struct properties_header *properties;178struct setup_data *data;179u32 data_len;180u64 pa_data;181int ret;182183if (!x86_apple_machine)184return 0;185186pa_data = boot_params.hdr.setup_data;187while (pa_data) {188data = memremap(pa_data, sizeof(*data), MEMREMAP_WB);189if (!data) {190pr_err("cannot map setup_data header\n");191return -ENOMEM;192}193194if (data->type != SETUP_APPLE_PROPERTIES) {195pa_data = data->next;196memunmap(data);197continue;198}199200data_len = data->len;201memunmap(data);202203data = memremap(pa_data, sizeof(*data) + data_len, MEMREMAP_WB);204if (!data) {205pr_err("cannot map setup_data payload\n");206return -ENOMEM;207}208209properties = (struct properties_header *)data->data;210if (properties->version != 1) {211pr_err("unsupported version:\n");212print_hex_dump(KERN_ERR, pr_fmt(), DUMP_PREFIX_OFFSET,21316, 1, properties, data_len, true);214ret = -ENOTSUPP;215} else if (properties->len != data_len) {216pr_err("length mismatch, expected %u\n", data_len);217print_hex_dump(KERN_ERR, pr_fmt(), DUMP_PREFIX_OFFSET,21816, 1, properties, data_len, true);219ret = -EINVAL;220} else221ret = unmarshal_devices(properties);222223/*224* Can only free the setup_data payload but not its header225* to avoid breaking the chain of ->next pointers.226*/227data->len = 0;228memunmap(data);229memblock_free_late(pa_data + sizeof(*data), data_len);230231return ret;232}233return 0;234}235236fs_initcall(map_properties);237238239