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