- Update Xen patches to 3.3-rc5 and c/s 1157.
[linux-flexiantxendom0-3.2.10.git] / arch / x86 / kernel / microcode_core-xen.c
1 /*
2  *      CPU Microcode Update Driver for Linux on Xen
3  *
4  *      Copyright (C) 2000-2006 Tigran Aivazian <tigran@aivazian.fsnet.co.uk>
5  *                    2006      Shaohua Li <shaohua.li@intel.com>
6  *
7  *      This driver allows to upgrade microcode on Intel processors
8  *      belonging to IA-32 family - PentiumPro, Pentium II,
9  *      Pentium III, Xeon, Pentium 4, etc.
10  *
11  *      Reference: Section 8.11 of Volume 3a, IA-32 Intel? Architecture
12  *      Software Developer's Manual
13  *      Order Number 253668 or free download from:
14  *
15  *      http://developer.intel.com/Assets/PDF/manual/253668.pdf
16  *
17  *      For more information, go to http://www.urbanmyth.org/microcode
18  *
19  *      This program is free software; you can redistribute it and/or
20  *      modify it under the terms of the GNU General Public License
21  *      as published by the Free Software Foundation; either version
22  *      2 of the License, or (at your option) any later version.
23  */
24
25 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
26
27 #include <linux/platform_device.h>
28 #include <linux/miscdevice.h>
29 #include <linux/capability.h>
30 #include <linux/kernel.h>
31 #include <linux/module.h>
32 #include <linux/mutex.h>
33 #include <linux/cpu.h>
34 #include <linux/fs.h>
35 #include <linux/mm.h>
36 #include <linux/firmware.h>
37 #include <linux/uaccess.h>
38 #include <linux/vmalloc.h>
39
40 #include <asm/microcode.h>
41 #include <asm/processor.h>
42 #include <asm/cpu_device_id.h>
43
44 #include <xen/pcpu.h>
45
46 MODULE_DESCRIPTION("Microcode Update Driver");
47 MODULE_AUTHOR("Tigran Aivazian <tigran@aivazian.fsnet.co.uk>");
48 MODULE_LICENSE("GPL");
49
50 static int verbose;
51 module_param(verbose, int, 0644);
52
53 #define MICROCODE_VERSION       "2.00-xen"
54
55 /*
56  * Synchronization.
57  *
58  * All non cpu-hotplug-callback call sites use:
59  *
60  * - microcode_mutex to synchronize with each other;
61  * - get/put_online_cpus() to synchronize with
62  *   the cpu-hotplug-callback call sites.
63  *
64  * We guarantee that only a single cpu is being
65  * updated at any particular moment of time.
66  */
67 static DEFINE_MUTEX(microcode_mutex);
68
69 #ifdef CONFIG_MICROCODE_OLD_INTERFACE
70 static int do_microcode_update(const void __user *ubuf, size_t len)
71 {
72         int err;
73         void *kbuf;
74
75         kbuf = vmalloc(len);
76         if (!kbuf)
77                 return -ENOMEM;
78
79         if (copy_from_user(kbuf, ubuf, len) == 0) {
80                 struct xen_platform_op op;
81
82                 op.cmd = XENPF_microcode_update;
83                 set_xen_guest_handle(op.u.microcode.data, kbuf);
84                 op.u.microcode.length = len;
85                 err = HYPERVISOR_platform_op(&op);
86         } else
87                 err = -EFAULT;
88
89         vfree(kbuf);
90
91         return err;
92 }
93
94 static int microcode_open(struct inode *inode, struct file *file)
95 {
96         return capable(CAP_SYS_RAWIO) ? nonseekable_open(inode, file) : -EPERM;
97 }
98
99 static ssize_t microcode_write(struct file *file, const char __user *buf,
100                                size_t len, loff_t *ppos)
101 {
102         ssize_t ret = -EINVAL;
103
104         if ((len >> PAGE_SHIFT) > totalram_pages) {
105                 pr_err("too much data (max %ld pages)\n", totalram_pages);
106                 return ret;
107         }
108
109         mutex_lock(&microcode_mutex);
110
111         if (do_microcode_update(buf, len) == 0)
112                 ret = (ssize_t)len;
113
114         mutex_unlock(&microcode_mutex);
115
116         return ret;
117 }
118
119 static const struct file_operations microcode_fops = {
120         .owner                  = THIS_MODULE,
121         .write                  = microcode_write,
122         .open                   = microcode_open,
123         .llseek         = no_llseek,
124 };
125
126 static struct miscdevice microcode_dev = {
127         .minor                  = MICROCODE_MINOR,
128         .name                   = "microcode",
129         .nodename               = "cpu/microcode",
130         .fops                   = &microcode_fops,
131 };
132
133 static int __init microcode_dev_init(void)
134 {
135         int error;
136
137         if (!is_initial_xendomain())
138                 return -ENODEV;
139
140         error = misc_register(&microcode_dev);
141         if (error) {
142                 pr_err("can't misc_register on minor=%d\n", MICROCODE_MINOR);
143                 return error;
144         }
145
146         return 0;
147 }
148
149 static void __exit microcode_dev_exit(void)
150 {
151         misc_deregister(&microcode_dev);
152 }
153
154 MODULE_ALIAS_MISCDEV(MICROCODE_MINOR);
155 MODULE_ALIAS("devname:cpu/microcode");
156 #else
157 #define microcode_dev_init()    0
158 #define microcode_dev_exit()    do { } while (0)
159 #endif
160
161 /* fake device for request_firmware */
162 static struct platform_device   *microcode_pdev;
163
164 static int request_microcode(const char *name)
165 {
166         const struct firmware *firmware;
167         int error;
168         struct xen_platform_op op;
169
170         error = request_firmware(&firmware, name, &microcode_pdev->dev);
171         if (error) {
172                 pr_debug("microcode: data file %s load failed\n", name);
173                 return error;
174         }
175
176         op.cmd = XENPF_microcode_update;
177         set_xen_guest_handle(op.u.microcode.data, firmware->data);
178         op.u.microcode.length = firmware->size;
179         error = HYPERVISOR_platform_op(&op);
180
181         release_firmware(firmware);
182
183         if (error)
184                 pr_debug("ucode load failed\n");
185
186         return error;
187 }
188
189 static const char amd_uc_name[] = "amd-ucode/microcode_amd.bin";
190 static const char amd_uc_fmt[] = "amd-ucode/microcode_amd_fam%x.bin";
191 static const char intel_uc_fmt[] = "intel-ucode/%02x-%02x-%02x";
192
193 static int ucode_cpu_callback(struct notifier_block *nfb,
194                               unsigned long action, void *hcpu)
195 {
196         unsigned int cpu = (unsigned long)hcpu;
197         struct xen_platform_op op;
198         char buf[36];
199         const char *uc_name = buf;
200
201         switch (action) {
202         case CPU_ONLINE:
203                 op.cmd = XENPF_get_cpu_version;
204                 op.u.pcpu_version.xen_cpuid = cpu;
205                 if (HYPERVISOR_platform_op(&op))
206                         break;
207                 if (op.u.pcpu_version.family == boot_cpu_data.x86
208                     && op.u.pcpu_version.model == boot_cpu_data.x86_model
209                     && op.u.pcpu_version.stepping == boot_cpu_data.x86_mask)
210                         break;
211                 if (strncmp(op.u.pcpu_version.vendor_id,
212                             "GenuineIntel", 12) == 0)
213                         snprintf(buf, sizeof(buf), intel_uc_fmt,
214                                  op.u.pcpu_version.family,
215                                  op.u.pcpu_version.model,
216                                  op.u.pcpu_version.stepping);
217                 else if (strncmp(op.u.pcpu_version.vendor_id,
218                                  "AuthenicAMD", 12) == 0) {
219                         if (op.u.pcpu_version.family >= 0x15)
220                                 snprintf(buf, sizeof(buf), amd_uc_fmt,
221                                          op.u.pcpu_version.family);
222                         else
223                                 uc_name = amd_uc_name;
224                 } else
225                         break;
226                 request_microcode(uc_name);
227                 break;
228         }
229
230         return NOTIFY_OK;
231 }
232
233 static struct notifier_block ucode_cpu_notifier = {
234         .notifier_call = ucode_cpu_callback
235 };
236
237 #ifdef MODULE
238 /* Autoload on Intel and AMD systems */
239 static const struct x86_cpu_id microcode_id[] = {
240         { X86_VENDOR_INTEL, X86_FAMILY_ANY, X86_MODEL_ANY, },
241         { X86_VENDOR_AMD, X86_FAMILY_ANY, X86_MODEL_ANY, },
242         {}
243 };
244 MODULE_DEVICE_TABLE(x86cpu, microcode_id);
245 #endif
246
247 static int __init microcode_init(void)
248 {
249         const struct cpuinfo_x86 *c = &boot_cpu_data;
250         char buf[36];
251         const char *fw_name = buf;
252         int error;
253
254         if (c->x86_vendor == X86_VENDOR_INTEL)
255                 snprintf(buf, sizeof(buf), intel_uc_fmt,
256                          c->x86, c->x86_model, c->x86_mask);
257         else if (c->x86_vendor == X86_VENDOR_AMD) {
258                 if (c->x86 >= 0x15)
259                         snprintf(buf, sizeof(buf), amd_uc_fmt, c->x86);
260                 else
261                         fw_name = amd_uc_name;
262         } else {
263                 pr_err("no support for this CPU vendor\n");
264                 return -ENODEV;
265         }
266
267         microcode_pdev = platform_device_register_simple("microcode", -1,
268                                                          NULL, 0);
269         if (IS_ERR(microcode_pdev))
270                 return PTR_ERR(microcode_pdev);
271
272         request_microcode(fw_name);
273
274         error = microcode_dev_init();
275         if (error) {
276                 platform_device_unregister(microcode_pdev);
277                 return error;
278         }
279
280         pr_info("Microcode Update Driver: v" MICROCODE_VERSION
281                 " <tigran@aivazian.fsnet.co.uk>, Peter Oruba\n");
282
283         error = register_pcpu_notifier(&ucode_cpu_notifier);
284         if (error)
285                 pr_warn("pCPU notifier registration failed (%d)\n", error);
286
287         return 0;
288 }
289 module_init(microcode_init);
290
291 static void __exit microcode_exit(void)
292 {
293         unregister_pcpu_notifier(&ucode_cpu_notifier);
294         microcode_dev_exit();
295         platform_device_unregister(microcode_pdev);
296
297         pr_info("Microcode Update Driver: v" MICROCODE_VERSION " removed.\n");
298 }
299 module_exit(microcode_exit);