149 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * apple.c - Apple ACPI quirks4 * Copyright (C) 2017 Lukas Wunner <lukas@wunner.de>5 */6 7#include <linux/acpi.h>8#include <linux/bitmap.h>9#include <linux/platform_data/x86/apple.h>10#include <linux/uuid.h>11#include "../internal.h"12 13/* Apple _DSM device properties GUID */14static const guid_t apple_prp_guid =15 GUID_INIT(0xa0b5b7c6, 0x1318, 0x441c,16 0xb0, 0xc9, 0xfe, 0x69, 0x5e, 0xaf, 0x94, 0x9b);17 18/**19 * acpi_extract_apple_properties - retrieve and convert Apple _DSM properties20 * @adev: ACPI device for which to retrieve the properties21 *22 * Invoke Apple's custom _DSM once to check the protocol version and once more23 * to retrieve the properties. They are marshalled up in a single package as24 * alternating key/value elements, unlike _DSD which stores them as a package25 * of 2-element packages. Convert to _DSD format and make them available under26 * the primary fwnode.27 */28void acpi_extract_apple_properties(struct acpi_device *adev)29{30 unsigned int i, j = 0, newsize = 0, numprops, numvalid;31 union acpi_object *props, *newprops;32 unsigned long *valid = NULL;33 void *free_space;34 35 if (!x86_apple_machine)36 return;37 38 props = acpi_evaluate_dsm_typed(adev->handle, &apple_prp_guid, 1, 0,39 NULL, ACPI_TYPE_BUFFER);40 if (!props)41 return;42 43 if (!props->buffer.length)44 goto out_free;45 46 if (props->buffer.pointer[0] != 3) {47 acpi_handle_info(adev->handle, FW_INFO48 "unsupported properties version %*ph\n",49 props->buffer.length, props->buffer.pointer);50 goto out_free;51 }52 53 ACPI_FREE(props);54 props = acpi_evaluate_dsm_typed(adev->handle, &apple_prp_guid, 1, 1,55 NULL, ACPI_TYPE_PACKAGE);56 if (!props)57 return;58 59 numprops = props->package.count / 2;60 if (!numprops)61 goto out_free;62 63 valid = bitmap_zalloc(numprops, GFP_KERNEL);64 if (!valid)65 goto out_free;66 67 /* newsize = key length + value length of each tuple */68 for (i = 0; i < numprops; i++) {69 union acpi_object *key = &props->package.elements[i * 2];70 union acpi_object *val = &props->package.elements[i * 2 + 1];71 72 if ( key->type != ACPI_TYPE_STRING ||73 (val->type != ACPI_TYPE_INTEGER &&74 val->type != ACPI_TYPE_BUFFER &&75 val->type != ACPI_TYPE_STRING))76 continue; /* skip invalid properties */77 78 __set_bit(i, valid);79 newsize += key->string.length + 1;80 if ( val->type == ACPI_TYPE_BUFFER)81 newsize += val->buffer.length;82 else if (val->type == ACPI_TYPE_STRING)83 newsize += val->string.length + 1;84 }85 86 numvalid = bitmap_weight(valid, numprops);87 if (numprops > numvalid)88 acpi_handle_info(adev->handle, FW_INFO89 "skipped %u properties: wrong type\n",90 numprops - numvalid);91 if (numvalid == 0)92 goto out_free;93 94 /* newsize += top-level package + 3 objects for each key/value tuple */95 newsize += (1 + 3 * numvalid) * sizeof(union acpi_object);96 newprops = ACPI_ALLOCATE_ZEROED(newsize);97 if (!newprops)98 goto out_free;99 100 /* layout: top-level package | packages | key/value tuples | strings */101 newprops->type = ACPI_TYPE_PACKAGE;102 newprops->package.count = numvalid;103 newprops->package.elements = &newprops[1];104 free_space = &newprops[1 + 3 * numvalid];105 106 for_each_set_bit(i, valid, numprops) {107 union acpi_object *key = &props->package.elements[i * 2];108 union acpi_object *val = &props->package.elements[i * 2 + 1];109 unsigned int k = 1 + numvalid + j * 2; /* index into newprops */110 unsigned int v = k + 1;111 112 newprops[1 + j].type = ACPI_TYPE_PACKAGE;113 newprops[1 + j].package.count = 2;114 newprops[1 + j].package.elements = &newprops[k];115 116 newprops[k].type = ACPI_TYPE_STRING;117 newprops[k].string.length = key->string.length;118 newprops[k].string.pointer = free_space;119 memcpy(free_space, key->string.pointer, key->string.length);120 free_space += key->string.length + 1;121 122 newprops[v].type = val->type;123 if (val->type == ACPI_TYPE_INTEGER) {124 newprops[v].integer.value = val->integer.value;125 } else if (val->type == ACPI_TYPE_STRING) {126 newprops[v].string.length = val->string.length;127 newprops[v].string.pointer = free_space;128 memcpy(free_space, val->string.pointer,129 val->string.length);130 free_space += val->string.length + 1;131 } else {132 newprops[v].buffer.length = val->buffer.length;133 newprops[v].buffer.pointer = free_space;134 memcpy(free_space, val->buffer.pointer,135 val->buffer.length);136 free_space += val->buffer.length;137 }138 j++; /* count valid properties */139 }140 WARN_ON(free_space != (void *)newprops + newsize);141 142 adev->data.pointer = newprops;143 acpi_data_add_props(&adev->data, &apple_prp_guid, newprops);144 145out_free:146 ACPI_FREE(props);147 bitmap_free(valid);148}149