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