139adc483d378f79711f291539f20e3797337892dLiu Jinsong/* 239adc483d378f79711f291539f20e3797337892dLiu Jinsong * Copyright (C) 2012 Intel Corporation 339adc483d378f79711f291539f20e3797337892dLiu Jinsong * Author: Liu Jinsong <jinsong.liu@intel.com> 439adc483d378f79711f291539f20e3797337892dLiu Jinsong * Author: Jiang Yunhong <yunhong.jiang@intel.com> 539adc483d378f79711f291539f20e3797337892dLiu Jinsong * 639adc483d378f79711f291539f20e3797337892dLiu Jinsong * This program is free software; you can redistribute it and/or modify 739adc483d378f79711f291539f20e3797337892dLiu Jinsong * it under the terms of the GNU General Public License as published by 839adc483d378f79711f291539f20e3797337892dLiu Jinsong * the Free Software Foundation; either version 2 of the License, or (at 939adc483d378f79711f291539f20e3797337892dLiu Jinsong * your option) any later version. 1039adc483d378f79711f291539f20e3797337892dLiu Jinsong * 1139adc483d378f79711f291539f20e3797337892dLiu Jinsong * This program is distributed in the hope that it will be useful, but 1239adc483d378f79711f291539f20e3797337892dLiu Jinsong * WITHOUT ANY WARRANTY; without even the implied warranty of 1339adc483d378f79711f291539f20e3797337892dLiu Jinsong * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or 1439adc483d378f79711f291539f20e3797337892dLiu Jinsong * NON INFRINGEMENT. See the GNU General Public License for more 1539adc483d378f79711f291539f20e3797337892dLiu Jinsong * details. 1639adc483d378f79711f291539f20e3797337892dLiu Jinsong */ 1739adc483d378f79711f291539f20e3797337892dLiu Jinsong 18283c0972d53769ee44750cad4c27e3f5fa26ec1fJoe Perches#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 19283c0972d53769ee44750cad4c27e3f5fa26ec1fJoe Perches 2039adc483d378f79711f291539f20e3797337892dLiu Jinsong#include <linux/kernel.h> 2139adc483d378f79711f291539f20e3797337892dLiu Jinsong#include <linux/module.h> 2239adc483d378f79711f291539f20e3797337892dLiu Jinsong#include <linux/init.h> 2339adc483d378f79711f291539f20e3797337892dLiu Jinsong#include <linux/types.h> 2439adc483d378f79711f291539f20e3797337892dLiu Jinsong#include <linux/cpu.h> 2539adc483d378f79711f291539f20e3797337892dLiu Jinsong#include <linux/acpi.h> 2639adc483d378f79711f291539f20e3797337892dLiu Jinsong#include <linux/uaccess.h> 2739adc483d378f79711f291539f20e3797337892dLiu Jinsong#include <acpi/processor.h> 2839adc483d378f79711f291539f20e3797337892dLiu Jinsong#include <xen/acpi.h> 2939adc483d378f79711f291539f20e3797337892dLiu Jinsong#include <xen/interface/platform.h> 3039adc483d378f79711f291539f20e3797337892dLiu Jinsong#include <asm/xen/hypercall.h> 3139adc483d378f79711f291539f20e3797337892dLiu Jinsong 3239adc483d378f79711f291539f20e3797337892dLiu Jinsong#define PREFIX "ACPI:xen_cpu_hotplug:" 3339adc483d378f79711f291539f20e3797337892dLiu Jinsong 3439adc483d378f79711f291539f20e3797337892dLiu Jinsong#define INSTALL_NOTIFY_HANDLER 0 3539adc483d378f79711f291539f20e3797337892dLiu Jinsong#define UNINSTALL_NOTIFY_HANDLER 1 3639adc483d378f79711f291539f20e3797337892dLiu Jinsong 3739adc483d378f79711f291539f20e3797337892dLiu Jinsongstatic acpi_status xen_acpi_cpu_hotadd(struct acpi_processor *pr); 3839adc483d378f79711f291539f20e3797337892dLiu Jinsong 3939adc483d378f79711f291539f20e3797337892dLiu Jinsong/* -------------------------------------------------------------------------- 4039adc483d378f79711f291539f20e3797337892dLiu Jinsong Driver Interface 4139adc483d378f79711f291539f20e3797337892dLiu Jinsong-------------------------------------------------------------------------- */ 4239adc483d378f79711f291539f20e3797337892dLiu Jinsong 4339adc483d378f79711f291539f20e3797337892dLiu Jinsongstatic int xen_acpi_processor_enable(struct acpi_device *device) 4439adc483d378f79711f291539f20e3797337892dLiu Jinsong{ 4539adc483d378f79711f291539f20e3797337892dLiu Jinsong acpi_status status = 0; 4639adc483d378f79711f291539f20e3797337892dLiu Jinsong unsigned long long value; 4739adc483d378f79711f291539f20e3797337892dLiu Jinsong union acpi_object object = { 0 }; 4839adc483d378f79711f291539f20e3797337892dLiu Jinsong struct acpi_buffer buffer = { sizeof(union acpi_object), &object }; 4939adc483d378f79711f291539f20e3797337892dLiu Jinsong struct acpi_processor *pr; 5039adc483d378f79711f291539f20e3797337892dLiu Jinsong 5139adc483d378f79711f291539f20e3797337892dLiu Jinsong pr = acpi_driver_data(device); 5239adc483d378f79711f291539f20e3797337892dLiu Jinsong if (!pr) { 5339adc483d378f79711f291539f20e3797337892dLiu Jinsong pr_err(PREFIX "Cannot find driver data\n"); 5439adc483d378f79711f291539f20e3797337892dLiu Jinsong return -EINVAL; 5539adc483d378f79711f291539f20e3797337892dLiu Jinsong } 5639adc483d378f79711f291539f20e3797337892dLiu Jinsong 5739adc483d378f79711f291539f20e3797337892dLiu Jinsong if (!strcmp(acpi_device_hid(device), ACPI_PROCESSOR_OBJECT_HID)) { 5839adc483d378f79711f291539f20e3797337892dLiu Jinsong /* Declared with "Processor" statement; match ProcessorID */ 5939adc483d378f79711f291539f20e3797337892dLiu Jinsong status = acpi_evaluate_object(pr->handle, NULL, NULL, &buffer); 6039adc483d378f79711f291539f20e3797337892dLiu Jinsong if (ACPI_FAILURE(status)) { 6139adc483d378f79711f291539f20e3797337892dLiu Jinsong pr_err(PREFIX "Evaluating processor object\n"); 6239adc483d378f79711f291539f20e3797337892dLiu Jinsong return -ENODEV; 6339adc483d378f79711f291539f20e3797337892dLiu Jinsong } 6439adc483d378f79711f291539f20e3797337892dLiu Jinsong 6539adc483d378f79711f291539f20e3797337892dLiu Jinsong pr->acpi_id = object.processor.proc_id; 6639adc483d378f79711f291539f20e3797337892dLiu Jinsong } else { 6739adc483d378f79711f291539f20e3797337892dLiu Jinsong /* Declared with "Device" statement; match _UID */ 6839adc483d378f79711f291539f20e3797337892dLiu Jinsong status = acpi_evaluate_integer(pr->handle, METHOD_NAME__UID, 6939adc483d378f79711f291539f20e3797337892dLiu Jinsong NULL, &value); 7039adc483d378f79711f291539f20e3797337892dLiu Jinsong if (ACPI_FAILURE(status)) { 7139adc483d378f79711f291539f20e3797337892dLiu Jinsong pr_err(PREFIX "Evaluating processor _UID\n"); 7239adc483d378f79711f291539f20e3797337892dLiu Jinsong return -ENODEV; 7339adc483d378f79711f291539f20e3797337892dLiu Jinsong } 7439adc483d378f79711f291539f20e3797337892dLiu Jinsong 7539adc483d378f79711f291539f20e3797337892dLiu Jinsong pr->acpi_id = value; 7639adc483d378f79711f291539f20e3797337892dLiu Jinsong } 7739adc483d378f79711f291539f20e3797337892dLiu Jinsong 7839adc483d378f79711f291539f20e3797337892dLiu Jinsong pr->id = xen_pcpu_id(pr->acpi_id); 7939adc483d378f79711f291539f20e3797337892dLiu Jinsong 8039adc483d378f79711f291539f20e3797337892dLiu Jinsong if ((int)pr->id < 0) 8139adc483d378f79711f291539f20e3797337892dLiu Jinsong /* This cpu is not presented at hypervisor, try to hotadd it */ 8239adc483d378f79711f291539f20e3797337892dLiu Jinsong if (ACPI_FAILURE(xen_acpi_cpu_hotadd(pr))) { 8339adc483d378f79711f291539f20e3797337892dLiu Jinsong pr_err(PREFIX "Hotadd CPU (acpi_id = %d) failed.\n", 8439adc483d378f79711f291539f20e3797337892dLiu Jinsong pr->acpi_id); 8539adc483d378f79711f291539f20e3797337892dLiu Jinsong return -ENODEV; 8639adc483d378f79711f291539f20e3797337892dLiu Jinsong } 8739adc483d378f79711f291539f20e3797337892dLiu Jinsong 8839adc483d378f79711f291539f20e3797337892dLiu Jinsong return 0; 8939adc483d378f79711f291539f20e3797337892dLiu Jinsong} 9039adc483d378f79711f291539f20e3797337892dLiu Jinsong 91a83048ebd449a441fdbd3fa854e6b1a71552cc99Paul Gortmakerstatic int xen_acpi_processor_add(struct acpi_device *device) 9239adc483d378f79711f291539f20e3797337892dLiu Jinsong{ 9339adc483d378f79711f291539f20e3797337892dLiu Jinsong int ret; 9439adc483d378f79711f291539f20e3797337892dLiu Jinsong struct acpi_processor *pr; 9539adc483d378f79711f291539f20e3797337892dLiu Jinsong 9639adc483d378f79711f291539f20e3797337892dLiu Jinsong if (!device) 9739adc483d378f79711f291539f20e3797337892dLiu Jinsong return -EINVAL; 9839adc483d378f79711f291539f20e3797337892dLiu Jinsong 9939adc483d378f79711f291539f20e3797337892dLiu Jinsong pr = kzalloc(sizeof(struct acpi_processor), GFP_KERNEL); 10039adc483d378f79711f291539f20e3797337892dLiu Jinsong if (!pr) 10139adc483d378f79711f291539f20e3797337892dLiu Jinsong return -ENOMEM; 10239adc483d378f79711f291539f20e3797337892dLiu Jinsong 10339adc483d378f79711f291539f20e3797337892dLiu Jinsong pr->handle = device->handle; 10439adc483d378f79711f291539f20e3797337892dLiu Jinsong strcpy(acpi_device_name(device), ACPI_PROCESSOR_DEVICE_NAME); 10539adc483d378f79711f291539f20e3797337892dLiu Jinsong strcpy(acpi_device_class(device), ACPI_PROCESSOR_CLASS); 10639adc483d378f79711f291539f20e3797337892dLiu Jinsong device->driver_data = pr; 10739adc483d378f79711f291539f20e3797337892dLiu Jinsong 10839adc483d378f79711f291539f20e3797337892dLiu Jinsong ret = xen_acpi_processor_enable(device); 10939adc483d378f79711f291539f20e3797337892dLiu Jinsong if (ret) 11039adc483d378f79711f291539f20e3797337892dLiu Jinsong pr_err(PREFIX "Error when enabling Xen processor\n"); 11139adc483d378f79711f291539f20e3797337892dLiu Jinsong 11239adc483d378f79711f291539f20e3797337892dLiu Jinsong return ret; 11339adc483d378f79711f291539f20e3797337892dLiu Jinsong} 11439adc483d378f79711f291539f20e3797337892dLiu Jinsong 11577be36de8b07027a70fbc8f02703ccd76cd16d09Linus Torvaldsstatic int xen_acpi_processor_remove(struct acpi_device *device) 11639adc483d378f79711f291539f20e3797337892dLiu Jinsong{ 11739adc483d378f79711f291539f20e3797337892dLiu Jinsong struct acpi_processor *pr; 11839adc483d378f79711f291539f20e3797337892dLiu Jinsong 11939adc483d378f79711f291539f20e3797337892dLiu Jinsong if (!device) 12039adc483d378f79711f291539f20e3797337892dLiu Jinsong return -EINVAL; 12139adc483d378f79711f291539f20e3797337892dLiu Jinsong 12239adc483d378f79711f291539f20e3797337892dLiu Jinsong pr = acpi_driver_data(device); 12339adc483d378f79711f291539f20e3797337892dLiu Jinsong if (!pr) 12439adc483d378f79711f291539f20e3797337892dLiu Jinsong return -EINVAL; 12539adc483d378f79711f291539f20e3797337892dLiu Jinsong 12639adc483d378f79711f291539f20e3797337892dLiu Jinsong kfree(pr); 12739adc483d378f79711f291539f20e3797337892dLiu Jinsong return 0; 12839adc483d378f79711f291539f20e3797337892dLiu Jinsong} 12939adc483d378f79711f291539f20e3797337892dLiu Jinsong 13039adc483d378f79711f291539f20e3797337892dLiu Jinsong/*-------------------------------------------------------------- 13139adc483d378f79711f291539f20e3797337892dLiu Jinsong Acpi processor hotplug support 13239adc483d378f79711f291539f20e3797337892dLiu Jinsong--------------------------------------------------------------*/ 13339adc483d378f79711f291539f20e3797337892dLiu Jinsong 13439adc483d378f79711f291539f20e3797337892dLiu Jinsongstatic int is_processor_present(acpi_handle handle) 13539adc483d378f79711f291539f20e3797337892dLiu Jinsong{ 13639adc483d378f79711f291539f20e3797337892dLiu Jinsong acpi_status status; 13739adc483d378f79711f291539f20e3797337892dLiu Jinsong unsigned long long sta = 0; 13839adc483d378f79711f291539f20e3797337892dLiu Jinsong 13939adc483d378f79711f291539f20e3797337892dLiu Jinsong 14039adc483d378f79711f291539f20e3797337892dLiu Jinsong status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); 14139adc483d378f79711f291539f20e3797337892dLiu Jinsong 14239adc483d378f79711f291539f20e3797337892dLiu Jinsong if (ACPI_SUCCESS(status) && (sta & ACPI_STA_DEVICE_PRESENT)) 14339adc483d378f79711f291539f20e3797337892dLiu Jinsong return 1; 14439adc483d378f79711f291539f20e3797337892dLiu Jinsong 14539adc483d378f79711f291539f20e3797337892dLiu Jinsong /* 14639adc483d378f79711f291539f20e3797337892dLiu Jinsong * _STA is mandatory for a processor that supports hot plug 14739adc483d378f79711f291539f20e3797337892dLiu Jinsong */ 14839adc483d378f79711f291539f20e3797337892dLiu Jinsong if (status == AE_NOT_FOUND) 14939adc483d378f79711f291539f20e3797337892dLiu Jinsong pr_info(PREFIX "Processor does not support hot plug\n"); 15039adc483d378f79711f291539f20e3797337892dLiu Jinsong else 15139adc483d378f79711f291539f20e3797337892dLiu Jinsong pr_info(PREFIX "Processor Device is not present"); 15239adc483d378f79711f291539f20e3797337892dLiu Jinsong return 0; 15339adc483d378f79711f291539f20e3797337892dLiu Jinsong} 15439adc483d378f79711f291539f20e3797337892dLiu Jinsong 15539adc483d378f79711f291539f20e3797337892dLiu Jinsongstatic int xen_apic_id(acpi_handle handle) 15639adc483d378f79711f291539f20e3797337892dLiu Jinsong{ 15739adc483d378f79711f291539f20e3797337892dLiu Jinsong struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 15839adc483d378f79711f291539f20e3797337892dLiu Jinsong union acpi_object *obj; 15939adc483d378f79711f291539f20e3797337892dLiu Jinsong struct acpi_madt_local_apic *lapic; 16039adc483d378f79711f291539f20e3797337892dLiu Jinsong int apic_id; 16139adc483d378f79711f291539f20e3797337892dLiu Jinsong 16239adc483d378f79711f291539f20e3797337892dLiu Jinsong if (ACPI_FAILURE(acpi_evaluate_object(handle, "_MAT", NULL, &buffer))) 16339adc483d378f79711f291539f20e3797337892dLiu Jinsong return -EINVAL; 16439adc483d378f79711f291539f20e3797337892dLiu Jinsong 16539adc483d378f79711f291539f20e3797337892dLiu Jinsong if (!buffer.length || !buffer.pointer) 16639adc483d378f79711f291539f20e3797337892dLiu Jinsong return -EINVAL; 16739adc483d378f79711f291539f20e3797337892dLiu Jinsong 16839adc483d378f79711f291539f20e3797337892dLiu Jinsong obj = buffer.pointer; 16939adc483d378f79711f291539f20e3797337892dLiu Jinsong if (obj->type != ACPI_TYPE_BUFFER || 17039adc483d378f79711f291539f20e3797337892dLiu Jinsong obj->buffer.length < sizeof(*lapic)) { 17139adc483d378f79711f291539f20e3797337892dLiu Jinsong kfree(buffer.pointer); 17239adc483d378f79711f291539f20e3797337892dLiu Jinsong return -EINVAL; 17339adc483d378f79711f291539f20e3797337892dLiu Jinsong } 17439adc483d378f79711f291539f20e3797337892dLiu Jinsong 17539adc483d378f79711f291539f20e3797337892dLiu Jinsong lapic = (struct acpi_madt_local_apic *)obj->buffer.pointer; 17639adc483d378f79711f291539f20e3797337892dLiu Jinsong 17739adc483d378f79711f291539f20e3797337892dLiu Jinsong if (lapic->header.type != ACPI_MADT_TYPE_LOCAL_APIC || 17839adc483d378f79711f291539f20e3797337892dLiu Jinsong !(lapic->lapic_flags & ACPI_MADT_ENABLED)) { 17939adc483d378f79711f291539f20e3797337892dLiu Jinsong kfree(buffer.pointer); 18039adc483d378f79711f291539f20e3797337892dLiu Jinsong return -EINVAL; 18139adc483d378f79711f291539f20e3797337892dLiu Jinsong } 18239adc483d378f79711f291539f20e3797337892dLiu Jinsong 18339adc483d378f79711f291539f20e3797337892dLiu Jinsong apic_id = (uint32_t)lapic->id; 18439adc483d378f79711f291539f20e3797337892dLiu Jinsong kfree(buffer.pointer); 18539adc483d378f79711f291539f20e3797337892dLiu Jinsong buffer.length = ACPI_ALLOCATE_BUFFER; 18639adc483d378f79711f291539f20e3797337892dLiu Jinsong buffer.pointer = NULL; 18739adc483d378f79711f291539f20e3797337892dLiu Jinsong 18839adc483d378f79711f291539f20e3797337892dLiu Jinsong return apic_id; 18939adc483d378f79711f291539f20e3797337892dLiu Jinsong} 19039adc483d378f79711f291539f20e3797337892dLiu Jinsong 19139adc483d378f79711f291539f20e3797337892dLiu Jinsongstatic int xen_hotadd_cpu(struct acpi_processor *pr) 19239adc483d378f79711f291539f20e3797337892dLiu Jinsong{ 19339adc483d378f79711f291539f20e3797337892dLiu Jinsong int cpu_id, apic_id, pxm; 19439adc483d378f79711f291539f20e3797337892dLiu Jinsong struct xen_platform_op op; 19539adc483d378f79711f291539f20e3797337892dLiu Jinsong 19639adc483d378f79711f291539f20e3797337892dLiu Jinsong apic_id = xen_apic_id(pr->handle); 19739adc483d378f79711f291539f20e3797337892dLiu Jinsong if (apic_id < 0) { 19839adc483d378f79711f291539f20e3797337892dLiu Jinsong pr_err(PREFIX "Failed to get apic_id for acpi_id %d\n", 19939adc483d378f79711f291539f20e3797337892dLiu Jinsong pr->acpi_id); 20039adc483d378f79711f291539f20e3797337892dLiu Jinsong return -ENODEV; 20139adc483d378f79711f291539f20e3797337892dLiu Jinsong } 20239adc483d378f79711f291539f20e3797337892dLiu Jinsong 20339adc483d378f79711f291539f20e3797337892dLiu Jinsong pxm = xen_acpi_get_pxm(pr->handle); 20439adc483d378f79711f291539f20e3797337892dLiu Jinsong if (pxm < 0) { 20539adc483d378f79711f291539f20e3797337892dLiu Jinsong pr_err(PREFIX "Failed to get _PXM for acpi_id %d\n", 20639adc483d378f79711f291539f20e3797337892dLiu Jinsong pr->acpi_id); 20739adc483d378f79711f291539f20e3797337892dLiu Jinsong return pxm; 20839adc483d378f79711f291539f20e3797337892dLiu Jinsong } 20939adc483d378f79711f291539f20e3797337892dLiu Jinsong 21039adc483d378f79711f291539f20e3797337892dLiu Jinsong op.cmd = XENPF_cpu_hotadd; 21139adc483d378f79711f291539f20e3797337892dLiu Jinsong op.u.cpu_add.apic_id = apic_id; 21239adc483d378f79711f291539f20e3797337892dLiu Jinsong op.u.cpu_add.acpi_id = pr->acpi_id; 21339adc483d378f79711f291539f20e3797337892dLiu Jinsong op.u.cpu_add.pxm = pxm; 21439adc483d378f79711f291539f20e3797337892dLiu Jinsong 21539adc483d378f79711f291539f20e3797337892dLiu Jinsong cpu_id = HYPERVISOR_dom0_op(&op); 21639adc483d378f79711f291539f20e3797337892dLiu Jinsong if (cpu_id < 0) 21739adc483d378f79711f291539f20e3797337892dLiu Jinsong pr_err(PREFIX "Failed to hotadd CPU for acpi_id %d\n", 21839adc483d378f79711f291539f20e3797337892dLiu Jinsong pr->acpi_id); 21939adc483d378f79711f291539f20e3797337892dLiu Jinsong 22039adc483d378f79711f291539f20e3797337892dLiu Jinsong return cpu_id; 22139adc483d378f79711f291539f20e3797337892dLiu Jinsong} 22239adc483d378f79711f291539f20e3797337892dLiu Jinsong 22339adc483d378f79711f291539f20e3797337892dLiu Jinsongstatic acpi_status xen_acpi_cpu_hotadd(struct acpi_processor *pr) 22439adc483d378f79711f291539f20e3797337892dLiu Jinsong{ 22539adc483d378f79711f291539f20e3797337892dLiu Jinsong if (!is_processor_present(pr->handle)) 22639adc483d378f79711f291539f20e3797337892dLiu Jinsong return AE_ERROR; 22739adc483d378f79711f291539f20e3797337892dLiu Jinsong 22839adc483d378f79711f291539f20e3797337892dLiu Jinsong pr->id = xen_hotadd_cpu(pr); 22939adc483d378f79711f291539f20e3797337892dLiu Jinsong if ((int)pr->id < 0) 23039adc483d378f79711f291539f20e3797337892dLiu Jinsong return AE_ERROR; 23139adc483d378f79711f291539f20e3797337892dLiu Jinsong 23239adc483d378f79711f291539f20e3797337892dLiu Jinsong /* 23339adc483d378f79711f291539f20e3797337892dLiu Jinsong * Sync with Xen hypervisor, providing new /sys/.../xen_cpuX 23439adc483d378f79711f291539f20e3797337892dLiu Jinsong * interface after cpu hotadded. 23539adc483d378f79711f291539f20e3797337892dLiu Jinsong */ 23639adc483d378f79711f291539f20e3797337892dLiu Jinsong xen_pcpu_hotplug_sync(); 23739adc483d378f79711f291539f20e3797337892dLiu Jinsong 23839adc483d378f79711f291539f20e3797337892dLiu Jinsong return AE_OK; 23939adc483d378f79711f291539f20e3797337892dLiu Jinsong} 24039adc483d378f79711f291539f20e3797337892dLiu Jinsong 24139adc483d378f79711f291539f20e3797337892dLiu Jinsongstatic int acpi_processor_device_remove(struct acpi_device *device) 24239adc483d378f79711f291539f20e3797337892dLiu Jinsong{ 24339adc483d378f79711f291539f20e3797337892dLiu Jinsong pr_debug(PREFIX "Xen does not support CPU hotremove\n"); 24439adc483d378f79711f291539f20e3797337892dLiu Jinsong 24539adc483d378f79711f291539f20e3797337892dLiu Jinsong return -ENOSYS; 24639adc483d378f79711f291539f20e3797337892dLiu Jinsong} 24739adc483d378f79711f291539f20e3797337892dLiu Jinsong 24839adc483d378f79711f291539f20e3797337892dLiu Jinsongstatic void acpi_processor_hotplug_notify(acpi_handle handle, 24939adc483d378f79711f291539f20e3797337892dLiu Jinsong u32 event, void *data) 25039adc483d378f79711f291539f20e3797337892dLiu Jinsong{ 25139adc483d378f79711f291539f20e3797337892dLiu Jinsong struct acpi_processor *pr; 25239adc483d378f79711f291539f20e3797337892dLiu Jinsong struct acpi_device *device = NULL; 25339adc483d378f79711f291539f20e3797337892dLiu Jinsong u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */ 25439adc483d378f79711f291539f20e3797337892dLiu Jinsong int result; 25539adc483d378f79711f291539f20e3797337892dLiu Jinsong 2561b37d6ea2cdc61102f4643d8cd654ec15212b927Liu Jinsong acpi_scan_lock_acquire(); 2571b37d6ea2cdc61102f4643d8cd654ec15212b927Liu Jinsong 25839adc483d378f79711f291539f20e3797337892dLiu Jinsong switch (event) { 25939adc483d378f79711f291539f20e3797337892dLiu Jinsong case ACPI_NOTIFY_BUS_CHECK: 26039adc483d378f79711f291539f20e3797337892dLiu Jinsong case ACPI_NOTIFY_DEVICE_CHECK: 26139adc483d378f79711f291539f20e3797337892dLiu Jinsong ACPI_DEBUG_PRINT((ACPI_DB_INFO, 26239adc483d378f79711f291539f20e3797337892dLiu Jinsong "Processor driver received %s event\n", 26339adc483d378f79711f291539f20e3797337892dLiu Jinsong (event == ACPI_NOTIFY_BUS_CHECK) ? 26439adc483d378f79711f291539f20e3797337892dLiu Jinsong "ACPI_NOTIFY_BUS_CHECK" : "ACPI_NOTIFY_DEVICE_CHECK")); 26539adc483d378f79711f291539f20e3797337892dLiu Jinsong 26639adc483d378f79711f291539f20e3797337892dLiu Jinsong if (!is_processor_present(handle)) 26739adc483d378f79711f291539f20e3797337892dLiu Jinsong break; 26839adc483d378f79711f291539f20e3797337892dLiu Jinsong 269202317a573b20d77a9abb7c16a3fd5b40cef3d9dRafael J. Wysocki acpi_bus_get_device(handle, &device); 270202317a573b20d77a9abb7c16a3fd5b40cef3d9dRafael J. Wysocki if (acpi_device_enumerated(device)) 27139adc483d378f79711f291539f20e3797337892dLiu Jinsong break; 27239adc483d378f79711f291539f20e3797337892dLiu Jinsong 2731b37d6ea2cdc61102f4643d8cd654ec15212b927Liu Jinsong result = acpi_bus_scan(handle); 27439adc483d378f79711f291539f20e3797337892dLiu Jinsong if (result) { 27539adc483d378f79711f291539f20e3797337892dLiu Jinsong pr_err(PREFIX "Unable to add the device\n"); 27639adc483d378f79711f291539f20e3797337892dLiu Jinsong break; 27739adc483d378f79711f291539f20e3797337892dLiu Jinsong } 278202317a573b20d77a9abb7c16a3fd5b40cef3d9dRafael J. Wysocki device = NULL; 279202317a573b20d77a9abb7c16a3fd5b40cef3d9dRafael J. Wysocki acpi_bus_get_device(handle, &device); 280202317a573b20d77a9abb7c16a3fd5b40cef3d9dRafael J. Wysocki if (!acpi_device_enumerated(device)) { 2811b37d6ea2cdc61102f4643d8cd654ec15212b927Liu Jinsong pr_err(PREFIX "Missing device object\n"); 2821b37d6ea2cdc61102f4643d8cd654ec15212b927Liu Jinsong break; 2831b37d6ea2cdc61102f4643d8cd654ec15212b927Liu Jinsong } 28439adc483d378f79711f291539f20e3797337892dLiu Jinsong ost_code = ACPI_OST_SC_SUCCESS; 28539adc483d378f79711f291539f20e3797337892dLiu Jinsong break; 28639adc483d378f79711f291539f20e3797337892dLiu Jinsong 28739adc483d378f79711f291539f20e3797337892dLiu Jinsong case ACPI_NOTIFY_EJECT_REQUEST: 28839adc483d378f79711f291539f20e3797337892dLiu Jinsong ACPI_DEBUG_PRINT((ACPI_DB_INFO, 28939adc483d378f79711f291539f20e3797337892dLiu Jinsong "received ACPI_NOTIFY_EJECT_REQUEST\n")); 29039adc483d378f79711f291539f20e3797337892dLiu Jinsong 29139adc483d378f79711f291539f20e3797337892dLiu Jinsong if (acpi_bus_get_device(handle, &device)) { 29239adc483d378f79711f291539f20e3797337892dLiu Jinsong pr_err(PREFIX "Device don't exist, dropping EJECT\n"); 29339adc483d378f79711f291539f20e3797337892dLiu Jinsong break; 29439adc483d378f79711f291539f20e3797337892dLiu Jinsong } 29539adc483d378f79711f291539f20e3797337892dLiu Jinsong pr = acpi_driver_data(device); 29639adc483d378f79711f291539f20e3797337892dLiu Jinsong if (!pr) { 29739adc483d378f79711f291539f20e3797337892dLiu Jinsong pr_err(PREFIX "Driver data is NULL, dropping EJECT\n"); 29839adc483d378f79711f291539f20e3797337892dLiu Jinsong break; 29939adc483d378f79711f291539f20e3797337892dLiu Jinsong } 30039adc483d378f79711f291539f20e3797337892dLiu Jinsong 30139adc483d378f79711f291539f20e3797337892dLiu Jinsong /* 30239adc483d378f79711f291539f20e3797337892dLiu Jinsong * TBD: implement acpi_processor_device_remove if Xen support 30339adc483d378f79711f291539f20e3797337892dLiu Jinsong * CPU hotremove in the future. 30439adc483d378f79711f291539f20e3797337892dLiu Jinsong */ 30539adc483d378f79711f291539f20e3797337892dLiu Jinsong acpi_processor_device_remove(device); 30639adc483d378f79711f291539f20e3797337892dLiu Jinsong break; 30739adc483d378f79711f291539f20e3797337892dLiu Jinsong 30839adc483d378f79711f291539f20e3797337892dLiu Jinsong default: 30939adc483d378f79711f291539f20e3797337892dLiu Jinsong ACPI_DEBUG_PRINT((ACPI_DB_INFO, 31039adc483d378f79711f291539f20e3797337892dLiu Jinsong "Unsupported event [0x%x]\n", event)); 31139adc483d378f79711f291539f20e3797337892dLiu Jinsong 31239adc483d378f79711f291539f20e3797337892dLiu Jinsong /* non-hotplug event; possibly handled by other handler */ 3131b37d6ea2cdc61102f4643d8cd654ec15212b927Liu Jinsong goto out; 31439adc483d378f79711f291539f20e3797337892dLiu Jinsong } 31539adc483d378f79711f291539f20e3797337892dLiu Jinsong 316700b8422fb9e6041907bda45a15f4e2ab0438a04Rafael J. Wysocki (void) acpi_evaluate_ost(handle, event, ost_code, NULL); 3171b37d6ea2cdc61102f4643d8cd654ec15212b927Liu Jinsong 3181b37d6ea2cdc61102f4643d8cd654ec15212b927Liu Jinsongout: 3191b37d6ea2cdc61102f4643d8cd654ec15212b927Liu Jinsong acpi_scan_lock_release(); 32039adc483d378f79711f291539f20e3797337892dLiu Jinsong} 32139adc483d378f79711f291539f20e3797337892dLiu Jinsong 32239adc483d378f79711f291539f20e3797337892dLiu Jinsongstatic acpi_status is_processor_device(acpi_handle handle) 32339adc483d378f79711f291539f20e3797337892dLiu Jinsong{ 32439adc483d378f79711f291539f20e3797337892dLiu Jinsong struct acpi_device_info *info; 32539adc483d378f79711f291539f20e3797337892dLiu Jinsong char *hid; 32639adc483d378f79711f291539f20e3797337892dLiu Jinsong acpi_status status; 32739adc483d378f79711f291539f20e3797337892dLiu Jinsong 32839adc483d378f79711f291539f20e3797337892dLiu Jinsong status = acpi_get_object_info(handle, &info); 32939adc483d378f79711f291539f20e3797337892dLiu Jinsong if (ACPI_FAILURE(status)) 33039adc483d378f79711f291539f20e3797337892dLiu Jinsong return status; 33139adc483d378f79711f291539f20e3797337892dLiu Jinsong 33239adc483d378f79711f291539f20e3797337892dLiu Jinsong if (info->type == ACPI_TYPE_PROCESSOR) { 33339adc483d378f79711f291539f20e3797337892dLiu Jinsong kfree(info); 33439adc483d378f79711f291539f20e3797337892dLiu Jinsong return AE_OK; /* found a processor object */ 33539adc483d378f79711f291539f20e3797337892dLiu Jinsong } 33639adc483d378f79711f291539f20e3797337892dLiu Jinsong 33739adc483d378f79711f291539f20e3797337892dLiu Jinsong if (!(info->valid & ACPI_VALID_HID)) { 33839adc483d378f79711f291539f20e3797337892dLiu Jinsong kfree(info); 33939adc483d378f79711f291539f20e3797337892dLiu Jinsong return AE_ERROR; 34039adc483d378f79711f291539f20e3797337892dLiu Jinsong } 34139adc483d378f79711f291539f20e3797337892dLiu Jinsong 34239adc483d378f79711f291539f20e3797337892dLiu Jinsong hid = info->hardware_id.string; 34339adc483d378f79711f291539f20e3797337892dLiu Jinsong if ((hid == NULL) || strcmp(hid, ACPI_PROCESSOR_DEVICE_HID)) { 34439adc483d378f79711f291539f20e3797337892dLiu Jinsong kfree(info); 34539adc483d378f79711f291539f20e3797337892dLiu Jinsong return AE_ERROR; 34639adc483d378f79711f291539f20e3797337892dLiu Jinsong } 34739adc483d378f79711f291539f20e3797337892dLiu Jinsong 34839adc483d378f79711f291539f20e3797337892dLiu Jinsong kfree(info); 34939adc483d378f79711f291539f20e3797337892dLiu Jinsong return AE_OK; /* found a processor device object */ 35039adc483d378f79711f291539f20e3797337892dLiu Jinsong} 35139adc483d378f79711f291539f20e3797337892dLiu Jinsong 35239adc483d378f79711f291539f20e3797337892dLiu Jinsongstatic acpi_status 35339adc483d378f79711f291539f20e3797337892dLiu Jinsongprocessor_walk_namespace_cb(acpi_handle handle, 35439adc483d378f79711f291539f20e3797337892dLiu Jinsong u32 lvl, void *context, void **rv) 35539adc483d378f79711f291539f20e3797337892dLiu Jinsong{ 35639adc483d378f79711f291539f20e3797337892dLiu Jinsong acpi_status status; 35739adc483d378f79711f291539f20e3797337892dLiu Jinsong int *action = context; 35839adc483d378f79711f291539f20e3797337892dLiu Jinsong 35939adc483d378f79711f291539f20e3797337892dLiu Jinsong status = is_processor_device(handle); 36039adc483d378f79711f291539f20e3797337892dLiu Jinsong if (ACPI_FAILURE(status)) 36139adc483d378f79711f291539f20e3797337892dLiu Jinsong return AE_OK; /* not a processor; continue to walk */ 36239adc483d378f79711f291539f20e3797337892dLiu Jinsong 36339adc483d378f79711f291539f20e3797337892dLiu Jinsong switch (*action) { 36439adc483d378f79711f291539f20e3797337892dLiu Jinsong case INSTALL_NOTIFY_HANDLER: 36539adc483d378f79711f291539f20e3797337892dLiu Jinsong acpi_install_notify_handler(handle, 36639adc483d378f79711f291539f20e3797337892dLiu Jinsong ACPI_SYSTEM_NOTIFY, 36739adc483d378f79711f291539f20e3797337892dLiu Jinsong acpi_processor_hotplug_notify, 36839adc483d378f79711f291539f20e3797337892dLiu Jinsong NULL); 36939adc483d378f79711f291539f20e3797337892dLiu Jinsong break; 37039adc483d378f79711f291539f20e3797337892dLiu Jinsong case UNINSTALL_NOTIFY_HANDLER: 37139adc483d378f79711f291539f20e3797337892dLiu Jinsong acpi_remove_notify_handler(handle, 37239adc483d378f79711f291539f20e3797337892dLiu Jinsong ACPI_SYSTEM_NOTIFY, 37339adc483d378f79711f291539f20e3797337892dLiu Jinsong acpi_processor_hotplug_notify); 37439adc483d378f79711f291539f20e3797337892dLiu Jinsong break; 37539adc483d378f79711f291539f20e3797337892dLiu Jinsong default: 37639adc483d378f79711f291539f20e3797337892dLiu Jinsong break; 37739adc483d378f79711f291539f20e3797337892dLiu Jinsong } 37839adc483d378f79711f291539f20e3797337892dLiu Jinsong 37939adc483d378f79711f291539f20e3797337892dLiu Jinsong /* found a processor; skip walking underneath */ 38039adc483d378f79711f291539f20e3797337892dLiu Jinsong return AE_CTRL_DEPTH; 38139adc483d378f79711f291539f20e3797337892dLiu Jinsong} 38239adc483d378f79711f291539f20e3797337892dLiu Jinsong 38339adc483d378f79711f291539f20e3797337892dLiu Jinsongstatic 38439adc483d378f79711f291539f20e3797337892dLiu Jinsongvoid acpi_processor_install_hotplug_notify(void) 38539adc483d378f79711f291539f20e3797337892dLiu Jinsong{ 38639adc483d378f79711f291539f20e3797337892dLiu Jinsong int action = INSTALL_NOTIFY_HANDLER; 38739adc483d378f79711f291539f20e3797337892dLiu Jinsong acpi_walk_namespace(ACPI_TYPE_ANY, 38839adc483d378f79711f291539f20e3797337892dLiu Jinsong ACPI_ROOT_OBJECT, 38939adc483d378f79711f291539f20e3797337892dLiu Jinsong ACPI_UINT32_MAX, 39039adc483d378f79711f291539f20e3797337892dLiu Jinsong processor_walk_namespace_cb, NULL, &action, NULL); 39139adc483d378f79711f291539f20e3797337892dLiu Jinsong} 39239adc483d378f79711f291539f20e3797337892dLiu Jinsong 39339adc483d378f79711f291539f20e3797337892dLiu Jinsongstatic 39439adc483d378f79711f291539f20e3797337892dLiu Jinsongvoid acpi_processor_uninstall_hotplug_notify(void) 39539adc483d378f79711f291539f20e3797337892dLiu Jinsong{ 39639adc483d378f79711f291539f20e3797337892dLiu Jinsong int action = UNINSTALL_NOTIFY_HANDLER; 39739adc483d378f79711f291539f20e3797337892dLiu Jinsong acpi_walk_namespace(ACPI_TYPE_ANY, 39839adc483d378f79711f291539f20e3797337892dLiu Jinsong ACPI_ROOT_OBJECT, 39939adc483d378f79711f291539f20e3797337892dLiu Jinsong ACPI_UINT32_MAX, 40039adc483d378f79711f291539f20e3797337892dLiu Jinsong processor_walk_namespace_cb, NULL, &action, NULL); 40139adc483d378f79711f291539f20e3797337892dLiu Jinsong} 40239adc483d378f79711f291539f20e3797337892dLiu Jinsong 40339adc483d378f79711f291539f20e3797337892dLiu Jinsongstatic const struct acpi_device_id processor_device_ids[] = { 40439adc483d378f79711f291539f20e3797337892dLiu Jinsong {ACPI_PROCESSOR_OBJECT_HID, 0}, 40539adc483d378f79711f291539f20e3797337892dLiu Jinsong {ACPI_PROCESSOR_DEVICE_HID, 0}, 40639adc483d378f79711f291539f20e3797337892dLiu Jinsong {"", 0}, 40739adc483d378f79711f291539f20e3797337892dLiu Jinsong}; 40839adc483d378f79711f291539f20e3797337892dLiu JinsongMODULE_DEVICE_TABLE(acpi, processor_device_ids); 40939adc483d378f79711f291539f20e3797337892dLiu Jinsong 41039adc483d378f79711f291539f20e3797337892dLiu Jinsongstatic struct acpi_driver xen_acpi_processor_driver = { 41139adc483d378f79711f291539f20e3797337892dLiu Jinsong .name = "processor", 41239adc483d378f79711f291539f20e3797337892dLiu Jinsong .class = ACPI_PROCESSOR_CLASS, 41339adc483d378f79711f291539f20e3797337892dLiu Jinsong .ids = processor_device_ids, 41439adc483d378f79711f291539f20e3797337892dLiu Jinsong .ops = { 41539adc483d378f79711f291539f20e3797337892dLiu Jinsong .add = xen_acpi_processor_add, 41639adc483d378f79711f291539f20e3797337892dLiu Jinsong .remove = xen_acpi_processor_remove, 41739adc483d378f79711f291539f20e3797337892dLiu Jinsong }, 41839adc483d378f79711f291539f20e3797337892dLiu Jinsong}; 41939adc483d378f79711f291539f20e3797337892dLiu Jinsong 42039adc483d378f79711f291539f20e3797337892dLiu Jinsongstatic int __init xen_acpi_processor_init(void) 42139adc483d378f79711f291539f20e3797337892dLiu Jinsong{ 42239adc483d378f79711f291539f20e3797337892dLiu Jinsong int result = 0; 42339adc483d378f79711f291539f20e3797337892dLiu Jinsong 42439adc483d378f79711f291539f20e3797337892dLiu Jinsong if (!xen_initial_domain()) 42539adc483d378f79711f291539f20e3797337892dLiu Jinsong return -ENODEV; 42639adc483d378f79711f291539f20e3797337892dLiu Jinsong 42739adc483d378f79711f291539f20e3797337892dLiu Jinsong /* unregister the stub which only used to reserve driver space */ 42839adc483d378f79711f291539f20e3797337892dLiu Jinsong xen_stub_processor_exit(); 42939adc483d378f79711f291539f20e3797337892dLiu Jinsong 43039adc483d378f79711f291539f20e3797337892dLiu Jinsong result = acpi_bus_register_driver(&xen_acpi_processor_driver); 43139adc483d378f79711f291539f20e3797337892dLiu Jinsong if (result < 0) { 43239adc483d378f79711f291539f20e3797337892dLiu Jinsong xen_stub_processor_init(); 43339adc483d378f79711f291539f20e3797337892dLiu Jinsong return result; 43439adc483d378f79711f291539f20e3797337892dLiu Jinsong } 43539adc483d378f79711f291539f20e3797337892dLiu Jinsong 43639adc483d378f79711f291539f20e3797337892dLiu Jinsong acpi_processor_install_hotplug_notify(); 43739adc483d378f79711f291539f20e3797337892dLiu Jinsong return 0; 43839adc483d378f79711f291539f20e3797337892dLiu Jinsong} 43939adc483d378f79711f291539f20e3797337892dLiu Jinsong 44039adc483d378f79711f291539f20e3797337892dLiu Jinsongstatic void __exit xen_acpi_processor_exit(void) 44139adc483d378f79711f291539f20e3797337892dLiu Jinsong{ 44239adc483d378f79711f291539f20e3797337892dLiu Jinsong if (!xen_initial_domain()) 44339adc483d378f79711f291539f20e3797337892dLiu Jinsong return; 44439adc483d378f79711f291539f20e3797337892dLiu Jinsong 44539adc483d378f79711f291539f20e3797337892dLiu Jinsong acpi_processor_uninstall_hotplug_notify(); 44639adc483d378f79711f291539f20e3797337892dLiu Jinsong 44739adc483d378f79711f291539f20e3797337892dLiu Jinsong acpi_bus_unregister_driver(&xen_acpi_processor_driver); 44839adc483d378f79711f291539f20e3797337892dLiu Jinsong 44939adc483d378f79711f291539f20e3797337892dLiu Jinsong /* 45039adc483d378f79711f291539f20e3797337892dLiu Jinsong * stub reserve space again to prevent any chance of native 45139adc483d378f79711f291539f20e3797337892dLiu Jinsong * driver loading. 45239adc483d378f79711f291539f20e3797337892dLiu Jinsong */ 45339adc483d378f79711f291539f20e3797337892dLiu Jinsong xen_stub_processor_init(); 45439adc483d378f79711f291539f20e3797337892dLiu Jinsong return; 45539adc483d378f79711f291539f20e3797337892dLiu Jinsong} 45639adc483d378f79711f291539f20e3797337892dLiu Jinsong 45739adc483d378f79711f291539f20e3797337892dLiu Jinsongmodule_init(xen_acpi_processor_init); 45839adc483d378f79711f291539f20e3797337892dLiu Jinsongmodule_exit(xen_acpi_processor_exit); 45939adc483d378f79711f291539f20e3797337892dLiu JinsongACPI_MODULE_NAME("xen-acpi-cpuhotplug"); 46039adc483d378f79711f291539f20e3797337892dLiu JinsongMODULE_AUTHOR("Liu Jinsong <jinsong.liu@intel.com>"); 46139adc483d378f79711f291539f20e3797337892dLiu JinsongMODULE_DESCRIPTION("Xen Hotplug CPU Driver"); 46239adc483d378f79711f291539f20e3797337892dLiu JinsongMODULE_LICENSE("GPL"); 463