linux/drivers/acpi/prmt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Author: Erik Kaneda <erik.kaneda@intel.com>
   4 * Copyright 2020 Intel Corporation
   5 *
   6 * prmt.c
   7 *
   8 * Each PRM service is an executable that is run in a restricted environment
   9 * that is invoked by writing to the PlatformRtMechanism OperationRegion from
  10 * AML bytecode.
  11 *
  12 * init_prmt initializes the Platform Runtime Mechanism (PRM) services by
  13 * processing data in the PRMT as well as registering an ACPI OperationRegion
  14 * handler for the PlatformRtMechanism subtype.
  15 *
  16 */
  17#include <linux/kernel.h>
  18#include <linux/efi.h>
  19#include <linux/acpi.h>
  20#include <linux/prmt.h>
  21#include <asm/efi.h>
  22
  23#pragma pack(1)
  24struct prm_mmio_addr_range {
  25        u64 phys_addr;
  26        u64 virt_addr;
  27        u32 length;
  28};
  29
  30struct prm_mmio_info {
  31        u64 mmio_count;
  32        struct prm_mmio_addr_range addr_ranges[];
  33};
  34
  35struct prm_buffer {
  36        u8 prm_status;
  37        u64 efi_status;
  38        u8 prm_cmd;
  39        guid_t handler_guid;
  40};
  41
  42struct prm_context_buffer {
  43        char signature[ACPI_NAMESEG_SIZE];
  44        u16 revision;
  45        u16 reserved;
  46        guid_t identifier;
  47        u64 static_data_buffer;
  48        struct prm_mmio_info *mmio_ranges;
  49};
  50#pragma pack()
  51
  52
  53static LIST_HEAD(prm_module_list);
  54
  55struct prm_handler_info {
  56        guid_t guid;
  57        u64 handler_addr;
  58        u64 static_data_buffer_addr;
  59        u64 acpi_param_buffer_addr;
  60
  61        struct list_head handler_list;
  62};
  63
  64struct prm_module_info {
  65        guid_t guid;
  66        u16 major_rev;
  67        u16 minor_rev;
  68        u16 handler_count;
  69        struct prm_mmio_info *mmio_info;
  70        bool updatable;
  71
  72        struct list_head module_list;
  73        struct prm_handler_info handlers[];
  74};
  75
  76
  77static u64 efi_pa_va_lookup(u64 pa)
  78{
  79        efi_memory_desc_t *md;
  80        u64 pa_offset = pa & ~PAGE_MASK;
  81        u64 page = pa & PAGE_MASK;
  82
  83        for_each_efi_memory_desc(md) {
  84                if (md->phys_addr < pa && pa < md->phys_addr + PAGE_SIZE * md->num_pages)
  85                        return pa_offset + md->virt_addr + page - md->phys_addr;
  86        }
  87
  88        return 0;
  89}
  90
  91
  92#define get_first_handler(a) ((struct acpi_prmt_handler_info *) ((char *) (a) + a->handler_info_offset))
  93#define get_next_handler(a) ((struct acpi_prmt_handler_info *) (sizeof(struct acpi_prmt_handler_info) + (char *) a))
  94
  95static int __init
  96acpi_parse_prmt(union acpi_subtable_headers *header, const unsigned long end)
  97{
  98        struct acpi_prmt_module_info *module_info;
  99        struct acpi_prmt_handler_info *handler_info;
 100        struct prm_handler_info *th;
 101        struct prm_module_info *tm;
 102        u64 mmio_count = 0;
 103        u64 cur_handler = 0;
 104        u32 module_info_size = 0;
 105        u64 mmio_range_size = 0;
 106        void *temp_mmio;
 107
 108        module_info = (struct acpi_prmt_module_info *) header;
 109        module_info_size = struct_size(tm, handlers, module_info->handler_info_count);
 110        tm = kmalloc(module_info_size, GFP_KERNEL);
 111
 112        guid_copy(&tm->guid, (guid_t *) module_info->module_guid);
 113        tm->major_rev = module_info->major_rev;
 114        tm->minor_rev = module_info->minor_rev;
 115        tm->handler_count = module_info->handler_info_count;
 116        tm->updatable = true;
 117
 118        if (module_info->mmio_list_pointer) {
 119                /*
 120                 * Each module is associated with a list of addr
 121                 * ranges that it can use during the service
 122                 */
 123                mmio_count = *(u64 *) memremap(module_info->mmio_list_pointer, 8, MEMREMAP_WB);
 124                mmio_range_size = struct_size(tm->mmio_info, addr_ranges, mmio_count);
 125                tm->mmio_info = kmalloc(mmio_range_size, GFP_KERNEL);
 126                temp_mmio = memremap(module_info->mmio_list_pointer, mmio_range_size, MEMREMAP_WB);
 127                memmove(tm->mmio_info, temp_mmio, mmio_range_size);
 128        } else {
 129                mmio_range_size = struct_size(tm->mmio_info, addr_ranges, mmio_count);
 130                tm->mmio_info = kmalloc(mmio_range_size, GFP_KERNEL);
 131                tm->mmio_info->mmio_count = 0;
 132        }
 133
 134        INIT_LIST_HEAD(&tm->module_list);
 135        list_add(&tm->module_list, &prm_module_list);
 136
 137        handler_info = get_first_handler(module_info);
 138        do {
 139                th = &tm->handlers[cur_handler];
 140
 141                guid_copy(&th->guid, (guid_t *)handler_info->handler_guid);
 142                th->handler_addr = efi_pa_va_lookup(handler_info->handler_address);
 143                th->static_data_buffer_addr = efi_pa_va_lookup(handler_info->static_data_buffer_address);
 144                th->acpi_param_buffer_addr = efi_pa_va_lookup(handler_info->acpi_param_buffer_address);
 145        } while (++cur_handler < tm->handler_count && (handler_info = get_next_handler(handler_info)));
 146
 147        return 0;
 148}
 149
 150#define GET_MODULE      0
 151#define GET_HANDLER     1
 152
 153static void *find_guid_info(const guid_t *guid, u8 mode)
 154{
 155        struct prm_handler_info *cur_handler;
 156        struct prm_module_info *cur_module;
 157        int i = 0;
 158
 159        list_for_each_entry(cur_module, &prm_module_list, module_list) {
 160                for (i = 0; i < cur_module->handler_count; ++i) {
 161                        cur_handler = &cur_module->handlers[i];
 162                        if (guid_equal(guid, &cur_handler->guid)) {
 163                                if (mode == GET_MODULE)
 164                                        return (void *)cur_module;
 165                                else
 166                                        return (void *)cur_handler;
 167                        }
 168                }
 169        }
 170
 171        return NULL;
 172}
 173
 174
 175static struct prm_module_info *find_prm_module(const guid_t *guid)
 176{
 177        return (struct prm_module_info *)find_guid_info(guid, GET_MODULE);
 178}
 179
 180static struct prm_handler_info *find_prm_handler(const guid_t *guid)
 181{
 182        return (struct prm_handler_info *) find_guid_info(guid, GET_HANDLER);
 183}
 184
 185/* In-coming PRM commands */
 186
 187#define PRM_CMD_RUN_SERVICE             0
 188#define PRM_CMD_START_TRANSACTION       1
 189#define PRM_CMD_END_TRANSACTION         2
 190
 191/* statuses that can be passed back to ASL */
 192
 193#define PRM_HANDLER_SUCCESS             0
 194#define PRM_HANDLER_ERROR               1
 195#define INVALID_PRM_COMMAND             2
 196#define PRM_HANDLER_GUID_NOT_FOUND      3
 197#define UPDATE_LOCK_ALREADY_HELD        4
 198#define UPDATE_UNLOCK_WITHOUT_LOCK      5
 199
 200/*
 201 * This is the PlatformRtMechanism opregion space handler.
 202 * @function: indicates the read/write. In fact as the PlatformRtMechanism
 203 * message is driven by command, only write is meaningful.
 204 *
 205 * @addr   : not used
 206 * @bits   : not used.
 207 * @value  : it is an in/out parameter. It points to the PRM message buffer.
 208 * @handler_context: not used
 209 */
 210static acpi_status acpi_platformrt_space_handler(u32 function,
 211                                                 acpi_physical_address addr,
 212                                                 u32 bits, acpi_integer *value,
 213                                                 void *handler_context,
 214                                                 void *region_context)
 215{
 216        struct prm_buffer *buffer = ACPI_CAST_PTR(struct prm_buffer, value);
 217        struct prm_handler_info *handler;
 218        struct prm_module_info *module;
 219        efi_status_t status;
 220        struct prm_context_buffer context;
 221
 222        /*
 223         * The returned acpi_status will always be AE_OK. Error values will be
 224         * saved in the first byte of the PRM message buffer to be used by ASL.
 225         */
 226        switch (buffer->prm_cmd) {
 227        case PRM_CMD_RUN_SERVICE:
 228
 229                handler = find_prm_handler(&buffer->handler_guid);
 230                module = find_prm_module(&buffer->handler_guid);
 231                if (!handler || !module)
 232                        goto invalid_guid;
 233
 234                ACPI_COPY_NAMESEG(context.signature, "PRMC");
 235                context.revision = 0x0;
 236                context.reserved = 0x0;
 237                context.identifier = handler->guid;
 238                context.static_data_buffer = handler->static_data_buffer_addr;
 239                context.mmio_ranges = module->mmio_info;
 240
 241                status = efi_call_virt_pointer(handler, handler_addr,
 242                                               handler->acpi_param_buffer_addr,
 243                                               &context);
 244                if (status == EFI_SUCCESS) {
 245                        buffer->prm_status = PRM_HANDLER_SUCCESS;
 246                } else {
 247                        buffer->prm_status = PRM_HANDLER_ERROR;
 248                        buffer->efi_status = status;
 249                }
 250                break;
 251
 252        case PRM_CMD_START_TRANSACTION:
 253
 254                module = find_prm_module(&buffer->handler_guid);
 255                if (!module)
 256                        goto invalid_guid;
 257
 258                if (module->updatable)
 259                        module->updatable = false;
 260                else
 261                        buffer->prm_status = UPDATE_LOCK_ALREADY_HELD;
 262                break;
 263
 264        case PRM_CMD_END_TRANSACTION:
 265
 266                module = find_prm_module(&buffer->handler_guid);
 267                if (!module)
 268                        goto invalid_guid;
 269
 270                if (module->updatable)
 271                        buffer->prm_status = UPDATE_UNLOCK_WITHOUT_LOCK;
 272                else
 273                        module->updatable = true;
 274                break;
 275
 276        default:
 277
 278                buffer->prm_status = INVALID_PRM_COMMAND;
 279                break;
 280        }
 281
 282        return AE_OK;
 283
 284invalid_guid:
 285        buffer->prm_status = PRM_HANDLER_GUID_NOT_FOUND;
 286        return AE_OK;
 287}
 288
 289void __init init_prmt(void)
 290{
 291        struct acpi_table_header *tbl;
 292        acpi_status status;
 293        int mc;
 294
 295        status = acpi_get_table(ACPI_SIG_PRMT, 0, &tbl);
 296        if (ACPI_FAILURE(status))
 297                return;
 298
 299        mc = acpi_table_parse_entries(ACPI_SIG_PRMT, sizeof(struct acpi_table_prmt) +
 300                                          sizeof (struct acpi_table_prmt_header),
 301                                          0, acpi_parse_prmt, 0);
 302        acpi_put_table(tbl);
 303        /*
 304         * Return immediately if PRMT table is not present or no PRM module found.
 305         */
 306        if (mc <= 0)
 307                return;
 308
 309        pr_info("PRM: found %u modules\n", mc);
 310
 311        status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,
 312                                                    ACPI_ADR_SPACE_PLATFORM_RT,
 313                                                    &acpi_platformrt_space_handler,
 314                                                    NULL, NULL);
 315        if (ACPI_FAILURE(status))
 316                pr_alert("PRM: OperationRegion handler could not be installed\n");
 317}
 318