xref: /linux/drivers/acpi/x86/apple.c (revision be239684b18e1cdcafcf8c7face4a2f562c745ad)
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 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(adev->handle, &apple_prp_guid, 1, 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(adev->handle, &apple_prp_guid, 1, 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(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(valid, 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(&adev->data, &apple_prp_guid, newprops);
144 
145 out_free:
146 	ACPI_FREE(props);
147 	bitmap_free(valid);
148 }
149