diff options
author | Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> | 2011-11-09 16:55:05 -0500 |
---|---|---|
committer | Konrad Rzeszutek Wilk <konrad.wilk@oracle.com> | 2011-11-09 16:55:05 -0500 |
commit | 99a55d75e44da47f59775e9fb31a8236a313f721 (patch) | |
tree | 60ee176079cc554bcefa3e123ded01f38036c0ef | |
parent | eccada5bdada737f0e2dca554833c9f98225a133 (diff) | |
parent | f6c958ff0d00ffbf1cdc8fcf2f2a82f06fbbb5f4 (diff) | |
download | xen-99a55d75e44da47f59775e9fb31a8236a313f721.tar.gz |
Merge branch 'stable/misc' into testing
* stable/misc:
x86/microcode: check proper return code.
xen/v86d: Fix /dev/mem to access memory below 1MB
xen: add CPU microcode update driver
xen: add dom0_op hypercall
xen/acpi: Domain0 acpi parser related platform hypercall
Conflicts:
arch/x86/xen/Kconfig
-rw-r--r-- | arch/x86/include/asm/microcode.h | 9 | ||||
-rw-r--r-- | arch/x86/kernel/Makefile | 1 | ||||
-rw-r--r-- | arch/x86/kernel/microcode_core.c | 5 | ||||
-rw-r--r-- | arch/x86/kernel/microcode_xen.c | 198 | ||||
-rw-r--r-- | arch/x86/xen/Kconfig | 5 | ||||
-rw-r--r-- | drivers/char/mem.c | 11 |
6 files changed, 225 insertions, 4 deletions
diff --git a/arch/x86/include/asm/microcode.h b/arch/x86/include/asm/microcode.h index 24215072d0e1e..22677d67dffbe 100644 --- a/arch/x86/include/asm/microcode.h +++ b/arch/x86/include/asm/microcode.h @@ -61,4 +61,13 @@ static inline struct microcode_ops * __init init_amd_microcode(void) } #endif +#ifdef CONFIG_MICROCODE_XEN +extern struct microcode_ops * __init init_xen_microcode(void); +#else +static inline struct microcode_ops * __init init_xen_microcode(void) +{ + return NULL; +} +#endif + #endif /* _ASM_X86_MICROCODE_H */ diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index 8baca3c4871c7..fab5ac407649a 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile @@ -92,6 +92,7 @@ obj-$(CONFIG_PCSPKR_PLATFORM) += pcspeaker.o microcode-y := microcode_core.o microcode-$(CONFIG_MICROCODE_INTEL) += microcode_intel.o microcode-$(CONFIG_MICROCODE_AMD) += microcode_amd.o +microcode-$(CONFIG_MICROCODE_XEN) += microcode_xen.o obj-$(CONFIG_MICROCODE) += microcode.o obj-$(CONFIG_X86_CHECK_BIOS_CORRUPTION) += check.o diff --git a/arch/x86/kernel/microcode_core.c b/arch/x86/kernel/microcode_core.c index f2d2a664e7975..ef2c583173146 100644 --- a/arch/x86/kernel/microcode_core.c +++ b/arch/x86/kernel/microcode_core.c @@ -84,6 +84,7 @@ #include <linux/mm.h> #include <linux/syscore_ops.h> +#include <xen/xen.h> #include <asm/microcode.h> #include <asm/processor.h> @@ -507,7 +508,9 @@ static int __init microcode_init(void) struct cpuinfo_x86 *c = &cpu_data(0); int error; - if (c->x86_vendor == X86_VENDOR_INTEL) + if (xen_pv_domain()) + microcode_ops = init_xen_microcode(); + else if (c->x86_vendor == X86_VENDOR_INTEL) microcode_ops = init_intel_microcode(); else if (c->x86_vendor == X86_VENDOR_AMD) microcode_ops = init_amd_microcode(); diff --git a/arch/x86/kernel/microcode_xen.c b/arch/x86/kernel/microcode_xen.c new file mode 100644 index 0000000000000..6a73957dd1875 --- /dev/null +++ b/arch/x86/kernel/microcode_xen.c @@ -0,0 +1,198 @@ +/* + * Xen microcode update driver + * + * Xen does most of the work here. We just pass the whole blob into + * Xen, and it will apply it to all CPUs as appropriate. Xen will + * worry about how different CPU models are actually updated. + */ +#include <linux/sched.h> +#include <linux/module.h> +#include <linux/firmware.h> +#include <linux/vmalloc.h> +#include <linux/uaccess.h> + +#include <asm/microcode.h> + +#include <xen/xen.h> +#include <xen/interface/platform.h> +#include <xen/interface/xen.h> + +#include <asm/xen/hypercall.h> +#include <asm/xen/hypervisor.h> + +MODULE_DESCRIPTION("Xen microcode update driver"); +MODULE_LICENSE("GPL"); + +struct xen_microcode { + size_t len; + char data[0]; +}; + +static int xen_microcode_update(int cpu) +{ + int err; + struct xen_platform_op op; + struct ucode_cpu_info *uci = ucode_cpu_info + cpu; + struct xen_microcode *uc = uci->mc; + + if (uc == NULL || uc->len == 0) { + /* + * We do all cpus at once, so we don't need to do + * other cpus explicitly (besides, these vcpu numbers + * have no relationship to underlying physical cpus). + */ + return 0; + } + + op.cmd = XENPF_microcode_update; + set_xen_guest_handle(op.u.microcode.data, uc->data); + op.u.microcode.length = uc->len; + + err = HYPERVISOR_dom0_op(&op); + + if (err != 0) + printk(KERN_WARNING "microcode_xen: microcode update failed: %d\n", err); + + return err; +} + +static enum ucode_state xen_request_microcode_fw(int cpu, struct device *device) +{ + char name[30]; + struct cpuinfo_x86 *c = &cpu_data(cpu); + const struct firmware *firmware; + struct ucode_cpu_info *uci = ucode_cpu_info + cpu; + enum ucode_state ret; + struct xen_microcode *uc; + size_t size; + int err; + + switch (c->x86_vendor) { + case X86_VENDOR_INTEL: + snprintf(name, sizeof(name), "intel-ucode/%02x-%02x-%02x", + c->x86, c->x86_model, c->x86_mask); + break; + + case X86_VENDOR_AMD: + snprintf(name, sizeof(name), "amd-ucode/microcode_amd.bin"); + break; + + default: + return UCODE_NFOUND; + } + + err = request_firmware(&firmware, name, device); + if (err) { + pr_debug("microcode: data file %s load failed\n", name); + return UCODE_NFOUND; + } + + /* + * Only bother getting real firmware for cpu 0; the others get + * dummy placeholders. + */ + if (cpu == 0) + size = firmware->size; + else + size = 0; + + if (uci->mc != NULL) { + vfree(uci->mc); + uci->mc = NULL; + } + + ret = UCODE_ERROR; + uc = vmalloc(sizeof(*uc) + size); + if (uc == NULL) + goto out; + + ret = UCODE_OK; + uc->len = size; + memcpy(uc->data, firmware->data, uc->len); + + uci->mc = uc; + +out: + release_firmware(firmware); + + return ret; +} + +static enum ucode_state xen_request_microcode_user(int cpu, + const void __user *buf, size_t size) +{ + struct ucode_cpu_info *uci = ucode_cpu_info + cpu; + struct xen_microcode *uc; + enum ucode_state ret; + size_t unread; + + if (cpu != 0) { + /* No real firmware for non-zero cpus; just store a + placeholder */ + size = 0; + } + + if (uci->mc != NULL) { + vfree(uci->mc); + uci->mc = NULL; + } + + ret = UCODE_ERROR; + uc = vmalloc(sizeof(*uc) + size); + if (uc == NULL) + goto out; + + uc->len = size; + + ret = UCODE_NFOUND; + + unread = copy_from_user(uc->data, buf, size); + + if (unread != 0) { + printk(KERN_WARNING "failed to read %zd of %zd bytes at %p -> %p\n", + unread, size, buf, uc->data); + goto out; + } + + ret = UCODE_OK; + +out: + if (ret == UCODE_OK) + uci->mc = uc; + else + vfree(uc); + + return ret; +} + +static void xen_microcode_fini_cpu(int cpu) +{ + struct ucode_cpu_info *uci = ucode_cpu_info + cpu; + + vfree(uci->mc); + uci->mc = NULL; +} + +static int xen_collect_cpu_info(int cpu, struct cpu_signature *sig) +{ + sig->sig = 0; + sig->pf = 0; + sig->rev = 0; + + return 0; +} + +static struct microcode_ops microcode_xen_ops = { + .request_microcode_user = xen_request_microcode_user, + .request_microcode_fw = xen_request_microcode_fw, + .collect_cpu_info = xen_collect_cpu_info, + .apply_microcode = xen_microcode_update, + .microcode_fini_cpu = xen_microcode_fini_cpu, +}; + +struct microcode_ops * __init init_xen_microcode(void) +{ + if (!xen_initial_domain()) + return NULL; + return µcode_xen_ops; +} diff --git a/arch/x86/xen/Kconfig b/arch/x86/xen/Kconfig index 26c731a106afd..eefea2700a843 100644 --- a/arch/x86/xen/Kconfig +++ b/arch/x86/xen/Kconfig @@ -48,3 +48,8 @@ config XEN_DEBUG_FS help Enable statistics output and various tuning options in debugfs. Enabling this option may incur a significant performance overhead. + + +config MICROCODE_XEN + def_bool y + depends on XEN_DOM0 && MICROCODE
\ No newline at end of file diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 1451790337160..5f96303fbbe44 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -314,9 +314,14 @@ static int mmap_mem(struct file *file, struct vm_area_struct *vma) &vma->vm_page_prot)) return -EINVAL; - vma->vm_page_prot = phys_mem_access_prot(file, vma->vm_pgoff, - size, - vma->vm_page_prot); + vma->vm_flags |= VM_RESERVED | VM_IO | VM_PFNMAP | VM_DONTEXPAND; + vma->vm_page_prot = __pgprot( + pgprot_val(vm_get_page_prot(vma->vm_flags)) | + _PAGE_IOMAP | + pgprot_val(phys_mem_access_prot(file, + vma->vm_pgoff, + size, + vma->vm_page_prot))); vma->vm_ops = &mmap_mem_ops; |