linux/drivers/xen/xen-acpi-memhotplug.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * Copyright (C) 2012 Intel Corporation
   4 *    Author: Liu Jinsong <jinsong.liu@intel.com>
   5 *    Author: Jiang Yunhong <yunhong.jiang@intel.com>
   6 */
   7
   8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
   9
  10#include <linux/kernel.h>
  11#include <linux/module.h>
  12#include <linux/init.h>
  13#include <linux/types.h>
  14#include <linux/acpi.h>
  15#include <xen/acpi.h>
  16#include <xen/interface/platform.h>
  17#include <asm/xen/hypercall.h>
  18
  19#define PREFIX "ACPI:xen_memory_hotplug:"
  20
  21struct acpi_memory_info {
  22        struct list_head list;
  23        u64 start_addr;         /* Memory Range start physical addr */
  24        u64 length;             /* Memory Range length */
  25        unsigned short caching; /* memory cache attribute */
  26        unsigned short write_protect;   /* memory read/write attribute */
  27                                /* copied from buffer getting from _CRS */
  28        unsigned int enabled:1;
  29};
  30
  31struct acpi_memory_device {
  32        struct acpi_device *device;
  33        struct list_head res_list;
  34};
  35
  36static bool acpi_hotmem_initialized __read_mostly;
  37
  38static int xen_hotadd_memory(int pxm, struct acpi_memory_info *info)
  39{
  40        int rc;
  41        struct xen_platform_op op;
  42
  43        op.cmd = XENPF_mem_hotadd;
  44        op.u.mem_add.spfn = info->start_addr >> PAGE_SHIFT;
  45        op.u.mem_add.epfn = (info->start_addr + info->length) >> PAGE_SHIFT;
  46        op.u.mem_add.pxm = pxm;
  47
  48        rc = HYPERVISOR_dom0_op(&op);
  49        if (rc)
  50                pr_err(PREFIX "Xen Hotplug Memory Add failed on "
  51                        "0x%lx -> 0x%lx, _PXM: %d, error: %d\n",
  52                        (unsigned long)info->start_addr,
  53                        (unsigned long)(info->start_addr + info->length),
  54                        pxm, rc);
  55
  56        return rc;
  57}
  58
  59static int xen_acpi_memory_enable_device(struct acpi_memory_device *mem_device)
  60{
  61        int pxm, result;
  62        int num_enabled = 0;
  63        struct acpi_memory_info *info;
  64
  65        if (!mem_device)
  66                return -EINVAL;
  67
  68        pxm = xen_acpi_get_pxm(mem_device->device->handle);
  69        if (pxm < 0)
  70                return pxm;
  71
  72        list_for_each_entry(info, &mem_device->res_list, list) {
  73                if (info->enabled) { /* just sanity check...*/
  74                        num_enabled++;
  75                        continue;
  76                }
  77
  78                if (!info->length)
  79                        continue;
  80
  81                result = xen_hotadd_memory(pxm, info);
  82                if (result)
  83                        continue;
  84                info->enabled = 1;
  85                num_enabled++;
  86        }
  87
  88        if (!num_enabled)
  89                return -ENODEV;
  90
  91        return 0;
  92}
  93
  94static acpi_status
  95acpi_memory_get_resource(struct acpi_resource *resource, void *context)
  96{
  97        struct acpi_memory_device *mem_device = context;
  98        struct acpi_resource_address64 address64;
  99        struct acpi_memory_info *info, *new;
 100        acpi_status status;
 101
 102        status = acpi_resource_to_address64(resource, &address64);
 103        if (ACPI_FAILURE(status) ||
 104            (address64.resource_type != ACPI_MEMORY_RANGE))
 105                return AE_OK;
 106
 107        list_for_each_entry(info, &mem_device->res_list, list) {
 108                if ((info->caching == address64.info.mem.caching) &&
 109                    (info->write_protect == address64.info.mem.write_protect) &&
 110                    (info->start_addr + info->length == address64.address.minimum)) {
 111                        info->length += address64.address.address_length;
 112                        return AE_OK;
 113                }
 114        }
 115
 116        new = kzalloc(sizeof(struct acpi_memory_info), GFP_KERNEL);
 117        if (!new)
 118                return AE_ERROR;
 119
 120        INIT_LIST_HEAD(&new->list);
 121        new->caching = address64.info.mem.caching;
 122        new->write_protect = address64.info.mem.write_protect;
 123        new->start_addr = address64.address.minimum;
 124        new->length = address64.address.address_length;
 125        list_add_tail(&new->list, &mem_device->res_list);
 126
 127        return AE_OK;
 128}
 129
 130static int
 131acpi_memory_get_device_resources(struct acpi_memory_device *mem_device)
 132{
 133        acpi_status status;
 134        struct acpi_memory_info *info, *n;
 135
 136        if (!list_empty(&mem_device->res_list))
 137                return 0;
 138
 139        status = acpi_walk_resources(mem_device->device->handle,
 140                METHOD_NAME__CRS, acpi_memory_get_resource, mem_device);
 141
 142        if (ACPI_FAILURE(status)) {
 143                list_for_each_entry_safe(info, n, &mem_device->res_list, list)
 144                        kfree(info);
 145                INIT_LIST_HEAD(&mem_device->res_list);
 146                return -EINVAL;
 147        }
 148
 149        return 0;
 150}
 151
 152static int acpi_memory_get_device(acpi_handle handle,
 153                                  struct acpi_memory_device **mem_device)
 154{
 155        struct acpi_device *device = NULL;
 156        int result = 0;
 157
 158        acpi_scan_lock_acquire();
 159
 160        acpi_bus_get_device(handle, &device);
 161        if (acpi_device_enumerated(device))
 162                goto end;
 163
 164        /*
 165         * Now add the notified device.  This creates the acpi_device
 166         * and invokes .add function
 167         */
 168        result = acpi_bus_scan(handle);
 169        if (result) {
 170                pr_warn(PREFIX "ACPI namespace scan failed\n");
 171                result = -EINVAL;
 172                goto out;
 173        }
 174        device = NULL;
 175        acpi_bus_get_device(handle, &device);
 176        if (!acpi_device_enumerated(device)) {
 177                pr_warn(PREFIX "Missing device object\n");
 178                result = -EINVAL;
 179                goto out;
 180        }
 181
 182end:
 183        *mem_device = acpi_driver_data(device);
 184        if (!(*mem_device)) {
 185                pr_err(PREFIX "driver data not found\n");
 186                result = -ENODEV;
 187                goto out;
 188        }
 189
 190out:
 191        acpi_scan_lock_release();
 192        return result;
 193}
 194
 195static int acpi_memory_check_device(struct acpi_memory_device *mem_device)
 196{
 197        unsigned long long current_status;
 198
 199        /* Get device present/absent information from the _STA */
 200        if (ACPI_FAILURE(acpi_evaluate_integer(mem_device->device->handle,
 201                                "_STA", NULL, &current_status)))
 202                return -ENODEV;
 203        /*
 204         * Check for device status. Device should be
 205         * present/enabled/functioning.
 206         */
 207        if (!((current_status & ACPI_STA_DEVICE_PRESENT)
 208              && (current_status & ACPI_STA_DEVICE_ENABLED)
 209              && (current_status & ACPI_STA_DEVICE_FUNCTIONING)))
 210                return -ENODEV;
 211
 212        return 0;
 213}
 214
 215static int acpi_memory_disable_device(struct acpi_memory_device *mem_device)
 216{
 217        pr_debug(PREFIX "Xen does not support memory hotremove\n");
 218
 219        return -ENOSYS;
 220}
 221
 222static void acpi_memory_device_notify(acpi_handle handle, u32 event, void *data)
 223{
 224        struct acpi_memory_device *mem_device;
 225        struct acpi_device *device;
 226        u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */
 227
 228        switch (event) {
 229        case ACPI_NOTIFY_BUS_CHECK:
 230                ACPI_DEBUG_PRINT((ACPI_DB_INFO,
 231                        "\nReceived BUS CHECK notification for device\n"));
 232                fallthrough;
 233        case ACPI_NOTIFY_DEVICE_CHECK:
 234                if (event == ACPI_NOTIFY_DEVICE_CHECK)
 235                        ACPI_DEBUG_PRINT((ACPI_DB_INFO,
 236                        "\nReceived DEVICE CHECK notification for device\n"));
 237
 238                if (acpi_memory_get_device(handle, &mem_device)) {
 239                        pr_err(PREFIX "Cannot find driver data\n");
 240                        break;
 241                }
 242
 243                ost_code = ACPI_OST_SC_SUCCESS;
 244                break;
 245
 246        case ACPI_NOTIFY_EJECT_REQUEST:
 247                ACPI_DEBUG_PRINT((ACPI_DB_INFO,
 248                        "\nReceived EJECT REQUEST notification for device\n"));
 249
 250                acpi_scan_lock_acquire();
 251                if (acpi_bus_get_device(handle, &device)) {
 252                        acpi_scan_lock_release();
 253                        pr_err(PREFIX "Device doesn't exist\n");
 254                        break;
 255                }
 256                mem_device = acpi_driver_data(device);
 257                if (!mem_device) {
 258                        acpi_scan_lock_release();
 259                        pr_err(PREFIX "Driver Data is NULL\n");
 260                        break;
 261                }
 262
 263                /*
 264                 * TBD: implement acpi_memory_disable_device and invoke
 265                 * acpi_bus_remove if Xen support hotremove in the future
 266                 */
 267                acpi_memory_disable_device(mem_device);
 268                acpi_scan_lock_release();
 269                break;
 270
 271        default:
 272                ACPI_DEBUG_PRINT((ACPI_DB_INFO,
 273                                  "Unsupported event [0x%x]\n", event));
 274                /* non-hotplug event; possibly handled by other handler */
 275                return;
 276        }
 277
 278        (void) acpi_evaluate_ost(handle, event, ost_code, NULL);
 279        return;
 280}
 281
 282static int xen_acpi_memory_device_add(struct acpi_device *device)
 283{
 284        int result;
 285        struct acpi_memory_device *mem_device = NULL;
 286
 287
 288        if (!device)
 289                return -EINVAL;
 290
 291        mem_device = kzalloc(sizeof(struct acpi_memory_device), GFP_KERNEL);
 292        if (!mem_device)
 293                return -ENOMEM;
 294
 295        INIT_LIST_HEAD(&mem_device->res_list);
 296        mem_device->device = device;
 297        sprintf(acpi_device_name(device), "%s", ACPI_MEMORY_DEVICE_NAME);
 298        sprintf(acpi_device_class(device), "%s", ACPI_MEMORY_DEVICE_CLASS);
 299        device->driver_data = mem_device;
 300
 301        /* Get the range from the _CRS */
 302        result = acpi_memory_get_device_resources(mem_device);
 303        if (result) {
 304                kfree(mem_device);
 305                return result;
 306        }
 307
 308        /*
 309         * For booting existed memory devices, early boot code has recognized
 310         * memory area by EFI/E820. If DSDT shows these memory devices on boot,
 311         * hotplug is not necessary for them.
 312         * For hot-added memory devices during runtime, it need hypercall to
 313         * Xen hypervisor to add memory.
 314         */
 315        if (!acpi_hotmem_initialized)
 316                return 0;
 317
 318        if (!acpi_memory_check_device(mem_device))
 319                result = xen_acpi_memory_enable_device(mem_device);
 320
 321        return result;
 322}
 323
 324static int xen_acpi_memory_device_remove(struct acpi_device *device)
 325{
 326        struct acpi_memory_device *mem_device = NULL;
 327
 328        if (!device || !acpi_driver_data(device))
 329                return -EINVAL;
 330
 331        mem_device = acpi_driver_data(device);
 332        kfree(mem_device);
 333
 334        return 0;
 335}
 336
 337/*
 338 * Helper function to check for memory device
 339 */
 340static acpi_status is_memory_device(acpi_handle handle)
 341{
 342        char *hardware_id;
 343        acpi_status status;
 344        struct acpi_device_info *info;
 345
 346        status = acpi_get_object_info(handle, &info);
 347        if (ACPI_FAILURE(status))
 348                return status;
 349
 350        if (!(info->valid & ACPI_VALID_HID)) {
 351                kfree(info);
 352                return AE_ERROR;
 353        }
 354
 355        hardware_id = info->hardware_id.string;
 356        if ((hardware_id == NULL) ||
 357            (strcmp(hardware_id, ACPI_MEMORY_DEVICE_HID)))
 358                status = AE_ERROR;
 359
 360        kfree(info);
 361        return status;
 362}
 363
 364static acpi_status
 365acpi_memory_register_notify_handler(acpi_handle handle,
 366                                    u32 level, void *ctxt, void **retv)
 367{
 368        acpi_status status;
 369
 370        status = is_memory_device(handle);
 371        if (ACPI_FAILURE(status))
 372                return AE_OK;   /* continue */
 373
 374        status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
 375                                             acpi_memory_device_notify, NULL);
 376        /* continue */
 377        return AE_OK;
 378}
 379
 380static acpi_status
 381acpi_memory_deregister_notify_handler(acpi_handle handle,
 382                                      u32 level, void *ctxt, void **retv)
 383{
 384        acpi_status status;
 385
 386        status = is_memory_device(handle);
 387        if (ACPI_FAILURE(status))
 388                return AE_OK;   /* continue */
 389
 390        status = acpi_remove_notify_handler(handle,
 391                                            ACPI_SYSTEM_NOTIFY,
 392                                            acpi_memory_device_notify);
 393
 394        return AE_OK;   /* continue */
 395}
 396
 397static const struct acpi_device_id memory_device_ids[] = {
 398        {ACPI_MEMORY_DEVICE_HID, 0},
 399        {"", 0},
 400};
 401MODULE_DEVICE_TABLE(acpi, memory_device_ids);
 402
 403static struct acpi_driver xen_acpi_memory_device_driver = {
 404        .name = "acpi_memhotplug",
 405        .class = ACPI_MEMORY_DEVICE_CLASS,
 406        .ids = memory_device_ids,
 407        .ops = {
 408                .add = xen_acpi_memory_device_add,
 409                .remove = xen_acpi_memory_device_remove,
 410                },
 411};
 412
 413static int __init xen_acpi_memory_device_init(void)
 414{
 415        int result;
 416        acpi_status status;
 417
 418        if (!xen_initial_domain())
 419                return -ENODEV;
 420
 421        /* unregister the stub which only used to reserve driver space */
 422        xen_stub_memory_device_exit();
 423
 424        result = acpi_bus_register_driver(&xen_acpi_memory_device_driver);
 425        if (result < 0) {
 426                xen_stub_memory_device_init();
 427                return -ENODEV;
 428        }
 429
 430        status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
 431                                     ACPI_UINT32_MAX,
 432                                     acpi_memory_register_notify_handler,
 433                                     NULL, NULL, NULL);
 434
 435        if (ACPI_FAILURE(status)) {
 436                pr_warn(PREFIX "walk_namespace failed\n");
 437                acpi_bus_unregister_driver(&xen_acpi_memory_device_driver);
 438                xen_stub_memory_device_init();
 439                return -ENODEV;
 440        }
 441
 442        acpi_hotmem_initialized = true;
 443        return 0;
 444}
 445
 446static void __exit xen_acpi_memory_device_exit(void)
 447{
 448        acpi_status status;
 449
 450        if (!xen_initial_domain())
 451                return;
 452
 453        status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
 454                                     ACPI_UINT32_MAX,
 455                                     acpi_memory_deregister_notify_handler,
 456                                     NULL, NULL, NULL);
 457        if (ACPI_FAILURE(status))
 458                pr_warn(PREFIX "walk_namespace failed\n");
 459
 460        acpi_bus_unregister_driver(&xen_acpi_memory_device_driver);
 461
 462        /*
 463         * stub reserve space again to prevent any chance of native
 464         * driver loading.
 465         */
 466        xen_stub_memory_device_init();
 467        return;
 468}
 469
 470module_init(xen_acpi_memory_device_init);
 471module_exit(xen_acpi_memory_device_exit);
 472ACPI_MODULE_NAME("xen-acpi-memhotplug");
 473MODULE_AUTHOR("Liu Jinsong <jinsong.liu@intel.com>");
 474MODULE_DESCRIPTION("Xen Hotplug Mem Driver");
 475MODULE_LICENSE("GPL");
 476