xref: /qemu/hw/virtio/virtio-nsm.c (revision d64db833d6e3cbe9ea5f36342480f920f3675cea)
1 /*
2  * AWS Nitro Secure Module (NSM) device
3  *
4  * Copyright (c) 2024 Dorjoy Chowdhury <dorjoychy111@gmail.com>
5  *
6  * This work is licensed under the terms of the GNU GPL, version 2 or
7  * (at your option) any later version.  See the COPYING file in the
8  * top-level directory.
9  */
10 
11 #include "qemu/osdep.h"
12 #include "qemu/iov.h"
13 #include "qemu/guest-random.h"
14 #include "qapi/error.h"
15 
16 #include "crypto/hash.h"
17 #include "hw/virtio/virtio.h"
18 #include "hw/virtio/virtio-nsm.h"
19 #include "hw/virtio/cbor-helpers.h"
20 #include "standard-headers/linux/virtio_ids.h"
21 
22 #define NSM_REQUEST_MAX_SIZE      0x1000
23 #define NSM_RESPONSE_BUF_SIZE     0x3000
24 #define NSM_RND_BUF_SIZE          256
25 
26 enum NSMResponseTypes {
27     NSM_SUCCESS = 0,
28     NSM_INVALID_ARGUMENT = 1,
29     NSM_INVALID_INDEX = 2,
30     NSM_READONLY_INDEX = 3,
31     NSM_INVALID_OPERATION = 4,
32     NSM_BUFFER_TOO_SMALL = 5,
33     NSM_INPUT_TOO_LARGE = 6,
34     NSM_INTERNAL_ERROR = 7,
35 };
36 
37 static const char *error_string(enum NSMResponseTypes type)
38 {
39     const char *str;
40     switch (type) {
41     case NSM_INVALID_ARGUMENT:
42         str = "InvalidArgument";
43         break;
44     case NSM_INVALID_INDEX:
45         str = "InvalidIndex";
46         break;
47     case NSM_READONLY_INDEX:
48         str = "ReadOnlyIndex";
49         break;
50     case NSM_INVALID_OPERATION:
51         str = "InvalidOperation";
52         break;
53     case NSM_BUFFER_TOO_SMALL:
54         str = "BufferTooSmall";
55         break;
56     case NSM_INPUT_TOO_LARGE:
57         str = "InputTooLarge";
58         break;
59     default:
60         str = "InternalError";
61         break;
62     }
63 
64     return str;
65 }
66 
67 /*
68  * Error response structure:
69  *
70  * {
71  *   Map(1) {
72  *     key = String("Error"),
73  *     value = String(error_name)
74  *   }
75  * }
76  *
77  * where error_name can be one of the following:
78  *   InvalidArgument
79  *   InvalidIndex
80  *   InvalidResponse
81  *   ReadOnlyIndex
82  *   InvalidOperation
83  *   BufferTooSmall
84  *   InputTooLarge
85  *   InternalError
86  */
87 
88 static bool error_response(struct iovec *response, enum NSMResponseTypes error,
89                            Error **errp)
90 {
91     cbor_item_t *root;
92     size_t len;
93     bool r = false;
94 
95     root = cbor_new_definite_map(1);
96     if (!root) {
97         goto err;
98     }
99 
100     if (!qemu_cbor_add_string_to_map(root, "Error", error_string(error))) {
101         goto err;
102     }
103 
104     len = cbor_serialize(root, response->iov_base, response->iov_len);
105     if (len == 0) {
106         error_setg(errp, "Response buffer is small for %s response",
107                    error_string(error));
108         goto out;
109     }
110     response->iov_len = len;
111     r = true;
112 
113  out:
114     if (root) {
115         cbor_decref(&root);
116     }
117     return r;
118 
119  err:
120     error_setg(errp, "Failed to initialize %s response", error_string(error));
121     goto out;
122 }
123 
124 /*
125  * GetRandom response structure:
126  *
127  * {
128  *   Map(1) {
129  *     key = String("GetRandom"),
130  *     value = Map(1) {
131  *       key = String("random"),
132  *       value = Byte_String()
133  *     }
134  *   }
135  * }
136  */
137 static bool handle_get_random(VirtIONSM *vnsm, struct iovec *request,
138                               struct iovec *response, Error **errp)
139 {
140     cbor_item_t *root, *nested_map;
141     size_t len;
142     uint8_t rnd[NSM_RND_BUF_SIZE];
143     bool r = false;
144 
145     root = cbor_new_definite_map(1);
146     if (!root) {
147         goto err;
148     }
149 
150     if (!qemu_cbor_add_map_to_map(root, "GetRandom", 1, &nested_map)) {
151         goto err;
152     }
153 
154     qemu_guest_getrandom_nofail(rnd, NSM_RND_BUF_SIZE);
155 
156     if (!qemu_cbor_add_bytestring_to_map(nested_map, "random", rnd,
157                                          NSM_RND_BUF_SIZE)) {
158         goto err;
159     }
160 
161     len = cbor_serialize(root, response->iov_base, response->iov_len);
162     if (len == 0) {
163         if (error_response(response, NSM_INPUT_TOO_LARGE, errp)) {
164             r = true;
165         }
166         goto out;
167     }
168 
169     response->iov_len = len;
170     r = true;
171 
172  out:
173     if (root) {
174         cbor_decref(&root);
175     }
176     return r;
177 
178  err:
179     error_setg(errp, "Failed to initialize GetRandom response");
180     goto out;
181 }
182 
183 /*
184  * DescribeNSM response structure:
185  *
186  * {
187  *   Map(1) {
188  *     key = String("DescribeNSM"),
189  *     value = Map(7) {
190  *       key = String("digest"),
191  *       value = String("SHA384"),
192  *       key = String("max_pcrs"),
193  *       value = Uint8(32),
194  *       key = String("module_id"),
195  *       value = String("i-1234-enc5678"),
196  *       key = String("locked_pcrs"),
197  *       value = Array<Uint8>(),
198  *       key = String("version_major"),
199  *       value = Uint8(1),
200  *       key = String("version_minor"),
201  *       value = Uint8(0),
202  *       key = String("version_patch"),
203  *       value = Uint8(0)
204  *     }
205  *   }
206  * }
207  */
208 static bool handle_describe_nsm(VirtIONSM *vnsm, struct iovec *request,
209                                 struct iovec *response, Error **errp)
210 {
211     cbor_item_t *root, *nested_map;
212     uint16_t locked_pcrs_cnt = 0;
213     uint8_t locked_pcrs_ind[NSM_MAX_PCRS];
214     size_t len;
215     bool r = false;
216 
217     root = cbor_new_definite_map(1);
218     if (!root) {
219         goto err;
220     }
221 
222     if (!qemu_cbor_add_map_to_map(root, "DescribeNSM", 7, &nested_map)) {
223         goto err;
224     }
225 
226     if (!qemu_cbor_add_string_to_map(nested_map, "digest", vnsm->digest)) {
227         goto err;
228     }
229 
230     if (!qemu_cbor_add_uint8_to_map(nested_map, "max_pcrs", vnsm->max_pcrs)) {
231         goto err;
232     }
233 
234     if (!qemu_cbor_add_string_to_map(nested_map, "module_id",
235                                      vnsm->module_id)) {
236         goto err;
237     }
238 
239     for (uint8_t i = 0; i < NSM_MAX_PCRS; ++i) {
240         if (vnsm->pcrs[i].locked) {
241             locked_pcrs_ind[locked_pcrs_cnt++] = i;
242         }
243     }
244     if (!qemu_cbor_add_uint8_array_to_map(nested_map, "locked_pcrs",
245                                           locked_pcrs_ind, locked_pcrs_cnt)) {
246         goto err;
247     }
248 
249     if (!qemu_cbor_add_uint8_to_map(nested_map, "version_major",
250                                     vnsm->version_major)) {
251         goto err;
252     }
253 
254     if (!qemu_cbor_add_uint8_to_map(nested_map, "version_minor",
255                                     vnsm->version_minor)) {
256         goto err;
257     }
258 
259     if (!qemu_cbor_add_uint8_to_map(nested_map, "version_patch",
260                                     vnsm->version_patch)) {
261         goto err;
262     }
263 
264     len = cbor_serialize(root, response->iov_base, response->iov_len);
265     if (len == 0) {
266         if (error_response(response, NSM_INPUT_TOO_LARGE, errp)) {
267             r = true;
268         }
269         goto out;
270     }
271 
272     response->iov_len = len;
273     r = true;
274 
275  out:
276     if (root) {
277         cbor_decref(&root);
278     }
279     return r;
280 
281  err:
282     error_setg(errp, "Failed to initialize DescribeNSM response");
283     goto out;
284 }
285 
286 /*
287  * DescribePCR request structure:
288  *
289  * {
290  *   Map(1) {
291  *     key = String("DescribePCR"),
292  *     value = Map(1) {
293  *       key = String("index"),
294  *       value = Uint8(pcr)
295  *     }
296  *   }
297  * }
298  */
299 typedef struct NSMDescribePCRReq {
300     uint8_t index;
301 } NSMDescribePCRReq;
302 
303 static enum NSMResponseTypes get_nsm_describe_pcr_req(
304     uint8_t *req, size_t len,
305     NSMDescribePCRReq *nsm_req)
306 {
307     size_t size;
308     uint8_t *str;
309     struct cbor_pair *pair;
310     cbor_item_t *item = NULL;
311     struct cbor_load_result result;
312     enum NSMResponseTypes r = NSM_INVALID_OPERATION;
313 
314     item = cbor_load(req, len, &result);
315     if (!item || result.error.code != CBOR_ERR_NONE) {
316         goto cleanup;
317     }
318 
319     pair = cbor_map_handle(item);
320     if (!cbor_isa_map(pair->value)) {
321         goto cleanup;
322     }
323     size = cbor_map_size(pair->value);
324     if (size < 1) {
325         goto cleanup;
326     }
327 
328     pair = cbor_map_handle(pair->value);
329     for (int i = 0; i < size; ++i) {
330         if (!cbor_isa_string(pair[i].key)) {
331             continue;
332         }
333 
334         str = cbor_string_handle(pair[i].key);
335         if (str && cbor_string_length(pair[i].key) == 5 &&
336             memcmp(str, "index", 5) == 0) {
337             if (!cbor_isa_uint(pair[i].value) ||
338                 cbor_int_get_width(pair[i].value) != CBOR_INT_8) {
339                 break;
340             }
341 
342             nsm_req->index = cbor_get_uint8(pair[i].value);
343             r = NSM_SUCCESS;
344             break;
345         }
346     }
347 
348  cleanup:
349     if (item) {
350         cbor_decref(&item);
351     }
352     return r;
353 }
354 
355 /*
356  * DescribePCR response structure:
357  *
358  * {
359  *   Map(1) {
360  *     key = String("DescribePCR"),
361  *     value = Map(2) {
362  *       key = String("data"),
363  *       value = Byte_String(),
364  *       key = String("lock"),
365  *       value = Bool()
366  *     }
367  *   }
368  * }
369  */
370 static bool handle_describe_pcr(VirtIONSM *vnsm, struct iovec *request,
371                                 struct iovec *response, Error **errp)
372 {
373     cbor_item_t *root = NULL;
374     cbor_item_t *nested_map;
375     size_t len;
376     NSMDescribePCRReq nsm_req;
377     enum NSMResponseTypes type;
378     struct PCRInfo *pcr;
379     bool r = false;
380 
381     type = get_nsm_describe_pcr_req(request->iov_base, request->iov_len,
382                                     &nsm_req);
383     if (type != NSM_SUCCESS) {
384         if (error_response(response, type, errp)) {
385             r = true;
386         }
387         goto out;
388     }
389     if (nsm_req.index >= vnsm->max_pcrs) {
390         if (error_response(response, NSM_INVALID_INDEX, errp)) {
391             r = true;
392         }
393         goto out;
394     }
395     pcr = &(vnsm->pcrs[nsm_req.index]);
396 
397     root = cbor_new_definite_map(1);
398     if (!root) {
399         goto err;
400     }
401 
402     if (!qemu_cbor_add_map_to_map(root, "DescribePCR", 2, &nested_map)) {
403         goto err;
404     }
405 
406     if (!qemu_cbor_add_bytestring_to_map(nested_map, "data", pcr->data,
407                                          QCRYPTO_HASH_DIGEST_LEN_SHA384)) {
408         goto err;
409     }
410 
411     if (!qemu_cbor_add_bool_to_map(nested_map, "lock", pcr->locked)) {
412         goto err;
413     }
414 
415     len = cbor_serialize(root, response->iov_base, response->iov_len);
416     if (len == 0) {
417         if (error_response(response, NSM_INPUT_TOO_LARGE, errp)) {
418             r = true;
419         }
420         goto out;
421     }
422 
423     response->iov_len = len;
424     r = true;
425 
426  out:
427     if (root) {
428         cbor_decref(&root);
429     }
430     return r;
431 
432  err:
433     error_setg(errp, "Failed to initialize DescribePCR response");
434     goto out;
435 }
436 
437 /*
438  * ExtendPCR request structure:
439  *
440  * {
441  *   Map(1) {
442  *     key = String("ExtendPCR"),
443  *     value = Map(2) {
444  *       key = String("index"),
445  *       value = Uint8(pcr),
446  *       key = String("data"),
447  *       value = Byte_String(data) || String(data),
448  *     }
449  *   }
450  * }
451  */
452 typedef struct NSMExtendPCRReq {
453     uint8_t index;
454     uint16_t data_len;
455     uint8_t data[NSM_REQUEST_MAX_SIZE];
456 } NSMExtendPCRReq;
457 
458 static enum NSMResponseTypes get_nsm_extend_pcr_req(uint8_t *req, size_t len,
459                                                     NSMExtendPCRReq *nsm_req)
460 {
461     cbor_item_t *item = NULL;
462     size_t size ;
463     uint8_t *str;
464     bool index_found = false;
465     bool data_found = false;
466     struct cbor_pair *pair;
467     struct cbor_load_result result;
468     enum NSMResponseTypes r = NSM_INVALID_OPERATION;
469 
470     item = cbor_load(req, len, &result);
471     if (!item || result.error.code != CBOR_ERR_NONE) {
472         goto cleanup;
473     }
474 
475     pair = cbor_map_handle(item);
476     if (!cbor_isa_map(pair->value)) {
477         goto cleanup;
478     }
479     size = cbor_map_size(pair->value);
480     if (size < 2) {
481         goto cleanup;
482     }
483 
484     pair = cbor_map_handle(pair->value);
485     for (int i = 0; i < size; ++i) {
486         if (!cbor_isa_string(pair[i].key)) {
487             continue;
488         }
489         str = cbor_string_handle(pair[i].key);
490         if (!str) {
491             continue;
492         }
493 
494         if (cbor_string_length(pair[i].key) == 5 &&
495             memcmp(str, "index", 5) == 0) {
496             if (!cbor_isa_uint(pair[i].value) ||
497                 cbor_int_get_width(pair[i].value) != CBOR_INT_8) {
498                 goto cleanup;
499             }
500             nsm_req->index = cbor_get_uint8(pair[i].value);
501             index_found = true;
502             continue;
503         }
504 
505         if (cbor_string_length(pair[i].key) == 4 &&
506             memcmp(str, "data", 4) == 0) {
507             if (cbor_isa_bytestring(pair[i].value)) {
508                 str = cbor_bytestring_handle(pair[i].value);
509                 if (!str) {
510                     goto cleanup;
511                 }
512                 nsm_req->data_len = cbor_bytestring_length(pair[i].value);
513             } else if (cbor_isa_string(pair[i].value)) {
514                 str = cbor_string_handle(pair[i].value);
515                 if (!str) {
516                     goto cleanup;
517                 }
518                 nsm_req->data_len = cbor_string_length(pair[i].value);
519             } else {
520                 goto cleanup;
521             }
522             /*
523              * nsm_req->data_len will be smaller than NSM_REQUEST_MAX_SIZE as
524              * we already check for the max request size before processing
525              * any request. So it's safe to copy.
526              */
527             memcpy(nsm_req->data, str, nsm_req->data_len);
528             data_found = true;
529             continue;
530         }
531     }
532 
533     if (index_found && data_found) {
534         r = NSM_SUCCESS;
535     }
536 
537  cleanup:
538     if (item) {
539         cbor_decref(&item);
540     }
541     return r;
542 }
543 
544 /*
545  * ExtendPCR response structure:
546  *
547  * {
548  *   Map(1) {
549  *     key = String("ExtendPCR"),
550  *     value = Map(1) {
551  *       key = String("data"),
552  *       value = Byte_String()
553  *     }
554  *   }
555  * }
556  */
557 static bool handle_extend_pcr(VirtIONSM *vnsm, struct iovec *request,
558                               struct iovec *response, Error **errp)
559 {
560     cbor_item_t *root = NULL;
561     cbor_item_t *nested_map;
562     size_t len;
563     struct PCRInfo *pcr;
564     enum NSMResponseTypes type;
565     bool r = false;
566     g_autofree NSMExtendPCRReq *nsm_req = g_malloc(sizeof(NSMExtendPCRReq));
567 
568     type = get_nsm_extend_pcr_req(request->iov_base, request->iov_len,
569                                   nsm_req);
570     if (type != NSM_SUCCESS) {
571         if (error_response(response, type, errp)) {
572             r = true;
573         }
574         goto out;
575     }
576     if (nsm_req->index >= vnsm->max_pcrs) {
577         if (error_response(response, NSM_INVALID_INDEX, errp)) {
578             r = true;
579         }
580         goto out;
581     }
582 
583     pcr = &(vnsm->pcrs[nsm_req->index]);
584 
585     if (pcr->locked) {
586         if (error_response(response, NSM_READONLY_INDEX, errp)) {
587             r = true;
588         }
589         goto out;
590     }
591 
592     if (!vnsm->extend_pcr(vnsm, nsm_req->index, nsm_req->data,
593                           nsm_req->data_len)) {
594         if (error_response(response, NSM_INTERNAL_ERROR, errp)) {
595             r = true;
596         }
597         goto out;
598     }
599 
600     root = cbor_new_definite_map(1);
601     if (!root) {
602         goto err;
603     }
604 
605     if (!qemu_cbor_add_map_to_map(root, "ExtendPCR", 1, &nested_map)) {
606         goto err;
607     }
608 
609     if (!qemu_cbor_add_bytestring_to_map(nested_map, "data", pcr->data,
610                                          QCRYPTO_HASH_DIGEST_LEN_SHA384)) {
611         goto err;
612     }
613 
614     len = cbor_serialize(root, response->iov_base, response->iov_len);
615     if (len == 0) {
616         if (error_response(response, NSM_BUFFER_TOO_SMALL, errp)) {
617             r = true;
618         }
619         goto out;
620     }
621 
622     response->iov_len = len;
623     r = true;
624 
625  out:
626     if (root) {
627         cbor_decref(&root);
628     }
629     return r;
630 
631  err:
632     error_setg(errp, "Failed to initialize DescribePCR response");
633     goto out;
634 }
635 
636 /*
637  * LockPCR request structure:
638  *
639  * {
640  *   Map(1) {
641  *     key = String("LockPCR"),
642  *     value = Map(1) {
643  *       key = String("index"),
644  *       value = Uint8(pcr)
645  *     }
646  *   }
647  * }
648  */
649 typedef struct NSMLockPCRReq {
650     uint8_t index;
651 } NSMLockPCRReq;
652 
653 static enum NSMResponseTypes get_nsm_lock_pcr_req(uint8_t *req, size_t len,
654                                                   NSMLockPCRReq *nsm_req)
655 {
656     cbor_item_t *item = NULL;
657     size_t size;
658     uint8_t *str;
659     struct cbor_pair *pair;
660     struct cbor_load_result result;
661     enum NSMResponseTypes r = NSM_INVALID_OPERATION;
662 
663     item = cbor_load(req, len, &result);
664     if (!item || result.error.code != CBOR_ERR_NONE) {
665         goto cleanup;
666     }
667 
668     pair = cbor_map_handle(item);
669     if (!cbor_isa_map(pair->value)) {
670         goto cleanup;
671     }
672     size = cbor_map_size(pair->value);
673     if (size < 1) {
674         goto cleanup;
675     }
676 
677     pair = cbor_map_handle(pair->value);
678     for (int i = 0; i < size; ++i) {
679         if (!cbor_isa_string(pair[i].key)) {
680             continue;
681         }
682         str = cbor_string_handle(pair[i].key);
683         if (str && cbor_string_length(pair[i].key) == 5 &&
684             memcmp(str, "index", 5) == 0) {
685             if (!cbor_isa_uint(pair[i].value) ||
686                 cbor_int_get_width(pair[i].value) != CBOR_INT_8) {
687                 break;
688             }
689 
690             nsm_req->index = cbor_get_uint8(pair[i].value);
691             r = NSM_SUCCESS;
692             break;
693         }
694     }
695 
696  cleanup:
697     if (item) {
698         cbor_decref(&item);
699     }
700     return r;
701 }
702 
703 /*
704  * LockPCR success response structure:
705  * {
706  *   String("LockPCR")
707  * }
708  */
709 static bool handle_lock_pcr(VirtIONSM *vnsm, struct iovec *request,
710                             struct iovec *response, Error **errp)
711 {
712     cbor_item_t *root = NULL;
713     size_t len;
714     NSMLockPCRReq nsm_req;
715     enum NSMResponseTypes type;
716     struct PCRInfo *pcr;
717     bool r = false;
718 
719     type = get_nsm_lock_pcr_req(request->iov_base, request->iov_len, &nsm_req);
720     if (type != NSM_SUCCESS) {
721         if (error_response(response, type, errp)) {
722             r = true;
723         }
724         goto cleanup;
725     }
726     if (nsm_req.index >= vnsm->max_pcrs) {
727         if (error_response(response, NSM_INVALID_INDEX, errp)) {
728             r = true;
729         }
730         goto cleanup;
731     }
732 
733     pcr = &(vnsm->pcrs[nsm_req.index]);
734 
735     if (pcr->locked) {
736         if (error_response(response, NSM_READONLY_INDEX, errp)) {
737             r = true;
738         }
739         goto cleanup;
740     }
741 
742     pcr->locked = true;
743 
744     root = cbor_build_string("LockPCR");
745     if (!root) {
746         goto err;
747     }
748 
749     len = cbor_serialize(root, response->iov_base, response->iov_len);
750     if (len == 0) {
751         if (error_response(response, NSM_BUFFER_TOO_SMALL, errp)) {
752             r = true;
753         }
754         goto cleanup;
755     }
756 
757     response->iov_len = len;
758     r = true;
759     goto cleanup;
760 
761  err:
762     error_setg(errp, "Failed to initialize LockPCR response");
763 
764  cleanup:
765     if (root) {
766         cbor_decref(&root);
767     }
768     return r;
769 }
770 
771 /*
772  * LockPCRs request structure:
773  *
774  * {
775  *   Map(1) {
776  *     key = String("LockPCRs"),
777  *     value = Map(1) {
778  *       key = String("range"),
779  *       value = Uint8(pcr)
780  *     }
781  *   }
782  * }
783  */
784 typedef struct NSMLockPCRsReq {
785     uint16_t range;
786 } NSMLockPCRsReq;
787 
788 static enum NSMResponseTypes get_nsm_lock_pcrs_req(uint8_t *req, size_t len,
789                                                    NSMLockPCRsReq *nsm_req)
790 {
791     cbor_item_t *item = NULL;
792     size_t size;
793     uint8_t *str;
794     struct cbor_pair *pair;
795     struct cbor_load_result result;
796     enum NSMResponseTypes r = NSM_INVALID_OPERATION;
797 
798     item = cbor_load(req, len, &result);
799     if (!item || result.error.code != CBOR_ERR_NONE) {
800         goto cleanup;
801     }
802 
803     pair = cbor_map_handle(item);
804     if (!cbor_isa_map(pair->value)) {
805         goto cleanup;
806     }
807     size = cbor_map_size(pair->value);
808     if (size < 1) {
809         goto cleanup;
810     }
811 
812     pair = cbor_map_handle(pair->value);
813     for (int i = 0; i < size; ++i) {
814         if (!cbor_isa_string(pair[i].key)) {
815             continue;
816         }
817         str = cbor_string_handle(pair[i].key);
818         if (str && cbor_string_length(pair[i].key) == 5 &&
819             memcmp(str, "range", 5) == 0) {
820             if (!cbor_isa_uint(pair[i].value) ||
821                 cbor_int_get_width(pair[i].value) != CBOR_INT_8) {
822                 break;
823             }
824 
825             nsm_req->range = cbor_get_uint8(pair[i].value);
826             r = NSM_SUCCESS;
827             break;
828         }
829     }
830 
831  cleanup:
832     if (item) {
833         cbor_decref(&item);
834     }
835     return r;
836 }
837 
838 /*
839  * LockPCRs success response structure:
840  * {
841  *   String("LockPCRs")
842  * }
843  */
844 static bool handle_lock_pcrs(VirtIONSM *vnsm, struct iovec *request,
845                              struct iovec *response, Error **errp)
846 {
847     cbor_item_t *root = NULL;
848     size_t len;
849     NSMLockPCRsReq nsm_req;
850     enum NSMResponseTypes type;
851     bool r = false;
852 
853     type = get_nsm_lock_pcrs_req(request->iov_base, request->iov_len, &nsm_req);
854     if (type != NSM_SUCCESS) {
855         if (error_response(response, type, errp)) {
856             r = true;
857         }
858         goto cleanup;
859     }
860     if (nsm_req.range > vnsm->max_pcrs) {
861         if (error_response(response, NSM_INVALID_INDEX, errp)) {
862             r = true;
863         }
864         goto cleanup;
865     }
866 
867     for (int i = 0; i < nsm_req.range; ++i) {
868         vnsm->pcrs[i].locked = true;
869     }
870 
871     root = cbor_build_string("LockPCRs");
872     if (!root) {
873         goto err;
874     }
875 
876     len = cbor_serialize(root, response->iov_base, response->iov_len);
877     if (len == 0) {
878         if (error_response(response, NSM_BUFFER_TOO_SMALL, errp)) {
879             r = true;
880         }
881         goto cleanup;
882     }
883 
884     response->iov_len = len;
885     r = true;
886     goto cleanup;
887 
888  err:
889     error_setg(errp, "Failed to initialize response");
890 
891  cleanup:
892     if (root) {
893         cbor_decref(&root);
894     }
895     return r;
896 }
897 
898 /*
899  * Attestation request structure:
900  *
901  *   Map(1) {
902  *     key = String("Attestation"),
903  *     value = Map(3) {
904  *       key = String("user_data"),
905  *       value = Byte_String() || null, // Optional
906  *       key = String("nonce"),
907  *       value = Byte_String() || null, // Optional
908  *       key = String("public_key"),
909  *       value = Byte_String() || null, // Optional
910  *     }
911  *   }
912  * }
913  */
914 
915 struct AttestationProperty {
916     bool is_null; /* True if property is not present in map or is null */
917     uint16_t len;
918     uint8_t buf[NSM_REQUEST_MAX_SIZE];
919 };
920 
921 typedef struct NSMAttestationReq {
922     struct AttestationProperty public_key;
923     struct AttestationProperty user_data;
924     struct AttestationProperty nonce;
925 } NSMAttestationReq;
926 
927 static bool fill_attestation_property(struct AttestationProperty *prop,
928                                       cbor_item_t *value)
929 {
930     uint8_t *str;
931     bool ret = false;
932 
933     if (cbor_is_null(value)) {
934         prop->is_null = true;
935         ret = true;
936         goto out;
937     } else if (cbor_isa_bytestring(value)) {
938         str = cbor_bytestring_handle(value);
939         if (!str) {
940             goto out;
941         }
942         prop->len = cbor_bytestring_length(value);
943     } else if (cbor_isa_string(value)) {
944         str = cbor_string_handle(value);
945         if (!str) {
946             goto out;
947         }
948         prop->len = cbor_string_length(value);
949     } else {
950         goto out;
951     }
952 
953     /*
954      * prop->len will be smaller than NSM_REQUEST_MAX_SIZE as we
955      * already check for the max request size before processing
956      * any request. So it's safe to copy.
957      */
958     memcpy(prop->buf, str, prop->len);
959     prop->is_null = false;
960     ret = true;
961 
962  out:
963     return ret;
964 }
965 
966 static enum NSMResponseTypes get_nsm_attestation_req(uint8_t *req, size_t len,
967                                                      NSMAttestationReq *nsm_req)
968 {
969     cbor_item_t *item = NULL;
970     size_t size;
971     uint8_t *str;
972     struct cbor_pair *pair;
973     struct cbor_load_result result;
974     enum NSMResponseTypes r = NSM_INVALID_OPERATION;
975 
976     nsm_req->public_key.is_null = true;
977     nsm_req->user_data.is_null = true;
978     nsm_req->nonce.is_null = true;
979 
980     item = cbor_load(req, len, &result);
981     if (!item || result.error.code != CBOR_ERR_NONE) {
982         goto cleanup;
983     }
984 
985     pair = cbor_map_handle(item);
986     if (!cbor_isa_map(pair->value)) {
987         goto cleanup;
988     }
989     size = cbor_map_size(pair->value);
990     if (size == 0) {
991         r = NSM_SUCCESS;
992         goto cleanup;
993     }
994 
995     pair = cbor_map_handle(pair->value);
996     for (int i = 0; i < size; ++i) {
997         if (!cbor_isa_string(pair[i].key)) {
998             continue;
999         }
1000 
1001         str = cbor_string_handle(pair[i].key);
1002         if (!str) {
1003             continue;
1004         }
1005 
1006         if (cbor_string_length(pair[i].key) == 10 &&
1007             memcmp(str, "public_key", 10) == 0) {
1008             if (!fill_attestation_property(&(nsm_req->public_key),
1009                                            pair[i].value)) {
1010                 goto cleanup;
1011             }
1012             continue;
1013         }
1014 
1015         if (cbor_string_length(pair[i].key) == 9 &&
1016             memcmp(str, "user_data", 9) == 0) {
1017             if (!fill_attestation_property(&(nsm_req->user_data),
1018                                            pair[i].value)) {
1019                 goto cleanup;
1020             }
1021             continue;
1022         }
1023 
1024         if (cbor_string_length(pair[i].key) == 5 &&
1025             memcmp(str, "nonce", 5) == 0) {
1026             if (!fill_attestation_property(&(nsm_req->nonce), pair[i].value)) {
1027                 goto cleanup;
1028             }
1029             continue;
1030         }
1031     }
1032 
1033     r = NSM_SUCCESS;
1034 
1035  cleanup:
1036     if (item) {
1037         cbor_decref(&item);
1038     }
1039     return r;
1040 }
1041 
1042 static bool add_protected_header_to_cose(cbor_item_t *cose)
1043 {
1044     cbor_item_t *map = NULL;
1045     cbor_item_t *key = NULL;
1046     cbor_item_t *value = NULL;
1047     cbor_item_t *bs = NULL;
1048     size_t len;
1049     bool r = false;
1050     size_t buf_len = 4096;
1051     g_autofree uint8_t *buf = g_malloc(buf_len);
1052 
1053     map = cbor_new_definite_map(1);
1054     if (!map) {
1055         goto cleanup;
1056     }
1057     key = cbor_build_uint8(1);
1058     if (!key) {
1059         goto cleanup;
1060     }
1061     value = cbor_new_int8();
1062     if (!value) {
1063         goto cleanup;
1064     }
1065     cbor_mark_negint(value);
1066     /* we don't actually sign the data, so we use -1 as the 'alg' value */
1067     cbor_set_uint8(value, 0);
1068 
1069     if (!qemu_cbor_map_add(map, key, value)) {
1070         goto cleanup;
1071     }
1072 
1073     len = cbor_serialize(map, buf, buf_len);
1074     if (len == 0) {
1075         goto cleanup_map;
1076     }
1077 
1078     bs = cbor_build_bytestring(buf, len);
1079     if (!bs) {
1080         goto cleanup_map;
1081     }
1082     if (!qemu_cbor_array_push(cose, bs)) {
1083         cbor_decref(&bs);
1084         goto cleanup_map;
1085     }
1086     r = true;
1087     goto cleanup_map;
1088 
1089  cleanup:
1090     if (key) {
1091         cbor_decref(&key);
1092     }
1093     if (value) {
1094         cbor_decref(&value);
1095     }
1096 
1097  cleanup_map:
1098     if (map) {
1099         cbor_decref(&map);
1100     }
1101     return r;
1102 }
1103 
1104 static bool add_unprotected_header_to_cose(cbor_item_t *cose)
1105 {
1106     cbor_item_t *map = cbor_new_definite_map(0);
1107     if (!map) {
1108         goto cleanup;
1109     }
1110     if (!qemu_cbor_array_push(cose, map)) {
1111         goto cleanup;
1112     }
1113 
1114     return true;
1115 
1116  cleanup:
1117     if (map) {
1118         cbor_decref(&map);
1119     }
1120     return false;
1121 }
1122 
1123 static bool add_ca_bundle_to_payload(cbor_item_t *map)
1124 {
1125     cbor_item_t *key_cbor = NULL;
1126     cbor_item_t *value_cbor = NULL;
1127     cbor_item_t *bs = NULL;
1128     uint8_t zero[64] = {0};
1129 
1130     key_cbor = cbor_build_string("cabundle");
1131     if (!key_cbor) {
1132         goto cleanup;
1133     }
1134     value_cbor = cbor_new_definite_array(1);
1135     if (!value_cbor) {
1136         goto cleanup;
1137     }
1138     bs = cbor_build_bytestring(zero, 64);
1139     if (!bs) {
1140         goto cleanup;
1141     }
1142     if (!qemu_cbor_array_push(value_cbor, bs)) {
1143         cbor_decref(&bs);
1144         goto cleanup;
1145     }
1146     if (!qemu_cbor_map_add(map, key_cbor, value_cbor)) {
1147         goto cleanup;
1148     }
1149 
1150     return true;
1151 
1152  cleanup:
1153     if (key_cbor) {
1154         cbor_decref(&key_cbor);
1155     }
1156     if (value_cbor) {
1157         cbor_decref(&value_cbor);
1158     }
1159     return false;
1160 }
1161 
1162 static bool add_payload_to_cose(cbor_item_t *cose, VirtIONSM *vnsm,
1163                                 NSMAttestationReq *req)
1164 {
1165     cbor_item_t *root = NULL;
1166     cbor_item_t *nested_map;
1167     cbor_item_t *bs = NULL;
1168     size_t locked_cnt;
1169     uint8_t ind[NSM_MAX_PCRS];
1170     size_t payload_map_size = 9;
1171     size_t len;
1172     struct PCRInfo *pcr;
1173     uint8_t zero[64] = {0};
1174     bool r = false;
1175     size_t buf_len = 16384;
1176     g_autofree uint8_t *buf = g_malloc(buf_len);
1177 
1178     root = cbor_new_definite_map(payload_map_size);
1179     if (!root) {
1180         goto cleanup;
1181     }
1182     if (!qemu_cbor_add_string_to_map(root, "module_id", vnsm->module_id)) {
1183         goto cleanup;
1184     }
1185     if (!qemu_cbor_add_string_to_map(root, "digest", vnsm->digest)) {
1186         goto cleanup;
1187     }
1188     if (!qemu_cbor_add_uint64_to_map(root, "timestamp",
1189                                      (uint64_t) time(NULL) * 1000)) {
1190         goto cleanup;
1191     }
1192 
1193     locked_cnt = 0;
1194     for (uint8_t i = 0; i < NSM_MAX_PCRS; ++i) {
1195         if (vnsm->pcrs[i].locked) {
1196             ind[locked_cnt++] = i;
1197         }
1198     }
1199     if (!qemu_cbor_add_map_to_map(root, "pcrs", locked_cnt, &nested_map)) {
1200         goto cleanup;
1201     }
1202     for (uint8_t i = 0; i < locked_cnt; ++i) {
1203         pcr = &(vnsm->pcrs[ind[i]]);
1204         if (!qemu_cbor_add_uint8_key_bytestring_to_map(
1205                 nested_map, ind[i],
1206                 pcr->data,
1207                 QCRYPTO_HASH_DIGEST_LEN_SHA384)) {
1208             goto cleanup;
1209         }
1210     }
1211     if (!qemu_cbor_add_bytestring_to_map(root, "certificate", zero, 64)) {
1212         goto cleanup;
1213     }
1214     if (!add_ca_bundle_to_payload(root)) {
1215         goto cleanup;
1216     }
1217 
1218     if (req->public_key.is_null) {
1219         if (!qemu_cbor_add_null_to_map(root, "public_key")) {
1220             goto cleanup;
1221         }
1222     } else if (!qemu_cbor_add_bytestring_to_map(root, "public_key",
1223                                                 req->public_key.buf,
1224                                                 req->public_key.len)) {
1225         goto cleanup;
1226     }
1227 
1228     if (req->user_data.is_null) {
1229         if (!qemu_cbor_add_null_to_map(root, "user_data")) {
1230             goto cleanup;
1231         }
1232     } else if (!qemu_cbor_add_bytestring_to_map(root, "user_data",
1233                                                 req->user_data.buf,
1234                                                 req->user_data.len)) {
1235             goto cleanup;
1236     }
1237 
1238     if (req->nonce.is_null) {
1239         if (!qemu_cbor_add_null_to_map(root, "nonce")) {
1240             goto cleanup;
1241         }
1242     } else if (!qemu_cbor_add_bytestring_to_map(root, "nonce",
1243                                                 req->nonce.buf,
1244                                                 req->nonce.len)) {
1245         goto cleanup;
1246     }
1247 
1248     len = cbor_serialize(root, buf, buf_len);
1249     if (len == 0) {
1250         goto cleanup;
1251     }
1252 
1253     bs = cbor_build_bytestring(buf, len);
1254     if (!bs) {
1255         goto cleanup;
1256     }
1257     if (!qemu_cbor_array_push(cose, bs)) {
1258         cbor_decref(&bs);
1259         goto cleanup;
1260     }
1261 
1262     r = true;
1263 
1264  cleanup:
1265     if (root) {
1266         cbor_decref(&root);
1267     }
1268     return r;
1269 }
1270 
1271 static bool add_signature_to_cose(cbor_item_t *cose)
1272 {
1273     cbor_item_t *bs = NULL;
1274     uint8_t zero[64] = {0};
1275 
1276     /* we don't actually sign the data, so we just put 64 zero bytes */
1277     bs = cbor_build_bytestring(zero, 64);
1278     if (!bs) {
1279         goto cleanup;
1280     }
1281 
1282     if (!qemu_cbor_array_push(cose, bs)) {
1283         goto cleanup;
1284     }
1285 
1286     return true;
1287 
1288  cleanup:
1289     if (bs) {
1290         cbor_decref(&bs);
1291     }
1292     return false;
1293 }
1294 
1295 /*
1296  * Attestation response structure:
1297  *
1298  * {
1299  *   Map(1) {
1300  *     key = String("Attestation"),
1301  *     value = Map(1) {
1302  *       key = String("document"),
1303  *       value = Byte_String()
1304  *     }
1305  *   }
1306  * }
1307  *
1308  * The document is a serialized COSE sign1 blob of the structure:
1309  * {
1310  *   Array(4) {
1311  *     [0] { ByteString() }, // serialized protected header
1312  *     [1] { Map(0) },       // 0 length map
1313  *     [2] { ByteString() }, // serialized payload
1314  *     [3] { ByteString() }, // signature
1315  *   }
1316  * }
1317  *
1318  * where [0] protected header is a serialized CBOR blob of the structure:
1319  * {
1320  *   Map(1) {
1321  *     key = Uint8(1)         // alg
1322  *     value = NegativeInt8() // Signing algorithm
1323  *   }
1324  * }
1325  *
1326  * [2] payload is serialized CBOR blob of the structure:
1327  * {
1328  *   Map(9) {
1329  *     [0] { key = String("module_id"), value = String(module_id) },
1330  *     [1] { key = String("digest"), value = String("SHA384") },
1331  *     [2] {
1332  *           key = String("timestamp"),
1333  *           value = Uint64(unix epoch of  when document was created)
1334  *         },
1335  *     [3] {
1336  *           key = String("pcrs"),
1337  *           value = Map(locked_pcr_cnt) {
1338  *                       key = Uint8(pcr_index),
1339  *                       value = ByteString(pcr_data)
1340  *                   },
1341  *         },
1342  *     [4] {
1343  *           key = String("certificate"),
1344  *           value = ByteString(Signing certificate)
1345  *         },
1346  *     [5] { key = String("cabundle"), value = Array(N) { ByteString()... } },
1347  *     [6] { key = String("public_key"), value = ByteString() || null },
1348  *     [7] { key = String("user_data"), value = ByteString() || null},
1349  *     [8] { key = String("nonce"), value = ByteString() || null},
1350  *   }
1351  * }
1352  */
1353 static bool handle_attestation(VirtIONSM *vnsm, struct iovec *request,
1354                                struct iovec *response, Error **errp)
1355 {
1356     cbor_item_t *root = NULL;
1357     cbor_item_t *cose = NULL;
1358     cbor_item_t *nested_map;
1359     size_t len;
1360     enum NSMResponseTypes type;
1361     bool r = false;
1362     size_t buf_len = 16384;
1363     g_autofree uint8_t *buf = g_malloc(buf_len);
1364     g_autofree NSMAttestationReq *nsm_req = g_malloc(sizeof(NSMAttestationReq));
1365 
1366     nsm_req->public_key.is_null = true;
1367     nsm_req->user_data.is_null = true;
1368     nsm_req->nonce.is_null = true;
1369 
1370     type = get_nsm_attestation_req(request->iov_base, request->iov_len,
1371                                    nsm_req);
1372     if (type != NSM_SUCCESS) {
1373         if (error_response(response, type, errp)) {
1374             r = true;
1375         }
1376         goto out;
1377     }
1378 
1379     cose = cbor_new_definite_array(4);
1380     if (!cose) {
1381         goto err;
1382     }
1383     if (!add_protected_header_to_cose(cose)) {
1384         goto err;
1385     }
1386     if (!add_unprotected_header_to_cose(cose)) {
1387         goto err;
1388     }
1389     if (!add_payload_to_cose(cose, vnsm, nsm_req)) {
1390         goto err;
1391     }
1392     if (!add_signature_to_cose(cose)) {
1393         goto err;
1394     }
1395 
1396     len = cbor_serialize(cose, buf, buf_len);
1397     if (len == 0) {
1398         goto err;
1399     }
1400 
1401     root = cbor_new_definite_map(1);
1402     if (!root) {
1403         goto err;
1404     }
1405     if (!qemu_cbor_add_map_to_map(root, "Attestation", 1, &nested_map)) {
1406         goto err;
1407     }
1408     if (!qemu_cbor_add_bytestring_to_map(nested_map, "document", buf, len)) {
1409         goto err;
1410     }
1411 
1412     len = cbor_serialize(root, response->iov_base, response->iov_len);
1413     if (len == 0) {
1414         if (error_response(response, NSM_INPUT_TOO_LARGE, errp)) {
1415             r = true;
1416         }
1417         goto out;
1418     }
1419 
1420     response->iov_len = len;
1421     r = true;
1422 
1423  out:
1424     if (root) {
1425         cbor_decref(&root);
1426     }
1427     if (cose) {
1428         cbor_decref(&cose);
1429     }
1430     return r;
1431 
1432  err:
1433     error_setg(errp, "Failed to initialize Attestation response");
1434     goto out;
1435 }
1436 
1437 enum CBOR_ROOT_TYPE {
1438     CBOR_ROOT_TYPE_STRING = 0,
1439     CBOR_ROOT_TYPE_MAP = 1,
1440 };
1441 
1442 struct nsm_cmd {
1443     char name[16];
1444     /*
1445      * There are 2 types of request
1446      * 1) String(); "GetRandom", "DescribeNSM"
1447      * 2) Map(1) { key: String(), value: ... }
1448      */
1449     enum CBOR_ROOT_TYPE root_type;
1450     bool (*response_fn)(VirtIONSM *vnsm, struct iovec *request,
1451                         struct iovec *response, Error **errp);
1452 };
1453 
1454 const struct nsm_cmd nsm_cmds[] = {
1455     { "GetRandom",   CBOR_ROOT_TYPE_STRING,  handle_get_random },
1456     { "DescribeNSM", CBOR_ROOT_TYPE_STRING,  handle_describe_nsm },
1457     { "DescribePCR", CBOR_ROOT_TYPE_MAP,     handle_describe_pcr },
1458     { "ExtendPCR",   CBOR_ROOT_TYPE_MAP,     handle_extend_pcr },
1459     { "LockPCR",     CBOR_ROOT_TYPE_MAP,     handle_lock_pcr },
1460     { "LockPCRs",    CBOR_ROOT_TYPE_MAP,     handle_lock_pcrs },
1461     { "Attestation", CBOR_ROOT_TYPE_MAP,     handle_attestation },
1462 };
1463 
1464 static const struct nsm_cmd *get_nsm_request_cmd(uint8_t *buf, size_t len)
1465 {
1466     size_t size;
1467     uint8_t *req;
1468     enum CBOR_ROOT_TYPE root_type;
1469     struct cbor_load_result result;
1470     cbor_item_t *item = cbor_load(buf, len, &result);
1471     if (!item || result.error.code != CBOR_ERR_NONE) {
1472         goto cleanup;
1473     }
1474 
1475     if (cbor_isa_string(item)) {
1476         size = cbor_string_length(item);
1477         req = cbor_string_handle(item);
1478         root_type = CBOR_ROOT_TYPE_STRING;
1479     } else if (cbor_isa_map(item) && cbor_map_size(item) == 1) {
1480         struct cbor_pair *handle = cbor_map_handle(item);
1481         if (cbor_isa_string(handle->key)) {
1482             size = cbor_string_length(handle->key);
1483             req = cbor_string_handle(handle->key);
1484             root_type = CBOR_ROOT_TYPE_MAP;
1485         } else {
1486             goto cleanup;
1487         }
1488     } else {
1489         goto cleanup;
1490     }
1491 
1492     if  (size == 0 || req == NULL) {
1493         goto cleanup;
1494     }
1495 
1496     for (int i = 0; i < ARRAY_SIZE(nsm_cmds); ++i) {
1497         if (nsm_cmds[i].root_type == root_type &&
1498             strlen(nsm_cmds[i].name) == size &&
1499             memcmp(nsm_cmds[i].name, req, size) == 0) {
1500             cbor_decref(&item);
1501             return &nsm_cmds[i];
1502         }
1503     }
1504 
1505  cleanup:
1506     if (item) {
1507         cbor_decref(&item);
1508     }
1509     return NULL;
1510 }
1511 
1512 static bool get_nsm_request_response(VirtIONSM *vnsm, struct iovec *req,
1513                                      struct iovec *resp, Error **errp)
1514 {
1515     const struct nsm_cmd *cmd;
1516 
1517     if (req->iov_len > NSM_REQUEST_MAX_SIZE) {
1518         if (error_response(resp, NSM_INPUT_TOO_LARGE, errp)) {
1519             return true;
1520         }
1521         error_setg(errp, "Failed to initialize InputTooLarge response");
1522         return false;
1523     }
1524 
1525     cmd = get_nsm_request_cmd(req->iov_base, req->iov_len);
1526 
1527     if (cmd == NULL) {
1528         if (error_response(resp, NSM_INVALID_OPERATION, errp)) {
1529             return true;
1530         }
1531         error_setg(errp, "Failed to initialize InvalidOperation response");
1532         return false;
1533     }
1534 
1535     return cmd->response_fn(vnsm, req, resp, errp);
1536 }
1537 
1538 static void handle_input(VirtIODevice *vdev, VirtQueue *vq)
1539 {
1540     g_autofree VirtQueueElement *out_elem = NULL;
1541     g_autofree VirtQueueElement *in_elem = NULL;
1542     VirtIONSM *vnsm = VIRTIO_NSM(vdev);
1543     Error *err = NULL;
1544     size_t sz;
1545     struct iovec req = {.iov_base = NULL, .iov_len = 0};
1546     struct iovec res = {.iov_base = NULL, .iov_len = 0};
1547 
1548     out_elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
1549     if (!out_elem) {
1550         /* nothing in virtqueue */
1551         return;
1552     }
1553 
1554     sz = iov_size(out_elem->out_sg, out_elem->out_num);
1555     if (sz == 0) {
1556         virtio_error(vdev, "Expected non-zero sized request buffer in "
1557                      "virtqueue");
1558         goto cleanup;
1559     }
1560 
1561     in_elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
1562     if (!in_elem) {
1563         virtio_error(vdev, "Expected response buffer after request buffer "
1564                      "in virtqueue");
1565         goto cleanup;
1566     }
1567     if (iov_size(in_elem->in_sg, in_elem->in_num) != NSM_RESPONSE_BUF_SIZE) {
1568         virtio_error(vdev, "Expected response buffer of length 0x3000");
1569         goto cleanup;
1570     }
1571 
1572     req.iov_base = g_malloc(sz);
1573     req.iov_len = iov_to_buf(out_elem->out_sg, out_elem->out_num, 0,
1574                              req.iov_base, sz);
1575     if (req.iov_len != sz) {
1576         virtio_error(vdev, "Failed to copy request buffer");
1577         goto cleanup;
1578     }
1579 
1580     res.iov_base = g_malloc(NSM_RESPONSE_BUF_SIZE);
1581     res.iov_len = NSM_RESPONSE_BUF_SIZE;
1582 
1583     if (!get_nsm_request_response(vnsm, &req, &res, &err)) {
1584         error_report_err(err);
1585         virtio_error(vdev, "Failed to get NSM request response");
1586         goto cleanup;
1587     }
1588 
1589     sz = iov_from_buf(in_elem->in_sg, in_elem->in_num, 0, res.iov_base,
1590                       res.iov_len);
1591     if (sz != res.iov_len) {
1592         virtio_error(vdev, "Failed to copy response buffer");
1593         goto cleanup;
1594     }
1595 
1596     g_free(req.iov_base);
1597     g_free(res.iov_base);
1598     virtqueue_push(vq, out_elem, 0);
1599     virtqueue_push(vq, in_elem, sz);
1600     virtio_notify(vdev, vq);
1601     return;
1602 
1603  cleanup:
1604     g_free(req.iov_base);
1605     g_free(res.iov_base);
1606     if (out_elem) {
1607         virtqueue_detach_element(vq, out_elem, 0);
1608     }
1609     if (in_elem) {
1610         virtqueue_detach_element(vq, in_elem, 0);
1611     }
1612 }
1613 
1614 static uint64_t get_features(VirtIODevice *vdev, uint64_t f, Error **errp)
1615 {
1616     return f;
1617 }
1618 
1619 static bool extend_pcr(VirtIONSM *vnsm, int ind, uint8_t *data, uint16_t len)
1620 {
1621     Error *err = NULL;
1622     struct PCRInfo *pcr = &(vnsm->pcrs[ind]);
1623     size_t digest_len = QCRYPTO_HASH_DIGEST_LEN_SHA384;
1624     uint8_t result[QCRYPTO_HASH_DIGEST_LEN_SHA384];
1625     uint8_t *ptr = result;
1626     struct iovec iov[2] = {
1627         { .iov_base = pcr->data, .iov_len = QCRYPTO_HASH_DIGEST_LEN_SHA384 },
1628         { .iov_base = data, .iov_len = len },
1629     };
1630 
1631     if (qcrypto_hash_bytesv(QCRYPTO_HASH_ALGO_SHA384, iov, 2, &ptr, &digest_len,
1632                             &err) < 0) {
1633         return false;
1634     }
1635 
1636     memcpy(pcr->data, result, QCRYPTO_HASH_DIGEST_LEN_SHA384);
1637     return true;
1638 }
1639 
1640 static void lock_pcr(VirtIONSM *vnsm, int ind)
1641 {
1642     vnsm->pcrs[ind].locked = true;
1643 }
1644 
1645 static void virtio_nsm_device_realize(DeviceState *dev, Error **errp)
1646 {
1647     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
1648     VirtIONSM *vnsm = VIRTIO_NSM(dev);
1649 
1650     vnsm->max_pcrs = NSM_MAX_PCRS;
1651     vnsm->digest = (char *) "SHA384";
1652     if (vnsm->module_id == NULL) {
1653         vnsm->module_id = (char *) "i-234-enc5678";
1654     }
1655     vnsm->version_major = 1;
1656     vnsm->version_minor = 0;
1657     vnsm->version_patch = 0;
1658     vnsm->extend_pcr = extend_pcr;
1659     vnsm->lock_pcr = lock_pcr;
1660 
1661     virtio_init(vdev, VIRTIO_ID_NITRO_SEC_MOD, 0);
1662 
1663     vnsm->vq = virtio_add_queue(vdev, 2, handle_input);
1664 }
1665 
1666 static void virtio_nsm_device_unrealize(DeviceState *dev)
1667 {
1668     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
1669 
1670     virtio_del_queue(vdev, 0);
1671     virtio_cleanup(vdev);
1672 }
1673 
1674 static const VMStateDescription vmstate_pcr_info_entry = {
1675     .name = "pcr_info_entry",
1676     .minimum_version_id = 1,
1677     .version_id = 1,
1678     .fields = (const VMStateField[]) {
1679         VMSTATE_BOOL(locked, struct PCRInfo),
1680         VMSTATE_UINT8_ARRAY(data, struct PCRInfo,
1681                             QCRYPTO_HASH_DIGEST_LEN_SHA384),
1682         VMSTATE_END_OF_LIST()
1683     },
1684 };
1685 
1686 static const VMStateDescription vmstate_virtio_nsm_device = {
1687     .name = "virtio-nsm-device",
1688     .minimum_version_id = 1,
1689     .version_id = 1,
1690     .fields = (const VMStateField[]) {
1691         VMSTATE_STRUCT_ARRAY(pcrs, VirtIONSM, NSM_MAX_PCRS, 1,
1692                              vmstate_pcr_info_entry, struct PCRInfo),
1693         VMSTATE_END_OF_LIST()
1694     },
1695 };
1696 
1697 static const VMStateDescription vmstate_virtio_nsm = {
1698     .name = "virtio-nsm",
1699     .minimum_version_id = 1,
1700     .version_id = 1,
1701     .fields = (const VMStateField[]) {
1702         VMSTATE_VIRTIO_DEVICE,
1703         VMSTATE_END_OF_LIST()
1704     },
1705 };
1706 
1707 static const Property virtio_nsm_properties[] = {
1708     DEFINE_PROP_STRING("module-id", VirtIONSM, module_id),
1709 };
1710 
1711 static void virtio_nsm_class_init(ObjectClass *klass, const void *data)
1712 {
1713     DeviceClass *dc = DEVICE_CLASS(klass);
1714     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
1715 
1716     device_class_set_props(dc, virtio_nsm_properties);
1717     dc->vmsd = &vmstate_virtio_nsm;
1718     set_bit(DEVICE_CATEGORY_MISC, dc->categories);
1719     vdc->realize = virtio_nsm_device_realize;
1720     vdc->unrealize = virtio_nsm_device_unrealize;
1721     vdc->get_features = get_features;
1722     vdc->vmsd = &vmstate_virtio_nsm_device;
1723 }
1724 
1725 static const TypeInfo virtio_nsm_info = {
1726     .name = TYPE_VIRTIO_NSM,
1727     .parent = TYPE_VIRTIO_DEVICE,
1728     .instance_size = sizeof(VirtIONSM),
1729     .class_init = virtio_nsm_class_init,
1730 };
1731 
1732 static void virtio_register_types(void)
1733 {
1734     type_register_static(&virtio_nsm_info);
1735 }
1736 
1737 type_init(virtio_register_types)
1738