diff options
-rw-r--r-- | meta/recipes-devtools/qemu/qemu/0004-Add-support-for-VM-suspend-resume-for-TPM-TIS-v2.9.patch | 719 | ||||
-rw-r--r-- | meta/recipes-devtools/qemu/qemu_2.10.0-rc2.bb | 55 |
2 files changed, 774 insertions, 0 deletions
diff --git a/meta/recipes-devtools/qemu/qemu/0004-Add-support-for-VM-suspend-resume-for-TPM-TIS-v2.9.patch b/meta/recipes-devtools/qemu/qemu/0004-Add-support-for-VM-suspend-resume-for-TPM-TIS-v2.9.patch new file mode 100644 index 0000000000..f1dbaffeac --- /dev/null +++ b/meta/recipes-devtools/qemu/qemu/0004-Add-support-for-VM-suspend-resume-for-TPM-TIS-v2.9.patch | |||
@@ -0,0 +1,719 @@ | |||
1 | From 5e9dd9063f514447ea4f54046793f4f01c297ed4 Mon Sep 17 00:00:00 2001 | ||
2 | From: Stefan Berger <stefanb@linux.vnet.ibm.com> | ||
3 | Date: Sat, 31 Dec 2016 11:23:32 -0500 | ||
4 | Subject: [PATCH 4/4] Add support for VM suspend/resume for TPM TIS | ||
5 | |||
6 | Extend the TPM TIS code to support suspend/resume. In case a command | ||
7 | is being processed by the external TPM when suspending, wait for the command | ||
8 | to complete to catch the result. In case the bottom half did not run, | ||
9 | run the one function the bottom half is supposed to run. This then | ||
10 | makes the resume operation work. | ||
11 | |||
12 | The passthrough backend does not support suspend/resume operation | ||
13 | and is therefore blocked from suspend/resume and migration. | ||
14 | |||
15 | The CUSE TPM's supported capabilities are tested and if sufficient | ||
16 | capabilities are implemented, suspend/resume, snapshotting and | ||
17 | migration are supported by the CUSE TPM. | ||
18 | |||
19 | Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com> | ||
20 | |||
21 | Upstream-Status: Pending [https://lists.nongnu.org/archive/html/qemu-devel/2016-06/msg00252.html] | ||
22 | Signed-off-by: Patrick Ohly <patrick.ohly@intel.com> | ||
23 | --- | ||
24 | hw/tpm/tpm_passthrough.c | 130 +++++++++++++++++++++++-- | ||
25 | hw/tpm/tpm_tis.c | 137 +++++++++++++++++++++++++- | ||
26 | hw/tpm/tpm_tis.h | 2 + | ||
27 | hw/tpm/tpm_util.c | 223 +++++++++++++++++++++++++++++++++++++++++++ | ||
28 | hw/tpm/tpm_util.h | 7 ++ | ||
29 | include/sysemu/tpm_backend.h | 12 +++ | ||
30 | 6 files changed, 503 insertions(+), 8 deletions(-) | ||
31 | |||
32 | diff --git a/hw/tpm/tpm_passthrough.c b/hw/tpm/tpm_passthrough.c | ||
33 | index 44739ebad2..bc8072d0bc 100644 | ||
34 | --- a/hw/tpm/tpm_passthrough.c | ||
35 | +++ b/hw/tpm/tpm_passthrough.c | ||
36 | @@ -34,6 +34,8 @@ | ||
37 | #include "tpm_tis.h" | ||
38 | #include "tpm_util.h" | ||
39 | #include "tpm_ioctl.h" | ||
40 | +#include "migration/migration.h" | ||
41 | +#include "qapi/error.h" | ||
42 | |||
43 | #define DEBUG_TPM 0 | ||
44 | |||
45 | @@ -49,6 +51,7 @@ | ||
46 | #define TYPE_TPM_CUSE "tpm-cuse" | ||
47 | |||
48 | static const TPMDriverOps tpm_passthrough_driver; | ||
49 | +static const VMStateDescription vmstate_tpm_cuse; | ||
50 | |||
51 | /* data structures */ | ||
52 | typedef struct TPMPassthruThreadParams { | ||
53 | @@ -79,6 +82,10 @@ struct TPMPassthruState { | ||
54 | QemuMutex state_lock; | ||
55 | QemuCond cmd_complete; /* singnaled once tpm_busy is false */ | ||
56 | bool tpm_busy; | ||
57 | + | ||
58 | + Error *migration_blocker; | ||
59 | + | ||
60 | + TPMBlobBuffers tpm_blobs; | ||
61 | }; | ||
62 | |||
63 | typedef struct TPMPassthruState TPMPassthruState; | ||
64 | @@ -306,6 +313,10 @@ static void tpm_passthrough_shutdown(TPMPassthruState *tpm_pt) | ||
65 | strerror(errno)); | ||
66 | } | ||
67 | } | ||
68 | + if (tpm_pt->migration_blocker) { | ||
69 | + migrate_del_blocker(tpm_pt->migration_blocker); | ||
70 | + error_free(tpm_pt->migration_blocker); | ||
71 | + } | ||
72 | } | ||
73 | |||
74 | /* | ||
75 | @@ -360,12 +371,14 @@ static int tpm_passthrough_cuse_check_caps(TPMPassthruState *tpm_pt) | ||
76 | /* | ||
77 | * Initialize the external CUSE TPM | ||
78 | */ | ||
79 | -static int tpm_passthrough_cuse_init(TPMPassthruState *tpm_pt) | ||
80 | +static int tpm_passthrough_cuse_init(TPMPassthruState *tpm_pt, | ||
81 | + bool is_resume) | ||
82 | { | ||
83 | int rc = 0; | ||
84 | - ptm_init init = { | ||
85 | - .u.req.init_flags = PTM_INIT_FLAG_DELETE_VOLATILE, | ||
86 | - }; | ||
87 | + ptm_init init; | ||
88 | + if (is_resume) { | ||
89 | + init.u.req.init_flags = PTM_INIT_FLAG_DELETE_VOLATILE; | ||
90 | + } | ||
91 | |||
92 | if (TPM_PASSTHROUGH_USES_CUSE_TPM(tpm_pt)) { | ||
93 | if (ioctl(tpm_pt->tpm_fd, PTM_INIT, &init) < 0) { | ||
94 | @@ -394,7 +407,7 @@ static int tpm_passthrough_startup_tpm(TPMBackend *tb) | ||
95 | tpm_passthrough_worker_thread, | ||
96 | &tpm_pt->tpm_thread_params); | ||
97 | |||
98 | - tpm_passthrough_cuse_init(tpm_pt); | ||
99 | + tpm_passthrough_cuse_init(tpm_pt, false); | ||
100 | |||
101 | return 0; | ||
102 | } | ||
103 | @@ -466,6 +479,32 @@ static int tpm_passthrough_reset_tpm_established_flag(TPMBackend *tb, | ||
104 | return rc; | ||
105 | } | ||
106 | |||
107 | +static int tpm_cuse_get_state_blobs(TPMBackend *tb, | ||
108 | + bool decrypted_blobs, | ||
109 | + TPMBlobBuffers *tpm_blobs) | ||
110 | +{ | ||
111 | + TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); | ||
112 | + | ||
113 | + assert(TPM_PASSTHROUGH_USES_CUSE_TPM(tpm_pt)); | ||
114 | + | ||
115 | + return tpm_util_cuse_get_state_blobs(tpm_pt->tpm_fd, decrypted_blobs, | ||
116 | + tpm_blobs); | ||
117 | +} | ||
118 | + | ||
119 | +static int tpm_cuse_set_state_blobs(TPMBackend *tb, | ||
120 | + TPMBlobBuffers *tpm_blobs) | ||
121 | +{ | ||
122 | + TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); | ||
123 | + | ||
124 | + assert(TPM_PASSTHROUGH_USES_CUSE_TPM(tpm_pt)); | ||
125 | + | ||
126 | + if (tpm_util_cuse_set_state_blobs(tpm_pt->tpm_fd, tpm_blobs)) { | ||
127 | + return 1; | ||
128 | + } | ||
129 | + | ||
130 | + return tpm_passthrough_cuse_init(tpm_pt, true); | ||
131 | +} | ||
132 | + | ||
133 | static bool tpm_passthrough_get_startup_error(TPMBackend *tb) | ||
134 | { | ||
135 | TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); | ||
136 | @@ -488,7 +527,7 @@ static void tpm_passthrough_deliver_request(TPMBackend *tb) | ||
137 | { | ||
138 | TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); | ||
139 | |||
140 | - /* TPM considered busy once TPM Request scheduled for processing */ | ||
141 | + /* TPM considered busy once TPM request scheduled for processing */ | ||
142 | qemu_mutex_lock(&tpm_pt->state_lock); | ||
143 | tpm_pt->tpm_busy = true; | ||
144 | qemu_mutex_unlock(&tpm_pt->state_lock); | ||
145 | @@ -601,6 +640,25 @@ static int tpm_passthrough_open_sysfs_cancel(TPMBackend *tb) | ||
146 | return fd; | ||
147 | } | ||
148 | |||
149 | +static void tpm_passthrough_block_migration(TPMPassthruState *tpm_pt) | ||
150 | +{ | ||
151 | + ptm_cap caps; | ||
152 | + | ||
153 | + if (TPM_PASSTHROUGH_USES_CUSE_TPM(tpm_pt)) { | ||
154 | + caps = PTM_CAP_GET_STATEBLOB | PTM_CAP_SET_STATEBLOB | | ||
155 | + PTM_CAP_STOP; | ||
156 | + if (!TPM_CUSE_IMPLEMENTS_ALL(tpm_pt, caps)) { | ||
157 | + error_setg(&tpm_pt->migration_blocker, | ||
158 | + "Migration disabled: CUSE TPM lacks necessary capabilities"); | ||
159 | + migrate_add_blocker(tpm_pt->migration_blocker); | ||
160 | + } | ||
161 | + } else { | ||
162 | + error_setg(&tpm_pt->migration_blocker, | ||
163 | + "Migration disabled: Passthrough TPM does not support migration"); | ||
164 | + migrate_add_blocker(tpm_pt->migration_blocker); | ||
165 | + } | ||
166 | +} | ||
167 | + | ||
168 | static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb) | ||
169 | { | ||
170 | TPMPassthruState *tpm_pt = TPM_PASSTHROUGH(tb); | ||
171 | @@ -642,7 +700,7 @@ static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb) | ||
172 | goto err_close_tpmdev; | ||
173 | } | ||
174 | /* init TPM for probing */ | ||
175 | - if (tpm_passthrough_cuse_init(tpm_pt)) { | ||
176 | + if (tpm_passthrough_cuse_init(tpm_pt, false)) { | ||
177 | goto err_close_tpmdev; | ||
178 | } | ||
179 | } | ||
180 | @@ -659,6 +717,7 @@ static int tpm_passthrough_handle_device_opts(QemuOpts *opts, TPMBackend *tb) | ||
181 | } | ||
182 | } | ||
183 | |||
184 | + tpm_passthrough_block_migration(tpm_pt); | ||
185 | |||
186 | return 0; | ||
187 | |||
188 | @@ -766,10 +825,13 @@ static void tpm_passthrough_inst_init(Object *obj) | ||
189 | |||
190 | qemu_mutex_init(&tpm_pt->state_lock); | ||
191 | qemu_cond_init(&tpm_pt->cmd_complete); | ||
192 | + | ||
193 | + vmstate_register(NULL, -1, &vmstate_tpm_cuse, obj); | ||
194 | } | ||
195 | |||
196 | static void tpm_passthrough_inst_finalize(Object *obj) | ||
197 | { | ||
198 | + vmstate_unregister(NULL, &vmstate_tpm_cuse, obj); | ||
199 | } | ||
200 | |||
201 | static void tpm_passthrough_class_init(ObjectClass *klass, void *data) | ||
202 | @@ -802,6 +864,60 @@ static const char *tpm_passthrough_cuse_create_desc(void) | ||
203 | return "CUSE TPM backend driver"; | ||
204 | } | ||
205 | |||
206 | +static void tpm_cuse_pre_save(void *opaque) | ||
207 | +{ | ||
208 | + TPMPassthruState *tpm_pt = opaque; | ||
209 | + TPMBackend *tb = &tpm_pt->parent; | ||
210 | + | ||
211 | + qemu_mutex_lock(&tpm_pt->state_lock); | ||
212 | + /* wait for TPM to finish processing */ | ||
213 | + if (tpm_pt->tpm_busy) { | ||
214 | + qemu_cond_wait(&tpm_pt->cmd_complete, &tpm_pt->state_lock); | ||
215 | + } | ||
216 | + qemu_mutex_unlock(&tpm_pt->state_lock); | ||
217 | + | ||
218 | + /* get the decrypted state blobs from the TPM */ | ||
219 | + tpm_cuse_get_state_blobs(tb, TRUE, &tpm_pt->tpm_blobs); | ||
220 | +} | ||
221 | + | ||
222 | +static int tpm_cuse_post_load(void *opaque, | ||
223 | + int version_id __attribute__((unused))) | ||
224 | +{ | ||
225 | + TPMPassthruState *tpm_pt = opaque; | ||
226 | + TPMBackend *tb = &tpm_pt->parent; | ||
227 | + | ||
228 | + return tpm_cuse_set_state_blobs(tb, &tpm_pt->tpm_blobs); | ||
229 | +} | ||
230 | + | ||
231 | +static const VMStateDescription vmstate_tpm_cuse = { | ||
232 | + .name = "cuse-tpm", | ||
233 | + .version_id = 1, | ||
234 | + .minimum_version_id = 0, | ||
235 | + .minimum_version_id_old = 0, | ||
236 | + .pre_save = tpm_cuse_pre_save, | ||
237 | + .post_load = tpm_cuse_post_load, | ||
238 | + .fields = (VMStateField[]) { | ||
239 | + VMSTATE_UINT32(tpm_blobs.permanent_flags, TPMPassthruState), | ||
240 | + VMSTATE_UINT32(tpm_blobs.permanent.size, TPMPassthruState), | ||
241 | + VMSTATE_VBUFFER_ALLOC_UINT32(tpm_blobs.permanent.buffer, | ||
242 | + TPMPassthruState, 1, NULL, | ||
243 | + tpm_blobs.permanent.size), | ||
244 | + | ||
245 | + VMSTATE_UINT32(tpm_blobs.volatil_flags, TPMPassthruState), | ||
246 | + VMSTATE_UINT32(tpm_blobs.volatil.size, TPMPassthruState), | ||
247 | + VMSTATE_VBUFFER_ALLOC_UINT32(tpm_blobs.volatil.buffer, | ||
248 | + TPMPassthruState, 1, NULL, | ||
249 | + tpm_blobs.volatil.size), | ||
250 | + | ||
251 | + VMSTATE_UINT32(tpm_blobs.savestate_flags, TPMPassthruState), | ||
252 | + VMSTATE_UINT32(tpm_blobs.savestate.size, TPMPassthruState), | ||
253 | + VMSTATE_VBUFFER_ALLOC_UINT32(tpm_blobs.savestate.buffer, | ||
254 | + TPMPassthruState, 1, NULL, | ||
255 | + tpm_blobs.savestate.size), | ||
256 | + VMSTATE_END_OF_LIST() | ||
257 | + } | ||
258 | +}; | ||
259 | + | ||
260 | static const TPMDriverOps tpm_cuse_driver = { | ||
261 | .type = TPM_TYPE_CUSE_TPM, | ||
262 | .opts = tpm_passthrough_cmdline_opts, | ||
263 | diff --git a/hw/tpm/tpm_tis.c b/hw/tpm/tpm_tis.c | ||
264 | index 14d9e83ea2..9b660cf737 100644 | ||
265 | --- a/hw/tpm/tpm_tis.c | ||
266 | +++ b/hw/tpm/tpm_tis.c | ||
267 | @@ -368,6 +368,8 @@ static void tpm_tis_receive_bh(void *opaque) | ||
268 | TPMTISEmuState *tis = &s->s.tis; | ||
269 | uint8_t locty = s->locty_number; | ||
270 | |||
271 | + tis->bh_scheduled = false; | ||
272 | + | ||
273 | qemu_mutex_lock(&s->state_lock); | ||
274 | |||
275 | tpm_tis_sts_set(&tis->loc[locty], | ||
276 | @@ -415,6 +417,8 @@ static void tpm_tis_receive_cb(TPMState *s, uint8_t locty, | ||
277 | qemu_mutex_unlock(&s->state_lock); | ||
278 | |||
279 | qemu_bh_schedule(tis->bh); | ||
280 | + | ||
281 | + tis->bh_scheduled = true; | ||
282 | } | ||
283 | |||
284 | /* | ||
285 | @@ -1030,9 +1034,140 @@ static void tpm_tis_reset(DeviceState *dev) | ||
286 | tpm_tis_do_startup_tpm(s); | ||
287 | } | ||
288 | |||
289 | + | ||
290 | +/* persistent state handling */ | ||
291 | + | ||
292 | +static void tpm_tis_pre_save(void *opaque) | ||
293 | +{ | ||
294 | + TPMState *s = opaque; | ||
295 | + TPMTISEmuState *tis = &s->s.tis; | ||
296 | + uint8_t locty = tis->active_locty; | ||
297 | + | ||
298 | + DPRINTF("tpm_tis: suspend: locty = %d : r_offset = %d, w_offset = %d\n", | ||
299 | + locty, tis->loc[0].r_offset, tis->loc[0].w_offset); | ||
300 | +#ifdef DEBUG_TIS | ||
301 | + tpm_tis_dump_state(opaque, 0); | ||
302 | +#endif | ||
303 | + | ||
304 | + qemu_mutex_lock(&s->state_lock); | ||
305 | + | ||
306 | + /* wait for outstanding request to complete */ | ||
307 | + if (TPM_TIS_IS_VALID_LOCTY(locty) && | ||
308 | + tis->loc[locty].state == TPM_TIS_STATE_EXECUTION) { | ||
309 | + /* | ||
310 | + * If we get here when the bh is scheduled but did not run, | ||
311 | + * we won't get notified... | ||
312 | + */ | ||
313 | + if (!tis->bh_scheduled) { | ||
314 | + /* backend thread to notify us */ | ||
315 | + qemu_cond_wait(&s->cmd_complete, &s->state_lock); | ||
316 | + } | ||
317 | + if (tis->loc[locty].state == TPM_TIS_STATE_EXECUTION) { | ||
318 | + /* bottom half did not run - run its function */ | ||
319 | + qemu_mutex_unlock(&s->state_lock); | ||
320 | + tpm_tis_receive_bh(opaque); | ||
321 | + qemu_mutex_lock(&s->state_lock); | ||
322 | + } | ||
323 | + } | ||
324 | + | ||
325 | + qemu_mutex_unlock(&s->state_lock); | ||
326 | + | ||
327 | + /* copy current active read or write buffer into the buffer | ||
328 | + written to disk */ | ||
329 | + if (TPM_TIS_IS_VALID_LOCTY(locty)) { | ||
330 | + switch (tis->loc[locty].state) { | ||
331 | + case TPM_TIS_STATE_RECEPTION: | ||
332 | + memcpy(tis->buf, | ||
333 | + tis->loc[locty].w_buffer.buffer, | ||
334 | + MIN(sizeof(tis->buf), | ||
335 | + tis->loc[locty].w_buffer.size)); | ||
336 | + tis->offset = tis->loc[locty].w_offset; | ||
337 | + break; | ||
338 | + case TPM_TIS_STATE_COMPLETION: | ||
339 | + memcpy(tis->buf, | ||
340 | + tis->loc[locty].r_buffer.buffer, | ||
341 | + MIN(sizeof(tis->buf), | ||
342 | + tis->loc[locty].r_buffer.size)); | ||
343 | + tis->offset = tis->loc[locty].r_offset; | ||
344 | + break; | ||
345 | + default: | ||
346 | + /* leak nothing */ | ||
347 | + memset(tis->buf, 0x0, sizeof(tis->buf)); | ||
348 | + break; | ||
349 | + } | ||
350 | + } | ||
351 | +} | ||
352 | + | ||
353 | +static int tpm_tis_post_load(void *opaque, | ||
354 | + int version_id __attribute__((unused))) | ||
355 | +{ | ||
356 | + TPMState *s = opaque; | ||
357 | + TPMTISEmuState *tis = &s->s.tis; | ||
358 | + | ||
359 | + uint8_t locty = tis->active_locty; | ||
360 | + | ||
361 | + if (TPM_TIS_IS_VALID_LOCTY(locty)) { | ||
362 | + switch (tis->loc[locty].state) { | ||
363 | + case TPM_TIS_STATE_RECEPTION: | ||
364 | + memcpy(tis->loc[locty].w_buffer.buffer, | ||
365 | + tis->buf, | ||
366 | + MIN(sizeof(tis->buf), | ||
367 | + tis->loc[locty].w_buffer.size)); | ||
368 | + tis->loc[locty].w_offset = tis->offset; | ||
369 | + break; | ||
370 | + case TPM_TIS_STATE_COMPLETION: | ||
371 | + memcpy(tis->loc[locty].r_buffer.buffer, | ||
372 | + tis->buf, | ||
373 | + MIN(sizeof(tis->buf), | ||
374 | + tis->loc[locty].r_buffer.size)); | ||
375 | + tis->loc[locty].r_offset = tis->offset; | ||
376 | + break; | ||
377 | + default: | ||
378 | + break; | ||
379 | + } | ||
380 | + } | ||
381 | + | ||
382 | + DPRINTF("tpm_tis: resume : locty = %d : r_offset = %d, w_offset = %d\n", | ||
383 | + locty, tis->loc[0].r_offset, tis->loc[0].w_offset); | ||
384 | + | ||
385 | + return 0; | ||
386 | +} | ||
387 | + | ||
388 | +static const VMStateDescription vmstate_locty = { | ||
389 | + .name = "loc", | ||
390 | + .version_id = 1, | ||
391 | + .minimum_version_id = 0, | ||
392 | + .minimum_version_id_old = 0, | ||
393 | + .fields = (VMStateField[]) { | ||
394 | + VMSTATE_UINT32(state, TPMLocality), | ||
395 | + VMSTATE_UINT32(inte, TPMLocality), | ||
396 | + VMSTATE_UINT32(ints, TPMLocality), | ||
397 | + VMSTATE_UINT8(access, TPMLocality), | ||
398 | + VMSTATE_UINT32(sts, TPMLocality), | ||
399 | + VMSTATE_UINT32(iface_id, TPMLocality), | ||
400 | + VMSTATE_END_OF_LIST(), | ||
401 | + } | ||
402 | +}; | ||
403 | + | ||
404 | static const VMStateDescription vmstate_tpm_tis = { | ||
405 | .name = "tpm", | ||
406 | - .unmigratable = 1, | ||
407 | + .version_id = 1, | ||
408 | + .minimum_version_id = 0, | ||
409 | + .minimum_version_id_old = 0, | ||
410 | + .pre_save = tpm_tis_pre_save, | ||
411 | + .post_load = tpm_tis_post_load, | ||
412 | + .fields = (VMStateField[]) { | ||
413 | + VMSTATE_UINT32(s.tis.offset, TPMState), | ||
414 | + VMSTATE_BUFFER(s.tis.buf, TPMState), | ||
415 | + VMSTATE_UINT8(s.tis.active_locty, TPMState), | ||
416 | + VMSTATE_UINT8(s.tis.aborting_locty, TPMState), | ||
417 | + VMSTATE_UINT8(s.tis.next_locty, TPMState), | ||
418 | + | ||
419 | + VMSTATE_STRUCT_ARRAY(s.tis.loc, TPMState, TPM_TIS_NUM_LOCALITIES, 1, | ||
420 | + vmstate_locty, TPMLocality), | ||
421 | + | ||
422 | + VMSTATE_END_OF_LIST() | ||
423 | + } | ||
424 | }; | ||
425 | |||
426 | static Property tpm_tis_properties[] = { | ||
427 | diff --git a/hw/tpm/tpm_tis.h b/hw/tpm/tpm_tis.h | ||
428 | index a1df41fa21..b7fc0ea1a9 100644 | ||
429 | --- a/hw/tpm/tpm_tis.h | ||
430 | +++ b/hw/tpm/tpm_tis.h | ||
431 | @@ -54,6 +54,8 @@ typedef struct TPMLocality { | ||
432 | |||
433 | typedef struct TPMTISEmuState { | ||
434 | QEMUBH *bh; | ||
435 | + bool bh_scheduled; /* bh scheduled but did not run yet */ | ||
436 | + | ||
437 | uint32_t offset; | ||
438 | uint8_t buf[TPM_TIS_BUFFER_MAX]; | ||
439 | |||
440 | diff --git a/hw/tpm/tpm_util.c b/hw/tpm/tpm_util.c | ||
441 | index 7b35429725..b6ff74d946 100644 | ||
442 | --- a/hw/tpm/tpm_util.c | ||
443 | +++ b/hw/tpm/tpm_util.c | ||
444 | @@ -22,6 +22,17 @@ | ||
445 | #include "qemu/osdep.h" | ||
446 | #include "tpm_util.h" | ||
447 | #include "tpm_int.h" | ||
448 | +#include "tpm_ioctl.h" | ||
449 | +#include "qemu/error-report.h" | ||
450 | + | ||
451 | +#define DEBUG_TPM 0 | ||
452 | + | ||
453 | +#define DPRINTF(fmt, ...) do { \ | ||
454 | + if (DEBUG_TPM) { \ | ||
455 | + fprintf(stderr, fmt, ## __VA_ARGS__); \ | ||
456 | + } \ | ||
457 | +} while (0) | ||
458 | + | ||
459 | |||
460 | /* | ||
461 | * A basic test of a TPM device. We expect a well formatted response header | ||
462 | @@ -125,3 +136,215 @@ int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version) | ||
463 | |||
464 | return 1; | ||
465 | } | ||
466 | + | ||
467 | +static void tpm_sized_buffer_reset(TPMSizedBuffer *tsb) | ||
468 | +{ | ||
469 | + g_free(tsb->buffer); | ||
470 | + tsb->buffer = NULL; | ||
471 | + tsb->size = 0; | ||
472 | +} | ||
473 | + | ||
474 | +/* | ||
475 | + * Transfer a TPM state blob from the TPM into a provided buffer. | ||
476 | + * | ||
477 | + * @fd: file descriptor to talk to the CUSE TPM | ||
478 | + * @type: the type of blob to transfer | ||
479 | + * @decrypted_blob: whether we request to receive decrypted blobs | ||
480 | + * @tsb: the TPMSizeBuffer to fill with the blob | ||
481 | + * @flags: the flags to return to the caller | ||
482 | + */ | ||
483 | +static int tpm_util_cuse_get_state_blob(int fd, | ||
484 | + uint8_t type, | ||
485 | + bool decrypted_blob, | ||
486 | + TPMSizedBuffer *tsb, | ||
487 | + uint32_t *flags) | ||
488 | +{ | ||
489 | + ptm_getstate pgs; | ||
490 | + uint16_t offset = 0; | ||
491 | + ptm_res res; | ||
492 | + ssize_t n; | ||
493 | + size_t to_read; | ||
494 | + | ||
495 | + tpm_sized_buffer_reset(tsb); | ||
496 | + | ||
497 | + pgs.u.req.state_flags = (decrypted_blob) ? PTM_STATE_FLAG_DECRYPTED : 0; | ||
498 | + pgs.u.req.type = type; | ||
499 | + pgs.u.req.offset = offset; | ||
500 | + | ||
501 | + if (ioctl(fd, PTM_GET_STATEBLOB, &pgs) < 0) { | ||
502 | + error_report("CUSE TPM PTM_GET_STATEBLOB ioctl failed: %s", | ||
503 | + strerror(errno)); | ||
504 | + goto err_exit; | ||
505 | + } | ||
506 | + res = pgs.u.resp.tpm_result; | ||
507 | + if (res != 0 && (res & 0x800) == 0) { | ||
508 | + error_report("Getting the stateblob (type %d) failed with a TPM " | ||
509 | + "error 0x%x", type, res); | ||
510 | + goto err_exit; | ||
511 | + } | ||
512 | + | ||
513 | + *flags = pgs.u.resp.state_flags; | ||
514 | + | ||
515 | + tsb->buffer = g_malloc(pgs.u.resp.totlength); | ||
516 | + memcpy(tsb->buffer, pgs.u.resp.data, pgs.u.resp.length); | ||
517 | + tsb->size = pgs.u.resp.length; | ||
518 | + | ||
519 | + /* if there are bytes left to get use read() interface */ | ||
520 | + while (tsb->size < pgs.u.resp.totlength) { | ||
521 | + to_read = pgs.u.resp.totlength - tsb->size; | ||
522 | + if (unlikely(to_read > SSIZE_MAX)) { | ||
523 | + to_read = SSIZE_MAX; | ||
524 | + } | ||
525 | + | ||
526 | + n = read(fd, &tsb->buffer[tsb->size], to_read); | ||
527 | + if (n != to_read) { | ||
528 | + error_report("Could not read stateblob (type %d) : %s", | ||
529 | + type, strerror(errno)); | ||
530 | + goto err_exit; | ||
531 | + } | ||
532 | + tsb->size += to_read; | ||
533 | + } | ||
534 | + | ||
535 | + DPRINTF("tpm_util: got state blob type %d, %d bytes, flags 0x%08x, " | ||
536 | + "decrypted=%d\n", type, tsb->size, *flags, decrypted_blob); | ||
537 | + | ||
538 | + return 0; | ||
539 | + | ||
540 | +err_exit: | ||
541 | + return 1; | ||
542 | +} | ||
543 | + | ||
544 | +int tpm_util_cuse_get_state_blobs(int tpm_fd, | ||
545 | + bool decrypted_blobs, | ||
546 | + TPMBlobBuffers *tpm_blobs) | ||
547 | +{ | ||
548 | + if (tpm_util_cuse_get_state_blob(tpm_fd, PTM_BLOB_TYPE_PERMANENT, | ||
549 | + decrypted_blobs, | ||
550 | + &tpm_blobs->permanent, | ||
551 | + &tpm_blobs->permanent_flags) || | ||
552 | + tpm_util_cuse_get_state_blob(tpm_fd, PTM_BLOB_TYPE_VOLATILE, | ||
553 | + decrypted_blobs, | ||
554 | + &tpm_blobs->volatil, | ||
555 | + &tpm_blobs->volatil_flags) || | ||
556 | + tpm_util_cuse_get_state_blob(tpm_fd, PTM_BLOB_TYPE_SAVESTATE, | ||
557 | + decrypted_blobs, | ||
558 | + &tpm_blobs->savestate, | ||
559 | + &tpm_blobs->savestate_flags)) { | ||
560 | + goto err_exit; | ||
561 | + } | ||
562 | + | ||
563 | + return 0; | ||
564 | + | ||
565 | + err_exit: | ||
566 | + tpm_sized_buffer_reset(&tpm_blobs->volatil); | ||
567 | + tpm_sized_buffer_reset(&tpm_blobs->permanent); | ||
568 | + tpm_sized_buffer_reset(&tpm_blobs->savestate); | ||
569 | + | ||
570 | + return 1; | ||
571 | +} | ||
572 | + | ||
573 | +static int tpm_util_cuse_do_set_stateblob_ioctl(int fd, | ||
574 | + uint32_t flags, | ||
575 | + uint32_t type, | ||
576 | + uint32_t length) | ||
577 | +{ | ||
578 | + ptm_setstate pss; | ||
579 | + | ||
580 | + pss.u.req.state_flags = flags; | ||
581 | + pss.u.req.type = type; | ||
582 | + pss.u.req.length = length; | ||
583 | + | ||
584 | + if (ioctl(fd, PTM_SET_STATEBLOB, &pss) < 0) { | ||
585 | + error_report("CUSE TPM PTM_SET_STATEBLOB ioctl failed: %s", | ||
586 | + strerror(errno)); | ||
587 | + return 1; | ||
588 | + } | ||
589 | + | ||
590 | + if (pss.u.resp.tpm_result != 0) { | ||
591 | + error_report("Setting the stateblob (type %d) failed with a TPM " | ||
592 | + "error 0x%x", type, pss.u.resp.tpm_result); | ||
593 | + return 1; | ||
594 | + } | ||
595 | + | ||
596 | + return 0; | ||
597 | +} | ||
598 | + | ||
599 | + | ||
600 | +/* | ||
601 | + * Transfer a TPM state blob to the CUSE TPM. | ||
602 | + * | ||
603 | + * @fd: file descriptor to talk to the CUSE TPM | ||
604 | + * @type: the type of TPM state blob to transfer | ||
605 | + * @tsb: TPMSizeBuffer containing the TPM state blob | ||
606 | + * @flags: Flags describing the (encryption) state of the TPM state blob | ||
607 | + */ | ||
608 | +static int tpm_util_cuse_set_state_blob(int fd, | ||
609 | + uint32_t type, | ||
610 | + TPMSizedBuffer *tsb, | ||
611 | + uint32_t flags) | ||
612 | +{ | ||
613 | + uint32_t offset = 0; | ||
614 | + ssize_t n; | ||
615 | + size_t to_write; | ||
616 | + | ||
617 | + /* initiate the transfer to the CUSE TPM */ | ||
618 | + if (tpm_util_cuse_do_set_stateblob_ioctl(fd, flags, type, 0)) { | ||
619 | + return 1; | ||
620 | + } | ||
621 | + | ||
622 | + /* use the write() interface for transferring the state blob */ | ||
623 | + while (offset < tsb->size) { | ||
624 | + to_write = tsb->size - offset; | ||
625 | + if (unlikely(to_write > SSIZE_MAX)) { | ||
626 | + to_write = SSIZE_MAX; | ||
627 | + } | ||
628 | + | ||
629 | + n = write(fd, &tsb->buffer[offset], to_write); | ||
630 | + if (n != to_write) { | ||
631 | + error_report("Writing the stateblob (type %d) failed: %s", | ||
632 | + type, strerror(errno)); | ||
633 | + goto err_exit; | ||
634 | + } | ||
635 | + offset += to_write; | ||
636 | + } | ||
637 | + | ||
638 | + /* inidicate that the transfer is finished */ | ||
639 | + if (tpm_util_cuse_do_set_stateblob_ioctl(fd, flags, type, 0)) { | ||
640 | + goto err_exit; | ||
641 | + } | ||
642 | + | ||
643 | + DPRINTF("tpm_util: set the state blob type %d, %d bytes, flags 0x%08x\n", | ||
644 | + type, tsb->size, flags); | ||
645 | + | ||
646 | + return 0; | ||
647 | + | ||
648 | +err_exit: | ||
649 | + return 1; | ||
650 | +} | ||
651 | + | ||
652 | +int tpm_util_cuse_set_state_blobs(int tpm_fd, | ||
653 | + TPMBlobBuffers *tpm_blobs) | ||
654 | +{ | ||
655 | + ptm_res res; | ||
656 | + | ||
657 | + if (ioctl(tpm_fd, PTM_STOP, &res) < 0) { | ||
658 | + error_report("tpm_passthrough: Could not stop " | ||
659 | + "the CUSE TPM: %s (%i)", | ||
660 | + strerror(errno), errno); | ||
661 | + return 1; | ||
662 | + } | ||
663 | + | ||
664 | + if (tpm_util_cuse_set_state_blob(tpm_fd, PTM_BLOB_TYPE_PERMANENT, | ||
665 | + &tpm_blobs->permanent, | ||
666 | + tpm_blobs->permanent_flags) || | ||
667 | + tpm_util_cuse_set_state_blob(tpm_fd, PTM_BLOB_TYPE_VOLATILE, | ||
668 | + &tpm_blobs->volatil, | ||
669 | + tpm_blobs->volatil_flags) || | ||
670 | + tpm_util_cuse_set_state_blob(tpm_fd, PTM_BLOB_TYPE_SAVESTATE, | ||
671 | + &tpm_blobs->savestate, | ||
672 | + tpm_blobs->savestate_flags)) { | ||
673 | + return 1; | ||
674 | + } | ||
675 | + | ||
676 | + return 0; | ||
677 | +} | ||
678 | diff --git a/hw/tpm/tpm_util.h b/hw/tpm/tpm_util.h | ||
679 | index df76245e6e..c24071d812 100644 | ||
680 | --- a/hw/tpm/tpm_util.h | ||
681 | +++ b/hw/tpm/tpm_util.h | ||
682 | @@ -26,4 +26,11 @@ | ||
683 | |||
684 | int tpm_util_test_tpmdev(int tpm_fd, TPMVersion *tpm_version); | ||
685 | |||
686 | +int tpm_util_cuse_get_state_blobs(int tpm_fd, | ||
687 | + bool decrypted_blobs, | ||
688 | + TPMBlobBuffers *tpm_blobs); | ||
689 | + | ||
690 | +int tpm_util_cuse_set_state_blobs(int tpm_fd, | ||
691 | + TPMBlobBuffers *tpm_blobs); | ||
692 | + | ||
693 | #endif /* TPM_TPM_UTIL_H */ | ||
694 | diff --git a/include/sysemu/tpm_backend.h b/include/sysemu/tpm_backend.h | ||
695 | index b58f52d39f..3403821b9d 100644 | ||
696 | --- a/include/sysemu/tpm_backend.h | ||
697 | +++ b/include/sysemu/tpm_backend.h | ||
698 | @@ -62,6 +62,18 @@ typedef struct TPMSizedBuffer { | ||
699 | uint8_t *buffer; | ||
700 | } TPMSizedBuffer; | ||
701 | |||
702 | +/* blobs from the TPM; part of VM state when migrating */ | ||
703 | +typedef struct TPMBlobBuffers { | ||
704 | + uint32_t permanent_flags; | ||
705 | + TPMSizedBuffer permanent; | ||
706 | + | ||
707 | + uint32_t volatil_flags; | ||
708 | + TPMSizedBuffer volatil; | ||
709 | + | ||
710 | + uint32_t savestate_flags; | ||
711 | + TPMSizedBuffer savestate; | ||
712 | +} TPMBlobBuffers; | ||
713 | + | ||
714 | struct TPMDriverOps { | ||
715 | enum TpmType type; | ||
716 | const QemuOptDesc *opts; | ||
717 | -- | ||
718 | 2.11.0 | ||
719 | |||
diff --git a/meta/recipes-devtools/qemu/qemu_2.10.0-rc2.bb b/meta/recipes-devtools/qemu/qemu_2.10.0-rc2.bb new file mode 100644 index 0000000000..04d656ba39 --- /dev/null +++ b/meta/recipes-devtools/qemu/qemu_2.10.0-rc2.bb | |||
@@ -0,0 +1,55 @@ | |||
1 | require qemu.inc | ||
2 | |||
3 | inherit ptest | ||
4 | |||
5 | RDEPENDS_${PN}-ptest = "bash make" | ||
6 | |||
7 | LIC_FILES_CHKSUM = "file://COPYING;md5=441c28d2cf86e15a37fa47e15a72fbac \ | ||
8 | file://COPYING.LIB;endline=24;md5=c04def7ae38850e7d3ef548588159913" | ||
9 | |||
10 | SRC_URI = "http://wiki.qemu-project.org/download/${BP}.tar.bz2 \ | ||
11 | file://powerpc_rom.bin \ | ||
12 | file://disable-grabs.patch \ | ||
13 | file://exclude-some-arm-EABI-obsolete-syscalls.patch \ | ||
14 | file://wacom.patch \ | ||
15 | file://add-ptest-in-makefile.patch \ | ||
16 | file://run-ptest \ | ||
17 | file://qemu-enlarge-env-entry-size.patch \ | ||
18 | file://no-valgrind.patch \ | ||
19 | file://pathlimit.patch \ | ||
20 | file://qemu-2.5.0-cflags.patch \ | ||
21 | file://glibc-2.25.patch \ | ||
22 | file://0001-Provide-support-for-the-CUSE-TPM.patch \ | ||
23 | file://0002-Introduce-condition-to-notify-waiters-of-completed-c.patch \ | ||
24 | file://0003-Introduce-condition-in-TPM-backend-for-notification.patch \ | ||
25 | file://0004-Add-support-for-VM-suspend-resume-for-TPM-TIS-v2.9.patch \ | ||
26 | file://apic-fixup-fallthrough-to-PIC.patch \ | ||
27 | " | ||
28 | |||
29 | SRC_URI_append_class-native = " \ | ||
30 | file://fix-libcap-header-issue-on-some-distro.patch \ | ||
31 | file://cpus.c-qemu_cpu_kick_thread_debugging.patch \ | ||
32 | " | ||
33 | SRC_URI[md5sum] = "634c498476e4b5643cf7a89e7416d0ae" | ||
34 | SRC_URI[sha256sum] = "9f8aaa2839634a2226a49d0f305ef922a7b0bc850d343aaee41948fd6f45700a" | ||
35 | |||
36 | COMPATIBLE_HOST_mipsarchn32 = "null" | ||
37 | COMPATIBLE_HOST_mipsarchn64 = "null" | ||
38 | |||
39 | do_install_append() { | ||
40 | # Prevent QA warnings about installed ${localstatedir}/run | ||
41 | if [ -d ${D}${localstatedir}/run ]; then rmdir ${D}${localstatedir}/run; fi | ||
42 | install -Dm 0755 ${WORKDIR}/powerpc_rom.bin ${D}${datadir}/qemu | ||
43 | } | ||
44 | |||
45 | do_compile_ptest() { | ||
46 | make buildtest-TESTS | ||
47 | } | ||
48 | |||
49 | do_install_ptest() { | ||
50 | cp -rL ${B}/tests ${D}${PTEST_PATH} | ||
51 | find ${D}${PTEST_PATH}/tests -type f -name "*.[Sshcod]" | xargs -i rm -rf {} | ||
52 | |||
53 | cp ${S}/tests/Makefile.include ${D}${PTEST_PATH}/tests | ||
54 | } | ||
55 | |||