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