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(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 | |
145 | out_free: |
146 | ACPI_FREE(props); |
147 | bitmap_free(bitmap: valid); |
148 | } |
149 | |