| 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 | #include "../internal.h" | 
|---|
| 12 |  | 
|---|
| 13 | /* Apple _DSM device properties GUID */ | 
|---|
| 14 | static 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 properties | 
|---|
| 20 | * @adev: ACPI device for which to retrieve the properties | 
|---|
| 21 | * | 
|---|
| 22 | * Invoke Apple's custom _DSM once to check the protocol version and once more | 
|---|
| 23 | * to retrieve the properties.  They are marshalled up in a single package as | 
|---|
| 24 | * alternating key/value elements, unlike _DSD which stores them as a package | 
|---|
| 25 | * of 2-element packages.  Convert to _DSD format and make them available under | 
|---|
| 26 | * the primary fwnode. | 
|---|
| 27 | */ | 
|---|
| 28 | void (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(handle: adev->handle, guid: &apple_prp_guid, rev: 1, func: 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_INFO | 
|---|
| 48 | "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(handle: adev->handle, guid: &apple_prp_guid, rev: 1, func: 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(nbits: 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(src: valid, nbits: numprops); | 
|---|
| 87 | if (numprops > numvalid) | 
|---|
| 88 | acpi_handle_info(adev->handle, FW_INFO | 
|---|
| 89 | "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(to: free_space, from: key->string.pointer, len: 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(to: free_space, from: val->string.pointer, | 
|---|
| 129 | len: 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(to: free_space, from: val->buffer.pointer, | 
|---|
| 135 | len: 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(data: &adev->data, guid: &apple_prp_guid, properties: newprops); | 
|---|
| 144 |  | 
|---|
| 145 | out_free: | 
|---|
| 146 | ACPI_FREE(props); | 
|---|
| 147 | bitmap_free(bitmap: valid); | 
|---|
| 148 | } | 
|---|
| 149 |  | 
|---|