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 */
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 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 */
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(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(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(data: &adev->data, guid: &apple_prp_guid, properties: newprops);
144
145out_free:
146 ACPI_FREE(props);
147 bitmap_free(bitmap: valid);
148}
149

source code of linux/drivers/acpi/x86/apple.c