File: microcode_xen.c

package info (click to toggle)
linux 3.2.73-2
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 692,996 kB
  • sloc: ansic: 9,719,677; asm: 244,034; xml: 40,377; makefile: 23,845; perl: 16,079; python: 4,929; sh: 4,425; cpp: 3,598; yacc: 2,979; lex: 1,726; awk: 708; pascal: 231; lisp: 218; sed: 30
file content (202 lines) | stat: -rw-r--r-- 4,287 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
/*
 * 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[36];
	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:
		/* Beginning with family 15h AMD uses family-specific firmware files. */
		if (c->x86 >= 0x15)
			snprintf(name, sizeof(name), "amd-ucode/microcode_amd_fam%.2xh.bin", c->x86);
		else
			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;
}