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 } 142