acpi_adxl.c (4827B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Address translation interface via ACPI DSM. 4 * Copyright (C) 2018 Intel Corporation 5 * 6 * Specification for this interface is available at: 7 * 8 * https://cdrdv2.intel.com/v1/dl/getContent/603354 9 */ 10 11#include <linux/acpi.h> 12#include <linux/adxl.h> 13 14#define ADXL_REVISION 0x1 15#define ADXL_IDX_GET_ADDR_PARAMS 0x1 16#define ADXL_IDX_FORWARD_TRANSLATE 0x2 17#define ACPI_ADXL_PATH "\\_SB.ADXL" 18 19/* 20 * The specification doesn't provide a limit on how many 21 * components are in a memory address. But since we allocate 22 * memory based on the number the BIOS tells us, we should 23 * defend against insane values. 24 */ 25#define ADXL_MAX_COMPONENTS 500 26 27#undef pr_fmt 28#define pr_fmt(fmt) "ADXL: " fmt 29 30static acpi_handle handle; 31static union acpi_object *params; 32static const guid_t adxl_guid = 33 GUID_INIT(0xAA3C050A, 0x7EA4, 0x4C1F, 34 0xAF, 0xDA, 0x12, 0x67, 0xDF, 0xD3, 0xD4, 0x8D); 35 36static int adxl_count; 37static char **adxl_component_names; 38 39static union acpi_object *adxl_dsm(int cmd, union acpi_object argv[]) 40{ 41 union acpi_object *obj, *o; 42 43 obj = acpi_evaluate_dsm_typed(handle, &adxl_guid, ADXL_REVISION, 44 cmd, argv, ACPI_TYPE_PACKAGE); 45 if (!obj) { 46 pr_info("DSM call failed for cmd=%d\n", cmd); 47 return NULL; 48 } 49 50 if (obj->package.count != 2) { 51 pr_info("Bad pkg count %d\n", obj->package.count); 52 goto err; 53 } 54 55 o = obj->package.elements; 56 if (o->type != ACPI_TYPE_INTEGER) { 57 pr_info("Bad 1st element type %d\n", o->type); 58 goto err; 59 } 60 if (o->integer.value) { 61 pr_info("Bad ret val %llu\n", o->integer.value); 62 goto err; 63 } 64 65 o = obj->package.elements + 1; 66 if (o->type != ACPI_TYPE_PACKAGE) { 67 pr_info("Bad 2nd element type %d\n", o->type); 68 goto err; 69 } 70 return obj; 71 72err: 73 ACPI_FREE(obj); 74 return NULL; 75} 76 77/** 78 * adxl_get_component_names - get list of memory component names 79 * Returns NULL terminated list of string names 80 * 81 * Give the caller a pointer to the list of memory component names 82 * e.g. { "SystemAddress", "ProcessorSocketId", "ChannelId", ... NULL } 83 * Caller should count how many strings in order to allocate a buffer 84 * for the return from adxl_decode(). 85 */ 86const char * const *adxl_get_component_names(void) 87{ 88 return (const char * const *)adxl_component_names; 89} 90EXPORT_SYMBOL_GPL(adxl_get_component_names); 91 92/** 93 * adxl_decode - ask BIOS to decode a system address to memory address 94 * @addr: the address to decode 95 * @component_values: pointer to array of values for each component 96 * Returns 0 on success, negative error code otherwise 97 * 98 * The index of each value returned in the array matches the index of 99 * each component name returned by adxl_get_component_names(). 100 * Components that are not defined for this address translation (e.g. 101 * mirror channel number for a non-mirrored address) are set to ~0ull. 102 */ 103int adxl_decode(u64 addr, u64 component_values[]) 104{ 105 union acpi_object argv4[2], *results, *r; 106 int i, cnt; 107 108 if (!adxl_component_names) 109 return -EOPNOTSUPP; 110 111 argv4[0].type = ACPI_TYPE_PACKAGE; 112 argv4[0].package.count = 1; 113 argv4[0].package.elements = &argv4[1]; 114 argv4[1].integer.type = ACPI_TYPE_INTEGER; 115 argv4[1].integer.value = addr; 116 117 results = adxl_dsm(ADXL_IDX_FORWARD_TRANSLATE, argv4); 118 if (!results) 119 return -EINVAL; 120 121 r = results->package.elements + 1; 122 cnt = r->package.count; 123 if (cnt != adxl_count) { 124 ACPI_FREE(results); 125 return -EINVAL; 126 } 127 r = r->package.elements; 128 129 for (i = 0; i < cnt; i++) 130 component_values[i] = r[i].integer.value; 131 132 ACPI_FREE(results); 133 134 return 0; 135} 136EXPORT_SYMBOL_GPL(adxl_decode); 137 138static int __init adxl_init(void) 139{ 140 char *path = ACPI_ADXL_PATH; 141 union acpi_object *p; 142 acpi_status status; 143 int i; 144 145 status = acpi_get_handle(NULL, path, &handle); 146 if (ACPI_FAILURE(status)) { 147 pr_debug("No ACPI handle for path %s\n", path); 148 return -ENODEV; 149 } 150 151 if (!acpi_has_method(handle, "_DSM")) { 152 pr_info("No DSM method\n"); 153 return -ENODEV; 154 } 155 156 if (!acpi_check_dsm(handle, &adxl_guid, ADXL_REVISION, 157 ADXL_IDX_GET_ADDR_PARAMS | 158 ADXL_IDX_FORWARD_TRANSLATE)) { 159 pr_info("DSM method does not support forward translate\n"); 160 return -ENODEV; 161 } 162 163 params = adxl_dsm(ADXL_IDX_GET_ADDR_PARAMS, NULL); 164 if (!params) { 165 pr_info("Failed to get component names\n"); 166 return -ENODEV; 167 } 168 169 p = params->package.elements + 1; 170 adxl_count = p->package.count; 171 if (adxl_count > ADXL_MAX_COMPONENTS) { 172 pr_info("Insane number of address component names %d\n", adxl_count); 173 ACPI_FREE(params); 174 return -ENODEV; 175 } 176 p = p->package.elements; 177 178 /* 179 * Allocate one extra for NULL termination. 180 */ 181 adxl_component_names = kcalloc(adxl_count + 1, sizeof(char *), GFP_KERNEL); 182 if (!adxl_component_names) { 183 ACPI_FREE(params); 184 return -ENOMEM; 185 } 186 187 for (i = 0; i < adxl_count; i++) 188 adxl_component_names[i] = p[i].string.pointer; 189 190 return 0; 191} 192subsys_initcall(adxl_init);