linux/drivers/acpi/acpi_memhotplug.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2004 Intel Corporation <naveen.b.s@intel.com>
   3 *
   4 * All rights reserved.
   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 * You should have received a copy of the GNU General Public License
  18 * along with this program; if not, write to the Free Software
  19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20 *
  21 *
  22 * ACPI based HotPlug driver that supports Memory Hotplug
  23 * This driver fields notifications from firmware for memory add
  24 * and remove operations and alerts the VM of the affected memory
  25 * ranges.
  26 */
  27
  28#include <linux/kernel.h>
  29#include <linux/module.h>
  30#include <linux/init.h>
  31#include <linux/types.h>
  32#include <linux/memory_hotplug.h>
  33#include <acpi/acpi_drivers.h>
  34
  35#define ACPI_MEMORY_DEVICE_CLASS                "memory"
  36#define ACPI_MEMORY_DEVICE_HID                  "PNP0C80"
  37#define ACPI_MEMORY_DEVICE_NAME                 "Hotplug Mem Device"
  38
  39#define _COMPONENT              ACPI_MEMORY_DEVICE_COMPONENT
  40
  41ACPI_MODULE_NAME("acpi_memhotplug");
  42MODULE_AUTHOR("Naveen B S <naveen.b.s@intel.com>");
  43MODULE_DESCRIPTION("Hotplug Mem Driver");
  44MODULE_LICENSE("GPL");
  45
  46/* Memory Device States */
  47#define MEMORY_INVALID_STATE    0
  48#define MEMORY_POWER_ON_STATE   1
  49#define MEMORY_POWER_OFF_STATE  2
  50
  51static int acpi_memory_device_add(struct acpi_device *device);
  52static int acpi_memory_device_remove(struct acpi_device *device, int type);
  53static int acpi_memory_device_start(struct acpi_device *device);
  54
  55static const struct acpi_device_id memory_device_ids[] = {
  56        {ACPI_MEMORY_DEVICE_HID, 0},
  57        {"", 0},
  58};
  59MODULE_DEVICE_TABLE(acpi, memory_device_ids);
  60
  61static struct acpi_driver acpi_memory_device_driver = {
  62        .name = "acpi_memhotplug",
  63        .class = ACPI_MEMORY_DEVICE_CLASS,
  64        .ids = memory_device_ids,
  65        .ops = {
  66                .add = acpi_memory_device_add,
  67                .remove = acpi_memory_device_remove,
  68                .start = acpi_memory_device_start,
  69                },
  70};
  71
  72struct acpi_memory_info {
  73        struct list_head list;
  74        u64 start_addr;         /* Memory Range start physical addr */
  75        u64 length;             /* Memory Range length */
  76        unsigned short caching; /* memory cache attribute */
  77        unsigned short write_protect;   /* memory read/write attribute */
  78        unsigned int enabled:1;
  79};
  80
  81struct acpi_memory_device {
  82        struct acpi_device * device;
  83        unsigned int state;     /* State of the memory device */
  84        struct list_head res_list;
  85};
  86
  87static int acpi_hotmem_initialized;
  88
  89static acpi_status
  90acpi_memory_get_resource(struct acpi_resource *resource, void *context)
  91{
  92        struct acpi_memory_device *mem_device = context;
  93        struct acpi_resource_address64 address64;
  94        struct acpi_memory_info *info, *new;
  95        acpi_status status;
  96
  97        status = acpi_resource_to_address64(resource, &address64);
  98        if (ACPI_FAILURE(status) ||
  99            (address64.resource_type != ACPI_MEMORY_RANGE))
 100                return AE_OK;
 101
 102        list_for_each_entry(info, &mem_device->res_list, list) {
 103                /* Can we combine the resource range information? */
 104                if ((info->caching == address64.info.mem.caching) &&
 105                    (info->write_protect == address64.info.mem.write_protect) &&
 106                    (info->start_addr + info->length == address64.minimum)) {
 107                        info->length += address64.address_length;
 108                        return AE_OK;
 109                }
 110        }
 111
 112        new = kzalloc(sizeof(struct acpi_memory_info), GFP_KERNEL);
 113        if (!new)
 114                return AE_ERROR;
 115
 116        INIT_LIST_HEAD(&new->list);
 117        new->caching = address64.info.mem.caching;
 118        new->write_protect = address64.info.mem.write_protect;
 119        new->start_addr = address64.minimum;
 120        new->length = address64.address_length;
 121        list_add_tail(&new->list, &mem_device->res_list);
 122
 123        return AE_OK;
 124}
 125
 126static int
 127acpi_memory_get_device_resources(struct acpi_memory_device *mem_device)
 128{
 129        acpi_status status;
 130        struct acpi_memory_info *info, *n;
 131
 132
 133        if (!list_empty(&mem_device->res_list))
 134                return 0;
 135
 136        status = acpi_walk_resources(mem_device->device->handle, METHOD_NAME__CRS,
 137                                     acpi_memory_get_resource, mem_device);
 138        if (ACPI_FAILURE(status)) {
 139                list_for_each_entry_safe(info, n, &mem_device->res_list, list)
 140                        kfree(info);
 141                INIT_LIST_HEAD(&mem_device->res_list);
 142                return -EINVAL;
 143        }
 144
 145        return 0;
 146}
 147
 148static int
 149acpi_memory_get_device(acpi_handle handle,
 150                       struct acpi_memory_device **mem_device)
 151{
 152        acpi_status status;
 153        acpi_handle phandle;
 154        struct acpi_device *device = NULL;
 155        struct acpi_device *pdevice = NULL;
 156
 157
 158        if (!acpi_bus_get_device(handle, &device) && device)
 159                goto end;
 160
 161        status = acpi_get_parent(handle, &phandle);
 162        if (ACPI_FAILURE(status)) {
 163                ACPI_EXCEPTION((AE_INFO, status, "Cannot find acpi parent"));
 164                return -EINVAL;
 165        }
 166
 167        /* Get the parent device */
 168        status = acpi_bus_get_device(phandle, &pdevice);
 169        if (ACPI_FAILURE(status)) {
 170                ACPI_EXCEPTION((AE_INFO, status, "Cannot get acpi bus device"));
 171                return -EINVAL;
 172        }
 173
 174        /*
 175         * Now add the notified device.  This creates the acpi_device
 176         * and invokes .add function
 177         */
 178        status = acpi_bus_add(&device, pdevice, handle, ACPI_BUS_TYPE_DEVICE);
 179        if (ACPI_FAILURE(status)) {
 180                ACPI_EXCEPTION((AE_INFO, status, "Cannot add acpi bus"));
 181                return -EINVAL;
 182        }
 183
 184      end:
 185        *mem_device = acpi_driver_data(device);
 186        if (!(*mem_device)) {
 187                printk(KERN_ERR "\n driver data not found");
 188                return -ENODEV;
 189        }
 190
 191        return 0;
 192}
 193
 194static int acpi_memory_check_device(struct acpi_memory_device *mem_device)
 195{
 196        unsigned long long current_status;
 197
 198        /* Get device present/absent information from the _STA */
 199        if (ACPI_FAILURE(acpi_evaluate_integer(mem_device->device->handle, "_STA",
 200                                               NULL, &current_status)))
 201                return -ENODEV;
 202        /*
 203         * Check for device status. Device should be
 204         * present/enabled/functioning.
 205         */
 206        if (!((current_status & ACPI_STA_DEVICE_PRESENT)
 207              && (current_status & ACPI_STA_DEVICE_ENABLED)
 208              && (current_status & ACPI_STA_DEVICE_FUNCTIONING)))
 209                return -ENODEV;
 210
 211        return 0;
 212}
 213
 214static int acpi_memory_enable_device(struct acpi_memory_device *mem_device)
 215{
 216        int result, num_enabled = 0;
 217        struct acpi_memory_info *info;
 218        int node;
 219
 220
 221        /* Get the range from the _CRS */
 222        result = acpi_memory_get_device_resources(mem_device);
 223        if (result) {
 224                printk(KERN_ERR PREFIX "get_device_resources failed\n");
 225                mem_device->state = MEMORY_INVALID_STATE;
 226                return result;
 227        }
 228
 229        node = acpi_get_node(mem_device->device->handle);
 230        /*
 231         * Tell the VM there is more memory here...
 232         * Note: Assume that this function returns zero on success
 233         * We don't have memory-hot-add rollback function,now.
 234         * (i.e. memory-hot-remove function)
 235         */
 236        list_for_each_entry(info, &mem_device->res_list, list) {
 237                if (info->enabled) { /* just sanity check...*/
 238                        num_enabled++;
 239                        continue;
 240                }
 241
 242                if (node < 0)
 243                        node = memory_add_physaddr_to_nid(info->start_addr);
 244
 245                result = add_memory(node, info->start_addr, info->length);
 246                if (result)
 247                        continue;
 248                info->enabled = 1;
 249                num_enabled++;
 250        }
 251        if (!num_enabled) {
 252                printk(KERN_ERR PREFIX "add_memory failed\n");
 253                mem_device->state = MEMORY_INVALID_STATE;
 254                return -EINVAL;
 255        }
 256
 257        return result;
 258}
 259
 260static int acpi_memory_powerdown_device(struct acpi_memory_device *mem_device)
 261{
 262        acpi_status status;
 263        struct acpi_object_list arg_list;
 264        union acpi_object arg;
 265        unsigned long long current_status;
 266
 267
 268        /* Issue the _EJ0 command */
 269        arg_list.count = 1;
 270        arg_list.pointer = &arg;
 271        arg.type = ACPI_TYPE_INTEGER;
 272        arg.integer.value = 1;
 273        status = acpi_evaluate_object(mem_device->device->handle,
 274                                      "_EJ0", &arg_list, NULL);
 275        /* Return on _EJ0 failure */
 276        if (ACPI_FAILURE(status)) {
 277                ACPI_EXCEPTION((AE_INFO, status, "_EJ0 failed"));
 278                return -ENODEV;
 279        }
 280
 281        /* Evalute _STA to check if the device is disabled */
 282        status = acpi_evaluate_integer(mem_device->device->handle, "_STA",
 283                                       NULL, &current_status);
 284        if (ACPI_FAILURE(status))
 285                return -ENODEV;
 286
 287        /* Check for device status.  Device should be disabled */
 288        if (current_status & ACPI_STA_DEVICE_ENABLED)
 289                return -EINVAL;
 290
 291        return 0;
 292}
 293
 294static int acpi_memory_disable_device(struct acpi_memory_device *mem_device)
 295{
 296        int result;
 297        struct acpi_memory_info *info, *n;
 298
 299
 300        /*
 301         * Ask the VM to offline this memory range.
 302         * Note: Assume that this function returns zero on success
 303         */
 304        list_for_each_entry_safe(info, n, &mem_device->res_list, list) {
 305                if (info->enabled) {
 306                        result = remove_memory(info->start_addr, info->length);
 307                        if (result)
 308                                return result;
 309                }
 310                kfree(info);
 311        }
 312
 313        /* Power-off and eject the device */
 314        result = acpi_memory_powerdown_device(mem_device);
 315        if (result) {
 316                /* Set the status of the device to invalid */
 317                mem_device->state = MEMORY_INVALID_STATE;
 318                return result;
 319        }
 320
 321        mem_device->state = MEMORY_POWER_OFF_STATE;
 322        return result;
 323}
 324
 325static void acpi_memory_device_notify(acpi_handle handle, u32 event, void *data)
 326{
 327        struct acpi_memory_device *mem_device;
 328        struct acpi_device *device;
 329
 330
 331        switch (event) {
 332        case ACPI_NOTIFY_BUS_CHECK:
 333                ACPI_DEBUG_PRINT((ACPI_DB_INFO,
 334                                  "\nReceived BUS CHECK notification for device\n"));
 335                /* Fall Through */
 336        case ACPI_NOTIFY_DEVICE_CHECK:
 337                if (event == ACPI_NOTIFY_DEVICE_CHECK)
 338                        ACPI_DEBUG_PRINT((ACPI_DB_INFO,
 339                                          "\nReceived DEVICE CHECK notification for device\n"));
 340                if (acpi_memory_get_device(handle, &mem_device)) {
 341                        printk(KERN_ERR PREFIX "Cannot find driver data\n");
 342                        return;
 343                }
 344
 345                if (!acpi_memory_check_device(mem_device)) {
 346                        if (acpi_memory_enable_device(mem_device))
 347                                printk(KERN_ERR PREFIX
 348                                            "Cannot enable memory device\n");
 349                }
 350                break;
 351        case ACPI_NOTIFY_EJECT_REQUEST:
 352                ACPI_DEBUG_PRINT((ACPI_DB_INFO,
 353                                  "\nReceived EJECT REQUEST notification for device\n"));
 354
 355                if (acpi_bus_get_device(handle, &device)) {
 356                        printk(KERN_ERR PREFIX "Device doesn't exist\n");
 357                        break;
 358                }
 359                mem_device = acpi_driver_data(device);
 360                if (!mem_device) {
 361                        printk(KERN_ERR PREFIX "Driver Data is NULL\n");
 362                        break;
 363                }
 364
 365                /*
 366                 * Currently disabling memory device from kernel mode
 367                 * TBD: Can also be disabled from user mode scripts
 368                 * TBD: Can also be disabled by Callback registration
 369                 *      with generic sysfs driver
 370                 */
 371                if (acpi_memory_disable_device(mem_device))
 372                        printk(KERN_ERR PREFIX
 373                                    "Disable memory device\n");
 374                /*
 375                 * TBD: Invoke acpi_bus_remove to cleanup data structures
 376                 */
 377                break;
 378        default:
 379                ACPI_DEBUG_PRINT((ACPI_DB_INFO,
 380                                  "Unsupported event [0x%x]\n", event));
 381                break;
 382        }
 383
 384        return;
 385}
 386
 387static int acpi_memory_device_add(struct acpi_device *device)
 388{
 389        int result;
 390        struct acpi_memory_device *mem_device = NULL;
 391
 392
 393        if (!device)
 394                return -EINVAL;
 395
 396        mem_device = kzalloc(sizeof(struct acpi_memory_device), GFP_KERNEL);
 397        if (!mem_device)
 398                return -ENOMEM;
 399
 400        INIT_LIST_HEAD(&mem_device->res_list);
 401        mem_device->device = device;
 402        sprintf(acpi_device_name(device), "%s", ACPI_MEMORY_DEVICE_NAME);
 403        sprintf(acpi_device_class(device), "%s", ACPI_MEMORY_DEVICE_CLASS);
 404        device->driver_data = mem_device;
 405
 406        /* Get the range from the _CRS */
 407        result = acpi_memory_get_device_resources(mem_device);
 408        if (result) {
 409                kfree(mem_device);
 410                return result;
 411        }
 412
 413        /* Set the device state */
 414        mem_device->state = MEMORY_POWER_ON_STATE;
 415
 416        printk(KERN_DEBUG "%s \n", acpi_device_name(device));
 417
 418        return result;
 419}
 420
 421static int acpi_memory_device_remove(struct acpi_device *device, int type)
 422{
 423        struct acpi_memory_device *mem_device = NULL;
 424
 425
 426        if (!device || !acpi_driver_data(device))
 427                return -EINVAL;
 428
 429        mem_device = acpi_driver_data(device);
 430        kfree(mem_device);
 431
 432        return 0;
 433}
 434
 435static int acpi_memory_device_start (struct acpi_device *device)
 436{
 437        struct acpi_memory_device *mem_device;
 438        int result = 0;
 439
 440        /*
 441         * Early boot code has recognized memory area by EFI/E820.
 442         * If DSDT shows these memory devices on boot, hotplug is not necessary
 443         * for them. So, it just returns until completion of this driver's
 444         * start up.
 445         */
 446        if (!acpi_hotmem_initialized)
 447                return 0;
 448
 449        mem_device = acpi_driver_data(device);
 450
 451        if (!acpi_memory_check_device(mem_device)) {
 452                /* call add_memory func */
 453                result = acpi_memory_enable_device(mem_device);
 454                if (result)
 455                        printk(KERN_ERR PREFIX
 456                                "Error in acpi_memory_enable_device\n");
 457        }
 458        return result;
 459}
 460
 461/*
 462 * Helper function to check for memory device
 463 */
 464static acpi_status is_memory_device(acpi_handle handle)
 465{
 466        char *hardware_id;
 467        acpi_status status;
 468        struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
 469        struct acpi_device_info *info;
 470
 471
 472        status = acpi_get_object_info(handle, &buffer);
 473        if (ACPI_FAILURE(status))
 474                return status;
 475
 476        info = buffer.pointer;
 477        if (!(info->valid & ACPI_VALID_HID)) {
 478                kfree(buffer.pointer);
 479                return AE_ERROR;
 480        }
 481
 482        hardware_id = info->hardware_id.value;
 483        if ((hardware_id == NULL) ||
 484            (strcmp(hardware_id, ACPI_MEMORY_DEVICE_HID)))
 485                status = AE_ERROR;
 486
 487        kfree(buffer.pointer);
 488        return status;
 489}
 490
 491static acpi_status
 492acpi_memory_register_notify_handler(acpi_handle handle,
 493                                    u32 level, void *ctxt, void **retv)
 494{
 495        acpi_status status;
 496
 497
 498        status = is_memory_device(handle);
 499        if (ACPI_FAILURE(status))
 500                return AE_OK;   /* continue */
 501
 502        status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
 503                                             acpi_memory_device_notify, NULL);
 504        /* continue */
 505        return AE_OK;
 506}
 507
 508static acpi_status
 509acpi_memory_deregister_notify_handler(acpi_handle handle,
 510                                      u32 level, void *ctxt, void **retv)
 511{
 512        acpi_status status;
 513
 514
 515        status = is_memory_device(handle);
 516        if (ACPI_FAILURE(status))
 517                return AE_OK;   /* continue */
 518
 519        status = acpi_remove_notify_handler(handle,
 520                                            ACPI_SYSTEM_NOTIFY,
 521                                            acpi_memory_device_notify);
 522
 523        return AE_OK;   /* continue */
 524}
 525
 526static int __init acpi_memory_device_init(void)
 527{
 528        int result;
 529        acpi_status status;
 530
 531
 532        result = acpi_bus_register_driver(&acpi_memory_device_driver);
 533
 534        if (result < 0)
 535                return -ENODEV;
 536
 537        status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
 538                                     ACPI_UINT32_MAX,
 539                                     acpi_memory_register_notify_handler,
 540                                     NULL, NULL);
 541
 542        if (ACPI_FAILURE(status)) {
 543                ACPI_EXCEPTION((AE_INFO, status, "walk_namespace failed"));
 544                acpi_bus_unregister_driver(&acpi_memory_device_driver);
 545                return -ENODEV;
 546        }
 547
 548        acpi_hotmem_initialized = 1;
 549        return 0;
 550}
 551
 552static void __exit acpi_memory_device_exit(void)
 553{
 554        acpi_status status;
 555
 556
 557        /*
 558         * Adding this to un-install notification handlers for all the device
 559         * handles.
 560         */
 561        status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
 562                                     ACPI_UINT32_MAX,
 563                                     acpi_memory_deregister_notify_handler,
 564                                     NULL, NULL);
 565
 566        if (ACPI_FAILURE(status))
 567                ACPI_EXCEPTION((AE_INFO, status, "walk_namespace failed"));
 568
 569        acpi_bus_unregister_driver(&acpi_memory_device_driver);
 570
 571        return;
 572}
 573
 574module_init(acpi_memory_device_init);
 575module_exit(acpi_memory_device_exit);
 576