📄 microcode.c
字号:
return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;}static ssize_t microcode_write (struct file *file, const char __user *buf, size_t len, loff_t *ppos){ ssize_t ret; if ((len >> PAGE_SHIFT) > num_physpages) { printk(KERN_ERR "microcode: too much data (max %ld pages)\n", num_physpages); return -EINVAL; } lock_cpu_hotplug(); mutex_lock(µcode_mutex); user_buffer = (void __user *) buf; user_buffer_size = (int) len; ret = do_microcode_update(); if (!ret) ret = (ssize_t)len; mutex_unlock(µcode_mutex); unlock_cpu_hotplug(); return ret;}static const struct file_operations microcode_fops = { .owner = THIS_MODULE, .write = microcode_write, .open = microcode_open,};static struct miscdevice microcode_dev = { .minor = MICROCODE_MINOR, .name = "microcode", .fops = µcode_fops,};static int __init microcode_dev_init (void){ int error; error = misc_register(µcode_dev); if (error) { printk(KERN_ERR "microcode: can't misc_register on minor=%d\n", MICROCODE_MINOR); return error; } return 0;}static void microcode_dev_exit (void){ misc_deregister(µcode_dev);}MODULE_ALIAS_MISCDEV(MICROCODE_MINOR);#else#define microcode_dev_init() 0#define microcode_dev_exit() do { } while(0)#endifstatic long get_next_ucode_from_buffer(void **mc, void *buf, unsigned long size, long offset){ microcode_header_t *mc_header; unsigned long total_size; /* No more data */ if (offset >= size) return 0; mc_header = (microcode_header_t *)(buf + offset); total_size = get_totalsize(mc_header); if (offset + total_size > size) { printk(KERN_ERR "microcode: error! Bad data in microcode data file\n"); return -EINVAL; } *mc = vmalloc(total_size); if (!*mc) { printk(KERN_ERR "microcode: error! Can not allocate memory\n"); return -ENOMEM; } memcpy(*mc, buf + offset, total_size); return offset + total_size;}/* fake device for request_firmware */static struct platform_device *microcode_pdev;static int cpu_request_microcode(int cpu){ char name[30]; struct cpuinfo_x86 *c = &cpu_data(cpu); const struct firmware *firmware; void *buf; unsigned long size; long offset = 0; int error; void *mc; /* We should bind the task to the CPU */ BUG_ON(cpu != raw_smp_processor_id()); sprintf(name,"intel-ucode/%02x-%02x-%02x", c->x86, c->x86_model, c->x86_mask); error = request_firmware(&firmware, name, µcode_pdev->dev); if (error) { pr_debug("ucode data file %s load failed\n", name); return error; } buf = (void *)firmware->data; size = firmware->size; while ((offset = get_next_ucode_from_buffer(&mc, buf, size, offset)) > 0) { error = microcode_sanity_check(mc); if (error) break; error = get_maching_microcode(mc, cpu); if (error < 0) break; /* * It's possible the data file has multiple matching ucode, * lets keep searching till the latest version */ if (error == 1) { apply_microcode(cpu); error = 0; } vfree(mc); } if (offset > 0) vfree(mc); if (offset < 0) error = offset; release_firmware(firmware); return error;}static int apply_microcode_check_cpu(int cpu){ struct cpuinfo_x86 *c = &cpu_data(cpu); struct ucode_cpu_info *uci = ucode_cpu_info + cpu; cpumask_t old; unsigned int val[2]; int err = 0; /* Check if the microcode is available */ if (!uci->mc) return 0; old = current->cpus_allowed; set_cpus_allowed(current, cpumask_of_cpu(cpu)); /* Check if the microcode we have in memory matches the CPU */ if (c->x86_vendor != X86_VENDOR_INTEL || c->x86 < 6 || cpu_has(c, X86_FEATURE_IA64) || uci->sig != cpuid_eax(0x00000001)) err = -EINVAL; if (!err && ((c->x86_model >= 5) || (c->x86 > 6))) { /* get processor flags from MSR 0x17 */ rdmsr(MSR_IA32_PLATFORM_ID, val[0], val[1]); if (uci->pf != (1 << ((val[1] >> 18) & 7))) err = -EINVAL; } if (!err) { wrmsr(MSR_IA32_UCODE_REV, 0, 0); /* see notes above for revision 1.07. Apparent chip bug */ sync_core(); /* get the current revision from MSR 0x8B */ rdmsr(MSR_IA32_UCODE_REV, val[0], val[1]); if (uci->rev != val[1]) err = -EINVAL; } if (!err) apply_microcode(cpu); else printk(KERN_ERR "microcode: Could not apply microcode to CPU%d:" " sig=0x%x, pf=0x%x, rev=0x%x\n", cpu, uci->sig, uci->pf, uci->rev); set_cpus_allowed(current, old); return err;}static void microcode_init_cpu(int cpu, int resume){ cpumask_t old; struct ucode_cpu_info *uci = ucode_cpu_info + cpu; old = current->cpus_allowed; set_cpus_allowed(current, cpumask_of_cpu(cpu)); mutex_lock(µcode_mutex); collect_cpu_info(cpu); if (uci->valid && system_state == SYSTEM_RUNNING && !resume) cpu_request_microcode(cpu); mutex_unlock(µcode_mutex); set_cpus_allowed(current, old);}static void microcode_fini_cpu(int cpu){ struct ucode_cpu_info *uci = ucode_cpu_info + cpu; mutex_lock(µcode_mutex); uci->valid = 0; vfree(uci->mc); uci->mc = NULL; mutex_unlock(µcode_mutex);}static ssize_t reload_store(struct sys_device *dev, const char *buf, size_t sz){ struct ucode_cpu_info *uci = ucode_cpu_info + dev->id; char *end; unsigned long val = simple_strtoul(buf, &end, 0); int err = 0; int cpu = dev->id; if (end == buf) return -EINVAL; if (val == 1) { cpumask_t old; old = current->cpus_allowed; lock_cpu_hotplug(); set_cpus_allowed(current, cpumask_of_cpu(cpu)); mutex_lock(µcode_mutex); if (uci->valid) err = cpu_request_microcode(cpu); mutex_unlock(µcode_mutex); unlock_cpu_hotplug(); set_cpus_allowed(current, old); } if (err) return err; return sz;}static ssize_t version_show(struct sys_device *dev, char *buf){ struct ucode_cpu_info *uci = ucode_cpu_info + dev->id; return sprintf(buf, "0x%x\n", uci->rev);}static ssize_t pf_show(struct sys_device *dev, char *buf){ struct ucode_cpu_info *uci = ucode_cpu_info + dev->id; return sprintf(buf, "0x%x\n", uci->pf);}static SYSDEV_ATTR(reload, 0200, NULL, reload_store);static SYSDEV_ATTR(version, 0400, version_show, NULL);static SYSDEV_ATTR(processor_flags, 0400, pf_show, NULL);static struct attribute *mc_default_attrs[] = { &attr_reload.attr, &attr_version.attr, &attr_processor_flags.attr, NULL};static struct attribute_group mc_attr_group = { .attrs = mc_default_attrs, .name = "microcode",};static int __mc_sysdev_add(struct sys_device *sys_dev, int resume){ int err, cpu = sys_dev->id; struct ucode_cpu_info *uci = ucode_cpu_info + cpu; if (!cpu_online(cpu)) return 0; pr_debug("Microcode:CPU %d added\n", cpu); memset(uci, 0, sizeof(*uci)); err = sysfs_create_group(&sys_dev->kobj, &mc_attr_group); if (err) return err; microcode_init_cpu(cpu, resume); return 0;}static int mc_sysdev_add(struct sys_device *sys_dev){ return __mc_sysdev_add(sys_dev, 0);}static int mc_sysdev_remove(struct sys_device *sys_dev){ int cpu = sys_dev->id; if (!cpu_online(cpu)) return 0; pr_debug("Microcode:CPU %d removed\n", cpu); microcode_fini_cpu(cpu); sysfs_remove_group(&sys_dev->kobj, &mc_attr_group); return 0;}static int mc_sysdev_resume(struct sys_device *dev){ int cpu = dev->id; if (!cpu_online(cpu)) return 0; pr_debug("Microcode:CPU %d resumed\n", cpu); /* only CPU 0 will apply ucode here */ apply_microcode(0); return 0;}static struct sysdev_driver mc_sysdev_driver = { .add = mc_sysdev_add, .remove = mc_sysdev_remove, .resume = mc_sysdev_resume,};static __cpuinit intmc_cpu_callback(struct notifier_block *nb, unsigned long action, void *hcpu){ unsigned int cpu = (unsigned long)hcpu; struct sys_device *sys_dev; sys_dev = get_cpu_sysdev(cpu); switch (action) { case CPU_UP_CANCELED_FROZEN: /* The CPU refused to come up during a system resume */ microcode_fini_cpu(cpu); break; case CPU_ONLINE: case CPU_DOWN_FAILED: mc_sysdev_add(sys_dev); break; case CPU_ONLINE_FROZEN: /* System-wide resume is in progress, try to apply microcode */ if (apply_microcode_check_cpu(cpu)) { /* The application of microcode failed */ microcode_fini_cpu(cpu); __mc_sysdev_add(sys_dev, 1); break; } case CPU_DOWN_FAILED_FROZEN: if (sysfs_create_group(&sys_dev->kobj, &mc_attr_group)) printk(KERN_ERR "Microcode: Failed to create the sysfs " "group for CPU%d\n", cpu); break; case CPU_DOWN_PREPARE: mc_sysdev_remove(sys_dev); break; case CPU_DOWN_PREPARE_FROZEN: /* Suspend is in progress, only remove the interface */ sysfs_remove_group(&sys_dev->kobj, &mc_attr_group); break; } return NOTIFY_OK;}static struct notifier_block __cpuinitdata mc_cpu_notifier = { .notifier_call = mc_cpu_callback,};static int __init microcode_init (void){ int error; error = microcode_dev_init(); if (error) return error; microcode_pdev = platform_device_register_simple("microcode", -1, NULL, 0); if (IS_ERR(microcode_pdev)) { microcode_dev_exit(); return PTR_ERR(microcode_pdev); } lock_cpu_hotplug(); error = sysdev_driver_register(&cpu_sysdev_class, &mc_sysdev_driver); unlock_cpu_hotplug(); if (error) { microcode_dev_exit(); platform_device_unregister(microcode_pdev); return error; } register_hotcpu_notifier(&mc_cpu_notifier); printk(KERN_INFO "IA-32 Microcode Update Driver: v" MICROCODE_VERSION " <tigran@aivazian.fsnet.co.uk>\n"); return 0;}static void __exit microcode_exit (void){ microcode_dev_exit(); unregister_hotcpu_notifier(&mc_cpu_notifier); lock_cpu_hotplug(); sysdev_driver_unregister(&cpu_sysdev_class, &mc_sysdev_driver); unlock_cpu_hotplug(); platform_device_unregister(microcode_pdev);}module_init(microcode_init)module_exit(microcode_exit)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -