aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKonrad Rzeszutek Wilk <konrad.wilk@oracle.com>2011-11-09 16:55:05 -0500
committerKonrad Rzeszutek Wilk <konrad.wilk@oracle.com>2011-11-09 16:55:05 -0500
commit99a55d75e44da47f59775e9fb31a8236a313f721 (patch)
tree60ee176079cc554bcefa3e123ded01f38036c0ef
parenteccada5bdada737f0e2dca554833c9f98225a133 (diff)
parentf6c958ff0d00ffbf1cdc8fcf2f2a82f06fbbb5f4 (diff)
downloadxen-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.h9
-rw-r--r--arch/x86/kernel/Makefile1
-rw-r--r--arch/x86/kernel/microcode_core.c5
-rw-r--r--arch/x86/kernel/microcode_xen.c198
-rw-r--r--arch/x86/xen/Kconfig5
-rw-r--r--drivers/char/mem.c11
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 &microcode_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;