From: jbeulich@novell.com Subject: force proper address translation in DCDBAS Patch-mainline: n/a The only caveat is that this doesn't work when Dom0 has its vCPU-s pinned. --- drivers/firmware/Kconfig | 1 drivers/firmware/dcdbas.c | 28 ++++++++- drivers/xen/core/domctl.c | 141 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/xen/core/domctl.h | 1 4 files changed, 169 insertions(+), 2 deletions(-) --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -91,6 +91,7 @@ config DELL_RBU config DCDBAS tristate "Dell Systems Management Base Driver" depends on X86 + select XEN_DOMCTL if XEN help The Dell Systems Management Base Driver provides a sysfs interface for systems management software to perform System Management --- a/drivers/firmware/dcdbas.c +++ b/drivers/firmware/dcdbas.c @@ -37,6 +37,10 @@ #include #include +#ifdef CONFIG_XEN +#include "../xen/core/domctl.h" +#endif + #include "dcdbas.h" #define DRIVER_NAME "dcdbas" @@ -107,7 +111,7 @@ static int smi_data_buf_realloc(unsigned /* set up new buffer for use */ smi_data_buf = buf; smi_data_buf_handle = handle; - smi_data_buf_phys_addr = (u32) virt_to_phys(buf); + smi_data_buf_phys_addr = (u32) handle; smi_data_buf_size = size; dev_dbg(&dcdbas_pdev->dev, "%s: phys: %x size: %lu\n", @@ -245,7 +249,9 @@ static ssize_t host_control_on_shutdown_ */ int dcdbas_smi_request(struct smi_cmd *smi_cmd) { +#ifndef CONFIG_XEN cpumask_var_t old_mask; +#endif int ret = 0; if (smi_cmd->magic != SMI_CMD_MAGIC) { @@ -255,6 +261,7 @@ int dcdbas_smi_request(struct smi_cmd *s } /* SMI requires CPU 0 */ +#ifndef CONFIG_XEN if (!alloc_cpumask_var(&old_mask, GFP_KERNEL)) return -ENOMEM; @@ -266,6 +273,14 @@ int dcdbas_smi_request(struct smi_cmd *s ret = -EBUSY; goto out; } +#else + ret = xen_set_physical_cpu_affinity(0); + if (ret) { + dev_dbg(&dcdbas_pdev->dev, "%s: failed (%d) to get CPU 0\n", + __func__, ret); + return ret; + } +#endif /* generate SMI */ /* inb to force posted write through and make SMI happen now */ @@ -280,9 +295,13 @@ int dcdbas_smi_request(struct smi_cmd *s : "memory" ); +#ifndef CONFIG_XEN out: set_cpus_allowed_ptr(current, old_mask); free_cpumask_var(old_mask); +#else + xen_set_physical_cpu_affinity(-1); +#endif return ret; } @@ -322,7 +341,7 @@ static ssize_t smi_request_store(struct break; case 1: /* Calling Interface SMI */ - smi_cmd->ebx = (u32) virt_to_phys(smi_cmd->command_buffer); + smi_cmd->ebx = (u32) virt_to_bus(smi_cmd->command_buffer); ret = dcdbas_smi_request(smi_cmd); if (!ret) ret = count; @@ -603,6 +622,11 @@ static int __init dcdbas_init(void) { int error; +#ifdef CONFIG_XEN + if (!is_initial_xendomain()) + return -ENODEV; +#endif + error = platform_driver_register(&dcdbas_driver); if (error) return error; --- a/drivers/xen/core/domctl.c +++ b/drivers/xen/core/domctl.c @@ -20,6 +20,8 @@ #undef __XEN_TOOLS__ #include #include +#include +#include #include #include @@ -34,6 +36,29 @@ typedef struct xen_domctl_address_size { typedef __attribute__((aligned(8))) uint64_t uint64_aligned_t; +struct xenctl_cpumap_v4 { + XEN_GUEST_HANDLE(uint8) bitmap; + uint32_t nr_cpus; +}; + +struct xenctl_cpumap_v5 { + union { + XEN_GUEST_HANDLE(uint8) bitmap; + uint64_aligned_t _align; + }; + uint32_t nr_cpus; +}; + +struct xen_domctl_vcpuaffinity_v4 { + uint32_t vcpu; + struct xenctl_cpumap_v4 cpumap; +}; + +struct xen_domctl_vcpuaffinity_v5 { + uint32_t vcpu; + struct xenctl_cpumap_v5 cpumap; +}; + union xen_domctl { /* v4: sle10 sp1: xen 3.0.4 + 32-on-64 patches */ struct { @@ -43,6 +68,7 @@ union xen_domctl { union { /* left out lots of other struct xen_domctl_foobar */ struct xen_domctl_address_size address_size; + struct xen_domctl_vcpuaffinity_v4 vcpu_affinity; uint64_t dummy_align; uint8_t dummy_pad[128]; }; @@ -59,6 +85,7 @@ union xen_domctl { domid_t domain; union { struct xen_domctl_address_size address_size; + struct xen_domctl_vcpuaffinity_v5 vcpu_affinity; uint64_aligned_t dummy_align; uint8_t dummy_pad[128]; }; @@ -124,4 +151,118 @@ int xen_guest_blkif_protocol(int domid) } EXPORT_SYMBOL_GPL(xen_guest_blkif_protocol); +#ifdef CONFIG_X86 + +#define vcpuaffinity(what, ver) ({ \ + memset(&domctl, 0, sizeof(domctl)); \ + domctl.v##ver.cmd = XEN_DOMCTL_##what##vcpuaffinity; \ + domctl.v##ver.interface_version = ver; \ + /* domctl.v##ver.domain = 0; */ \ + domctl.v##ver.vcpu_affinity.vcpu = smp_processor_id(); \ + domctl.v##ver.vcpu_affinity.cpumap.nr_cpus = nr; \ + set_xen_guest_handle(domctl.v##ver.vcpu_affinity.cpumap.bitmap, \ + mask); \ + hypervisor_domctl(&domctl); \ +}) + +static inline int get_vcpuaffinity(unsigned int nr, void *mask) +{ + union xen_domctl domctl; + int rc; + + BUILD_BUG_ON(XEN_DOMCTL_INTERFACE_VERSION > 7); + rc = vcpuaffinity(get, 7); +#if CONFIG_XEN_COMPAT < 0x040100 + if (rc) + rc = vcpuaffinity(get, 6); +#endif +#if CONFIG_XEN_COMPAT < 0x040000 + if (rc) + rc = vcpuaffinity(get, 5); +#endif +#if CONFIG_XEN_COMPAT < 0x030100 + if (rc) + rc = vcpuaffinity(get, 4); +#endif + return rc; +} + +static inline int set_vcpuaffinity(unsigned int nr, void *mask) +{ + union xen_domctl domctl; + int rc; + + BUILD_BUG_ON(XEN_DOMCTL_INTERFACE_VERSION > 7); + rc = vcpuaffinity(set, 7); +#if CONFIG_XEN_COMPAT < 0x040100 + if (rc) + rc = vcpuaffinity(set, 6); +#endif +#if CONFIG_XEN_COMPAT < 0x040000 + if (rc) + rc = vcpuaffinity(set, 5); +#endif +#if CONFIG_XEN_COMPAT < 0x030100 + if (rc) + rc = vcpuaffinity(set, 4); +#endif + return rc; +} + +static DEFINE_PER_CPU(void *, saved_pcpu_affinity); + +#define BITS_PER_PAGE (PAGE_SIZE * BITS_PER_LONG / sizeof(long)) + +int xen_set_physical_cpu_affinity(int pcpu) +{ + int rc; + + if (!is_initial_xendomain()) + return -EPERM; + + if (pcpu >= 0) { + void *oldmap; + + if (pcpu > BITS_PER_PAGE) + return -ERANGE; + + if (percpu_read(saved_pcpu_affinity)) + return -EBUSY; + + oldmap = (void *)get_zeroed_page(GFP_KERNEL); + if (!oldmap) + return -ENOMEM; + + rc = get_vcpuaffinity(BITS_PER_PAGE, oldmap); + if (!rc) { + void *newmap = kzalloc(BITS_TO_LONGS(pcpu + 1) + * sizeof(long), GFP_KERNEL); + + if (newmap) { + __set_bit(pcpu, newmap); + rc = set_vcpuaffinity(pcpu + 1, newmap); + kfree(newmap); + } else + rc = -ENOMEM; + } + + if (!rc) + percpu_write(saved_pcpu_affinity, oldmap); + else + free_page((unsigned long)oldmap); + } else { + if (!percpu_read(saved_pcpu_affinity)) + return 0; + rc = set_vcpuaffinity(BITS_PER_PAGE, + percpu_read(saved_pcpu_affinity)); + free_page((unsigned long)percpu_read(saved_pcpu_affinity)); + percpu_write(saved_pcpu_affinity, NULL); + } + + return rc; +} +EXPORT_SYMBOL_GPL(xen_set_physical_cpu_affinity); + +#endif /* CONFIG_X86 */ + MODULE_LICENSE("GPL"); --- a/drivers/xen/core/domctl.h +++ b/drivers/xen/core/domctl.h @@ -1,2 +1,3 @@ int xen_guest_address_size(int domid); int xen_guest_blkif_protocol(int domid); +int xen_set_physical_cpu_affinity(int pcpu);