xref: /openbmc/qemu/hw/virtio/virtio-nsm.c (revision 7d87775f)
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),
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                 goto cleanup;
509             }
510             str = cbor_bytestring_handle(pair[i].value);
511             if (!str) {
512                 goto cleanup;
513             }
514             nsm_req->data_len = cbor_bytestring_length(pair[i].value);
515             /*
516              * nsm_req->data_len will be smaller than NSM_REQUEST_MAX_SIZE as
517              * we already check for the max request size before processing
518              * any request. So it's safe to copy.
519              */
520             memcpy(nsm_req->data, str, nsm_req->data_len);
521             data_found = true;
522             continue;
523         }
524     }
525 
526     if (index_found && data_found) {
527         r = NSM_SUCCESS;
528     }
529 
530  cleanup:
531     if (item) {
532         cbor_decref(&item);
533     }
534     return r;
535 }
536 
537 /*
538  * ExtendPCR response structure:
539  *
540  * {
541  *   Map(1) {
542  *     key = String("ExtendPCR"),
543  *     value = Map(1) {
544  *       key = String("data"),
545  *       value = Byte_String()
546  *     }
547  *   }
548  * }
549  */
550 static bool handle_extend_pcr(VirtIONSM *vnsm, struct iovec *request,
551                               struct iovec *response, Error **errp)
552 {
553     cbor_item_t *root = NULL;
554     cbor_item_t *nested_map;
555     size_t len;
556     struct PCRInfo *pcr;
557     enum NSMResponseTypes type;
558     bool r = false;
559     g_autofree NSMExtendPCRReq *nsm_req = g_malloc(sizeof(NSMExtendPCRReq));
560 
561     type = get_nsm_extend_pcr_req(request->iov_base, request->iov_len,
562                                   nsm_req);
563     if (type != NSM_SUCCESS) {
564         if (error_response(response, type, errp)) {
565             r = true;
566         }
567         goto out;
568     }
569     if (nsm_req->index >= vnsm->max_pcrs) {
570         if (error_response(response, NSM_INVALID_INDEX, errp)) {
571             r = true;
572         }
573         goto out;
574     }
575 
576     pcr = &(vnsm->pcrs[nsm_req->index]);
577 
578     if (pcr->locked) {
579         if (error_response(response, NSM_READONLY_INDEX, errp)) {
580             r = true;
581         }
582         goto out;
583     }
584 
585     if (!vnsm->extend_pcr(vnsm, nsm_req->index, nsm_req->data,
586                           nsm_req->data_len)) {
587         if (error_response(response, NSM_INTERNAL_ERROR, errp)) {
588             r = true;
589         }
590         goto out;
591     }
592 
593     root = cbor_new_definite_map(1);
594     if (!root) {
595         goto err;
596     }
597 
598     if (!qemu_cbor_add_map_to_map(root, "ExtendPCR", 1, &nested_map)) {
599         goto err;
600     }
601 
602     if (!qemu_cbor_add_bytestring_to_map(nested_map, "data", pcr->data,
603                                          QCRYPTO_HASH_DIGEST_LEN_SHA384)) {
604         goto err;
605     }
606 
607     len = cbor_serialize(root, response->iov_base, response->iov_len);
608     if (len == 0) {
609         if (error_response(response, NSM_BUFFER_TOO_SMALL, errp)) {
610             r = true;
611         }
612         goto out;
613     }
614 
615     response->iov_len = len;
616     r = true;
617 
618  out:
619     if (root) {
620         cbor_decref(&root);
621     }
622     return r;
623 
624  err:
625     error_setg(errp, "Failed to initialize DescribePCR response");
626     goto out;
627 }
628 
629 /*
630  * LockPCR request structure:
631  *
632  * {
633  *   Map(1) {
634  *     key = String("LockPCR"),
635  *     value = Map(1) {
636  *       key = String("index"),
637  *       value = Uint8(pcr)
638  *     }
639  *   }
640  * }
641  */
642 typedef struct NSMLockPCRReq {
643     uint8_t index;
644 } NSMLockPCRReq;
645 
646 static enum NSMResponseTypes get_nsm_lock_pcr_req(uint8_t *req, size_t len,
647                                                   NSMLockPCRReq *nsm_req)
648 {
649     cbor_item_t *item = NULL;
650     size_t size;
651     uint8_t *str;
652     struct cbor_pair *pair;
653     struct cbor_load_result result;
654     enum NSMResponseTypes r = NSM_INVALID_OPERATION;
655 
656     item = cbor_load(req, len, &result);
657     if (!item || result.error.code != CBOR_ERR_NONE) {
658         goto cleanup;
659     }
660 
661     pair = cbor_map_handle(item);
662     if (!cbor_isa_map(pair->value)) {
663         goto cleanup;
664     }
665     size = cbor_map_size(pair->value);
666     if (size < 1) {
667         goto cleanup;
668     }
669 
670     pair = cbor_map_handle(pair->value);
671     for (int i = 0; i < size; ++i) {
672         if (!cbor_isa_string(pair[i].key)) {
673             continue;
674         }
675         str = cbor_string_handle(pair[i].key);
676         if (str && cbor_string_length(pair[i].key) == 5 &&
677             memcmp(str, "index", 5) == 0) {
678             if (!cbor_isa_uint(pair[i].value) ||
679                 cbor_int_get_width(pair[i].value) != CBOR_INT_8) {
680                 break;
681             }
682 
683             nsm_req->index = cbor_get_uint8(pair[i].value);
684             r = NSM_SUCCESS;
685             break;
686         }
687     }
688 
689  cleanup:
690     if (item) {
691         cbor_decref(&item);
692     }
693     return r;
694 }
695 
696 /*
697  * LockPCR success response structure:
698  * {
699  *   String("LockPCR")
700  * }
701  */
702 static bool handle_lock_pcr(VirtIONSM *vnsm, struct iovec *request,
703                             struct iovec *response, Error **errp)
704 {
705     cbor_item_t *root = NULL;
706     size_t len;
707     NSMLockPCRReq nsm_req;
708     enum NSMResponseTypes type;
709     struct PCRInfo *pcr;
710     bool r = false;
711 
712     type = get_nsm_lock_pcr_req(request->iov_base, request->iov_len, &nsm_req);
713     if (type != NSM_SUCCESS) {
714         if (error_response(response, type, errp)) {
715             r = true;
716         }
717         goto cleanup;
718     }
719     if (nsm_req.index >= vnsm->max_pcrs) {
720         if (error_response(response, NSM_INVALID_INDEX, errp)) {
721             r = true;
722         }
723         goto cleanup;
724     }
725 
726     pcr = &(vnsm->pcrs[nsm_req.index]);
727 
728     if (pcr->locked) {
729         if (error_response(response, NSM_READONLY_INDEX, errp)) {
730             r = true;
731         }
732         goto cleanup;
733     }
734 
735     pcr->locked = true;
736 
737     root = cbor_build_string("LockPCR");
738     if (!root) {
739         goto err;
740     }
741 
742     len = cbor_serialize(root, response->iov_base, response->iov_len);
743     if (len == 0) {
744         if (error_response(response, NSM_BUFFER_TOO_SMALL, errp)) {
745             r = true;
746         }
747         goto cleanup;
748     }
749 
750     response->iov_len = len;
751     r = true;
752     goto cleanup;
753 
754  err:
755     error_setg(errp, "Failed to initialize LockPCR response");
756 
757  cleanup:
758     if (root) {
759         cbor_decref(&root);
760     }
761     return r;
762 }
763 
764 /*
765  * LockPCRs request structure:
766  *
767  * {
768  *   Map(1) {
769  *     key = String("LockPCRs"),
770  *     value = Map(1) {
771  *       key = String("range"),
772  *       value = Uint8(pcr)
773  *     }
774  *   }
775  * }
776  */
777 typedef struct NSMLockPCRsReq {
778     uint16_t range;
779 } NSMLockPCRsReq;
780 
781 static enum NSMResponseTypes get_nsm_lock_pcrs_req(uint8_t *req, size_t len,
782                                                    NSMLockPCRsReq *nsm_req)
783 {
784     cbor_item_t *item = NULL;
785     size_t size;
786     uint8_t *str;
787     struct cbor_pair *pair;
788     struct cbor_load_result result;
789     enum NSMResponseTypes r = NSM_INVALID_OPERATION;
790 
791     item = cbor_load(req, len, &result);
792     if (!item || result.error.code != CBOR_ERR_NONE) {
793         goto cleanup;
794     }
795 
796     pair = cbor_map_handle(item);
797     if (!cbor_isa_map(pair->value)) {
798         goto cleanup;
799     }
800     size = cbor_map_size(pair->value);
801     if (size < 1) {
802         goto cleanup;
803     }
804 
805     pair = cbor_map_handle(pair->value);
806     for (int i = 0; i < size; ++i) {
807         if (!cbor_isa_string(pair[i].key)) {
808             continue;
809         }
810         str = cbor_string_handle(pair[i].key);
811         if (str && cbor_string_length(pair[i].key) == 5 &&
812             memcmp(str, "range", 5) == 0) {
813             if (!cbor_isa_uint(pair[i].value) ||
814                 cbor_int_get_width(pair[i].value) != CBOR_INT_8) {
815                 break;
816             }
817 
818             nsm_req->range = cbor_get_uint8(pair[i].value);
819             r = NSM_SUCCESS;
820             break;
821         }
822     }
823 
824  cleanup:
825     if (item) {
826         cbor_decref(&item);
827     }
828     return r;
829 }
830 
831 /*
832  * LockPCRs success response structure:
833  * {
834  *   String("LockPCRs")
835  * }
836  */
837 static bool handle_lock_pcrs(VirtIONSM *vnsm, struct iovec *request,
838                              struct iovec *response, Error **errp)
839 {
840     cbor_item_t *root = NULL;
841     size_t len;
842     NSMLockPCRsReq nsm_req;
843     enum NSMResponseTypes type;
844     bool r = false;
845 
846     type = get_nsm_lock_pcrs_req(request->iov_base, request->iov_len, &nsm_req);
847     if (type != NSM_SUCCESS) {
848         if (error_response(response, type, errp)) {
849             r = true;
850         }
851         goto cleanup;
852     }
853     if (nsm_req.range > vnsm->max_pcrs) {
854         if (error_response(response, NSM_INVALID_INDEX, errp)) {
855             r = true;
856         }
857         goto cleanup;
858     }
859 
860     for (int i = 0; i < nsm_req.range; ++i) {
861         vnsm->pcrs[i].locked = true;
862     }
863 
864     root = cbor_build_string("LockPCRs");
865     if (!root) {
866         goto err;
867     }
868 
869     len = cbor_serialize(root, response->iov_base, response->iov_len);
870     if (len == 0) {
871         if (error_response(response, NSM_BUFFER_TOO_SMALL, errp)) {
872             r = true;
873         }
874         goto cleanup;
875     }
876 
877     response->iov_len = len;
878     r = true;
879     goto cleanup;
880 
881  err:
882     error_setg(errp, "Failed to initialize response");
883 
884  cleanup:
885     if (root) {
886         cbor_decref(&root);
887     }
888     return r;
889 }
890 
891 /*
892  * Attestation request structure:
893  *
894  *   Map(1) {
895  *     key = String("Attestation"),
896  *     value = Map(3) {
897  *       key = String("user_data"),
898  *       value = Byte_String() || null, // Optional
899  *       key = String("nonce"),
900  *       value = Byte_String() || null, // Optional
901  *       key = String("public_key"),
902  *       value = Byte_String() || null, // Optional
903  *     }
904  *   }
905  * }
906  */
907 
908 struct AttestationProperty {
909     bool is_null; /* True if property is not present in map or is null */
910     uint16_t len;
911     uint8_t buf[NSM_REQUEST_MAX_SIZE];
912 };
913 
914 typedef struct NSMAttestationReq {
915     struct AttestationProperty public_key;
916     struct AttestationProperty user_data;
917     struct AttestationProperty nonce;
918 } NSMAttestationReq;
919 
920 static bool fill_attestation_property(struct AttestationProperty *prop,
921                                       cbor_item_t *value)
922 {
923     uint8_t *str;
924     bool ret = false;
925 
926     if (cbor_is_null(value)) {
927         prop->is_null = true;
928         ret = true;
929         goto out;
930     } else if (cbor_isa_bytestring(value)) {
931         str = cbor_bytestring_handle(value);
932         if (!str) {
933             goto out;
934         }
935         prop->len = cbor_bytestring_length(value);
936     } else if (cbor_isa_string(value)) {
937         str = cbor_string_handle(value);
938         if (!str) {
939             goto out;
940         }
941         prop->len = cbor_string_length(value);
942     } else {
943         goto out;
944     }
945 
946     /*
947      * prop->len will be smaller than NSM_REQUEST_MAX_SIZE as we
948      * already check for the max request size before processing
949      * any request. So it's safe to copy.
950      */
951     memcpy(prop->buf, str, prop->len);
952     prop->is_null = false;
953     ret = true;
954 
955  out:
956     return ret;
957 }
958 
959 static enum NSMResponseTypes get_nsm_attestation_req(uint8_t *req, size_t len,
960                                                      NSMAttestationReq *nsm_req)
961 {
962     cbor_item_t *item = NULL;
963     size_t size;
964     uint8_t *str;
965     struct cbor_pair *pair;
966     struct cbor_load_result result;
967     enum NSMResponseTypes r = NSM_INVALID_OPERATION;
968 
969     nsm_req->public_key.is_null = true;
970     nsm_req->user_data.is_null = true;
971     nsm_req->nonce.is_null = true;
972 
973     item = cbor_load(req, len, &result);
974     if (!item || result.error.code != CBOR_ERR_NONE) {
975         goto cleanup;
976     }
977 
978     pair = cbor_map_handle(item);
979     if (!cbor_isa_map(pair->value)) {
980         goto cleanup;
981     }
982     size = cbor_map_size(pair->value);
983     if (size == 0) {
984         r = NSM_SUCCESS;
985         goto cleanup;
986     }
987 
988     pair = cbor_map_handle(pair->value);
989     for (int i = 0; i < size; ++i) {
990         if (!cbor_isa_string(pair[i].key)) {
991             continue;
992         }
993 
994         str = cbor_string_handle(pair[i].key);
995         if (!str) {
996             continue;
997         }
998 
999         if (cbor_string_length(pair[i].key) == 10 &&
1000             memcmp(str, "public_key", 10) == 0) {
1001             if (!fill_attestation_property(&(nsm_req->public_key),
1002                                            pair[i].value)) {
1003                 goto cleanup;
1004             }
1005             continue;
1006         }
1007 
1008         if (cbor_string_length(pair[i].key) == 9 &&
1009             memcmp(str, "user_data", 9) == 0) {
1010             if (!fill_attestation_property(&(nsm_req->user_data),
1011                                            pair[i].value)) {
1012                 goto cleanup;
1013             }
1014             continue;
1015         }
1016 
1017         if (cbor_string_length(pair[i].key) == 5 &&
1018             memcmp(str, "nonce", 5) == 0) {
1019             if (!fill_attestation_property(&(nsm_req->nonce), pair[i].value)) {
1020                 goto cleanup;
1021             }
1022             continue;
1023         }
1024     }
1025 
1026     r = NSM_SUCCESS;
1027 
1028  cleanup:
1029     if (item) {
1030         cbor_decref(&item);
1031     }
1032     return r;
1033 }
1034 
1035 static bool add_protected_header_to_cose(cbor_item_t *cose)
1036 {
1037     cbor_item_t *map = NULL;
1038     cbor_item_t *key = NULL;
1039     cbor_item_t *value = NULL;
1040     cbor_item_t *bs = NULL;
1041     size_t len;
1042     bool r = false;
1043     size_t buf_len = 4096;
1044     g_autofree uint8_t *buf = g_malloc(buf_len);
1045 
1046     map = cbor_new_definite_map(1);
1047     if (!map) {
1048         goto cleanup;
1049     }
1050     key = cbor_build_uint8(1);
1051     if (!key) {
1052         goto cleanup;
1053     }
1054     value = cbor_new_int8();
1055     if (!value) {
1056         goto cleanup;
1057     }
1058     cbor_mark_negint(value);
1059     /* we don't actually sign the data, so we use -1 as the 'alg' value */
1060     cbor_set_uint8(value, 0);
1061 
1062     if (!qemu_cbor_map_add(map, key, value)) {
1063         goto cleanup;
1064     }
1065 
1066     len = cbor_serialize(map, buf, buf_len);
1067     if (len == 0) {
1068         goto cleanup_map;
1069     }
1070 
1071     bs = cbor_build_bytestring(buf, len);
1072     if (!bs) {
1073         goto cleanup_map;
1074     }
1075     if (!qemu_cbor_array_push(cose, bs)) {
1076         cbor_decref(&bs);
1077         goto cleanup_map;
1078     }
1079     r = true;
1080     goto cleanup_map;
1081 
1082  cleanup:
1083     if (key) {
1084         cbor_decref(&key);
1085     }
1086     if (value) {
1087         cbor_decref(&value);
1088     }
1089 
1090  cleanup_map:
1091     if (map) {
1092         cbor_decref(&map);
1093     }
1094     return r;
1095 }
1096 
1097 static bool add_unprotected_header_to_cose(cbor_item_t *cose)
1098 {
1099     cbor_item_t *map = cbor_new_definite_map(0);
1100     if (!map) {
1101         goto cleanup;
1102     }
1103     if (!qemu_cbor_array_push(cose, map)) {
1104         goto cleanup;
1105     }
1106 
1107     return true;
1108 
1109  cleanup:
1110     if (map) {
1111         cbor_decref(&map);
1112     }
1113     return false;
1114 }
1115 
1116 static bool add_ca_bundle_to_payload(cbor_item_t *map)
1117 {
1118     cbor_item_t *key_cbor = NULL;
1119     cbor_item_t *value_cbor = NULL;
1120     cbor_item_t *bs = NULL;
1121     uint8_t zero[64] = {0};
1122 
1123     key_cbor = cbor_build_string("cabundle");
1124     if (!key_cbor) {
1125         goto cleanup;
1126     }
1127     value_cbor = cbor_new_definite_array(1);
1128     if (!value_cbor) {
1129         goto cleanup;
1130     }
1131     bs = cbor_build_bytestring(zero, 64);
1132     if (!bs) {
1133         goto cleanup;
1134     }
1135     if (!qemu_cbor_array_push(value_cbor, bs)) {
1136         cbor_decref(&bs);
1137         goto cleanup;
1138     }
1139     if (!qemu_cbor_map_add(map, key_cbor, value_cbor)) {
1140         goto cleanup;
1141     }
1142 
1143     return true;
1144 
1145  cleanup:
1146     if (key_cbor) {
1147         cbor_decref(&key_cbor);
1148     }
1149     if (value_cbor) {
1150         cbor_decref(&value_cbor);
1151     }
1152     return false;
1153 }
1154 
1155 static bool add_payload_to_cose(cbor_item_t *cose, VirtIONSM *vnsm,
1156                                 NSMAttestationReq *req)
1157 {
1158     cbor_item_t *root = NULL;
1159     cbor_item_t *nested_map;
1160     cbor_item_t *bs = NULL;
1161     size_t locked_cnt;
1162     uint8_t ind[NSM_MAX_PCRS];
1163     size_t payload_map_size = 9;
1164     size_t len;
1165     struct PCRInfo *pcr;
1166     uint8_t zero[64] = {0};
1167     bool r = false;
1168     size_t buf_len = 16384;
1169     g_autofree uint8_t *buf = g_malloc(buf_len);
1170 
1171     root = cbor_new_definite_map(payload_map_size);
1172     if (!root) {
1173         goto cleanup;
1174     }
1175     if (!qemu_cbor_add_string_to_map(root, "module_id", vnsm->module_id)) {
1176         goto cleanup;
1177     }
1178     if (!qemu_cbor_add_string_to_map(root, "digest", vnsm->digest)) {
1179         goto cleanup;
1180     }
1181     if (!qemu_cbor_add_uint64_to_map(root, "timestamp",
1182                                      (uint64_t) time(NULL) * 1000)) {
1183         goto cleanup;
1184     }
1185 
1186     locked_cnt = 0;
1187     for (uint8_t i = 0; i < NSM_MAX_PCRS; ++i) {
1188         if (vnsm->pcrs[i].locked) {
1189             ind[locked_cnt++] = i;
1190         }
1191     }
1192     if (!qemu_cbor_add_map_to_map(root, "pcrs", locked_cnt, &nested_map)) {
1193         goto cleanup;
1194     }
1195     for (uint8_t i = 0; i < locked_cnt; ++i) {
1196         pcr = &(vnsm->pcrs[ind[i]]);
1197         if (!qemu_cbor_add_uint8_key_bytestring_to_map(
1198                 nested_map, ind[i],
1199                 pcr->data,
1200                 QCRYPTO_HASH_DIGEST_LEN_SHA384)) {
1201             goto cleanup;
1202         }
1203     }
1204     if (!qemu_cbor_add_bytestring_to_map(root, "certificate", zero, 64)) {
1205         goto cleanup;
1206     }
1207     if (!add_ca_bundle_to_payload(root)) {
1208         goto cleanup;
1209     }
1210 
1211     if (req->public_key.is_null) {
1212         if (!qemu_cbor_add_null_to_map(root, "public_key")) {
1213             goto cleanup;
1214         }
1215     } else if (!qemu_cbor_add_bytestring_to_map(root, "public_key",
1216                                                 req->public_key.buf,
1217                                                 req->public_key.len)) {
1218         goto cleanup;
1219     }
1220 
1221     if (req->user_data.is_null) {
1222         if (!qemu_cbor_add_null_to_map(root, "user_data")) {
1223             goto cleanup;
1224         }
1225     } else if (!qemu_cbor_add_bytestring_to_map(root, "user_data",
1226                                                 req->user_data.buf,
1227                                                 req->user_data.len)) {
1228             goto cleanup;
1229     }
1230 
1231     if (req->nonce.is_null) {
1232         if (!qemu_cbor_add_null_to_map(root, "nonce")) {
1233             goto cleanup;
1234         }
1235     } else if (!qemu_cbor_add_bytestring_to_map(root, "nonce",
1236                                                 req->nonce.buf,
1237                                                 req->nonce.len)) {
1238         goto cleanup;
1239     }
1240 
1241     len = cbor_serialize(root, buf, buf_len);
1242     if (len == 0) {
1243         goto cleanup;
1244     }
1245 
1246     bs = cbor_build_bytestring(buf, len);
1247     if (!bs) {
1248         goto cleanup;
1249     }
1250     if (!qemu_cbor_array_push(cose, bs)) {
1251         cbor_decref(&bs);
1252         goto cleanup;
1253     }
1254 
1255     r = true;
1256 
1257  cleanup:
1258     if (root) {
1259         cbor_decref(&root);
1260     }
1261     return r;
1262 }
1263 
1264 static bool add_signature_to_cose(cbor_item_t *cose)
1265 {
1266     cbor_item_t *bs = NULL;
1267     uint8_t zero[64] = {0};
1268 
1269     /* we don't actually sign the data, so we just put 64 zero bytes */
1270     bs = cbor_build_bytestring(zero, 64);
1271     if (!bs) {
1272         goto cleanup;
1273     }
1274 
1275     if (!qemu_cbor_array_push(cose, bs)) {
1276         goto cleanup;
1277     }
1278 
1279     return true;
1280 
1281  cleanup:
1282     if (bs) {
1283         cbor_decref(&bs);
1284     }
1285     return false;
1286 }
1287 
1288 /*
1289  * Attestation response structure:
1290  *
1291  * {
1292  *   Map(1) {
1293  *     key = String("Attestation"),
1294  *     value = Map(1) {
1295  *       key = String("document"),
1296  *       value = Byte_String()
1297  *     }
1298  *   }
1299  * }
1300  *
1301  * The document is a serialized COSE sign1 blob of the structure:
1302  * {
1303  *   Array(4) {
1304  *     [0] { ByteString() }, // serialized protected header
1305  *     [1] { Map(0) },       // 0 length map
1306  *     [2] { ByteString() }, // serialized payload
1307  *     [3] { ByteString() }, // signature
1308  *   }
1309  * }
1310  *
1311  * where [0] protected header is a serialized CBOR blob of the structure:
1312  * {
1313  *   Map(1) {
1314  *     key = Uint8(1)         // alg
1315  *     value = NegativeInt8() // Signing algorithm
1316  *   }
1317  * }
1318  *
1319  * [2] payload is serialized CBOR blob of the structure:
1320  * {
1321  *   Map(9) {
1322  *     [0] { key = String("module_id"), value = String(module_id) },
1323  *     [1] { key = String("digest"), value = String("SHA384") },
1324  *     [2] {
1325  *           key = String("timestamp"),
1326  *           value = Uint64(unix epoch of  when document was created)
1327  *         },
1328  *     [3] {
1329  *           key = String("pcrs"),
1330  *           value = Map(locked_pcr_cnt) {
1331  *                       key = Uint8(pcr_index),
1332  *                       value = ByteString(pcr_data)
1333  *                   },
1334  *         },
1335  *     [4] {
1336  *           key = String("certificate"),
1337  *           value = ByteString(Signing certificate)
1338  *         },
1339  *     [5] { key = String("cabundle"), value = Array(N) { ByteString()... } },
1340  *     [6] { key = String("public_key"), value = ByteString() || null },
1341  *     [7] { key = String("user_data"), value = ByteString() || null},
1342  *     [8] { key = String("nonce"), value = ByteString() || null},
1343  *   }
1344  * }
1345  */
1346 static bool handle_attestation(VirtIONSM *vnsm, struct iovec *request,
1347                                struct iovec *response, Error **errp)
1348 {
1349     cbor_item_t *root = NULL;
1350     cbor_item_t *cose = NULL;
1351     cbor_item_t *nested_map;
1352     size_t len;
1353     enum NSMResponseTypes type;
1354     bool r = false;
1355     size_t buf_len = 16384;
1356     g_autofree uint8_t *buf = g_malloc(buf_len);
1357     g_autofree NSMAttestationReq *nsm_req = g_malloc(sizeof(NSMAttestationReq));
1358 
1359     nsm_req->public_key.is_null = true;
1360     nsm_req->user_data.is_null = true;
1361     nsm_req->nonce.is_null = true;
1362 
1363     type = get_nsm_attestation_req(request->iov_base, request->iov_len,
1364                                    nsm_req);
1365     if (type != NSM_SUCCESS) {
1366         if (error_response(response, type, errp)) {
1367             r = true;
1368         }
1369         goto out;
1370     }
1371 
1372     cose = cbor_new_definite_array(4);
1373     if (!cose) {
1374         goto err;
1375     }
1376     if (!add_protected_header_to_cose(cose)) {
1377         goto err;
1378     }
1379     if (!add_unprotected_header_to_cose(cose)) {
1380         goto err;
1381     }
1382     if (!add_payload_to_cose(cose, vnsm, nsm_req)) {
1383         goto err;
1384     }
1385     if (!add_signature_to_cose(cose)) {
1386         goto err;
1387     }
1388 
1389     len = cbor_serialize(cose, buf, buf_len);
1390     if (len == 0) {
1391         goto err;
1392     }
1393 
1394     root = cbor_new_definite_map(1);
1395     if (!root) {
1396         goto err;
1397     }
1398     if (!qemu_cbor_add_map_to_map(root, "Attestation", 1, &nested_map)) {
1399         goto err;
1400     }
1401     if (!qemu_cbor_add_bytestring_to_map(nested_map, "document", buf, len)) {
1402         goto err;
1403     }
1404 
1405     len = cbor_serialize(root, response->iov_base, response->iov_len);
1406     if (len == 0) {
1407         if (error_response(response, NSM_INPUT_TOO_LARGE, errp)) {
1408             r = true;
1409         }
1410         goto out;
1411     }
1412 
1413     response->iov_len = len;
1414     r = true;
1415 
1416  out:
1417     if (root) {
1418         cbor_decref(&root);
1419     }
1420     if (cose) {
1421         cbor_decref(&cose);
1422     }
1423     return r;
1424 
1425  err:
1426     error_setg(errp, "Failed to initialize Attestation response");
1427     goto out;
1428 }
1429 
1430 enum CBOR_ROOT_TYPE {
1431     CBOR_ROOT_TYPE_STRING = 0,
1432     CBOR_ROOT_TYPE_MAP = 1,
1433 };
1434 
1435 struct nsm_cmd {
1436     char name[16];
1437     /*
1438      * There are 2 types of request
1439      * 1) String(); "GetRandom", "DescribeNSM"
1440      * 2) Map(1) { key: String(), value: ... }
1441      */
1442     enum CBOR_ROOT_TYPE root_type;
1443     bool (*response_fn)(VirtIONSM *vnsm, struct iovec *request,
1444                         struct iovec *response, Error **errp);
1445 };
1446 
1447 const struct nsm_cmd nsm_cmds[] = {
1448     { "GetRandom",   CBOR_ROOT_TYPE_STRING,  handle_get_random },
1449     { "DescribeNSM", CBOR_ROOT_TYPE_STRING,  handle_describe_nsm },
1450     { "DescribePCR", CBOR_ROOT_TYPE_MAP,     handle_describe_pcr },
1451     { "ExtendPCR",   CBOR_ROOT_TYPE_MAP,     handle_extend_pcr },
1452     { "LockPCR",     CBOR_ROOT_TYPE_MAP,     handle_lock_pcr },
1453     { "LockPCRs",    CBOR_ROOT_TYPE_MAP,     handle_lock_pcrs },
1454     { "Attestation", CBOR_ROOT_TYPE_MAP,     handle_attestation },
1455 };
1456 
1457 static const struct nsm_cmd *get_nsm_request_cmd(uint8_t *buf, size_t len)
1458 {
1459     size_t size;
1460     uint8_t *req;
1461     enum CBOR_ROOT_TYPE root_type;
1462     struct cbor_load_result result;
1463     cbor_item_t *item = cbor_load(buf, len, &result);
1464     if (!item || result.error.code != CBOR_ERR_NONE) {
1465         goto cleanup;
1466     }
1467 
1468     if (cbor_isa_string(item)) {
1469         size = cbor_string_length(item);
1470         req = cbor_string_handle(item);
1471         root_type = CBOR_ROOT_TYPE_STRING;
1472     } else if (cbor_isa_map(item) && cbor_map_size(item) == 1) {
1473         struct cbor_pair *handle = cbor_map_handle(item);
1474         if (cbor_isa_string(handle->key)) {
1475             size = cbor_string_length(handle->key);
1476             req = cbor_string_handle(handle->key);
1477             root_type = CBOR_ROOT_TYPE_MAP;
1478         } else {
1479             goto cleanup;
1480         }
1481     } else {
1482         goto cleanup;
1483     }
1484 
1485     if  (size == 0 || req == NULL) {
1486         goto cleanup;
1487     }
1488 
1489     for (int i = 0; i < ARRAY_SIZE(nsm_cmds); ++i) {
1490         if (nsm_cmds[i].root_type == root_type &&
1491             strlen(nsm_cmds[i].name) == size &&
1492             memcmp(nsm_cmds[i].name, req, size) == 0) {
1493             cbor_decref(&item);
1494             return &nsm_cmds[i];
1495         }
1496     }
1497 
1498  cleanup:
1499     if (item) {
1500         cbor_decref(&item);
1501     }
1502     return NULL;
1503 }
1504 
1505 static bool get_nsm_request_response(VirtIONSM *vnsm, struct iovec *req,
1506                                      struct iovec *resp, Error **errp)
1507 {
1508     const struct nsm_cmd *cmd;
1509 
1510     if (req->iov_len > NSM_REQUEST_MAX_SIZE) {
1511         if (error_response(resp, NSM_INPUT_TOO_LARGE, errp)) {
1512             return true;
1513         }
1514         error_setg(errp, "Failed to initialize InputTooLarge response");
1515         return false;
1516     }
1517 
1518     cmd = get_nsm_request_cmd(req->iov_base, req->iov_len);
1519 
1520     if (cmd == NULL) {
1521         if (error_response(resp, NSM_INVALID_OPERATION, errp)) {
1522             return true;
1523         }
1524         error_setg(errp, "Failed to initialize InvalidOperation response");
1525         return false;
1526     }
1527 
1528     return cmd->response_fn(vnsm, req, resp, errp);
1529 }
1530 
1531 static void handle_input(VirtIODevice *vdev, VirtQueue *vq)
1532 {
1533     g_autofree VirtQueueElement *out_elem = NULL;
1534     g_autofree VirtQueueElement *in_elem = NULL;
1535     VirtIONSM *vnsm = VIRTIO_NSM(vdev);
1536     Error *err = NULL;
1537     size_t sz;
1538     struct iovec req = {.iov_base = NULL, .iov_len = 0};
1539     struct iovec res = {.iov_base = NULL, .iov_len = 0};
1540 
1541     out_elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
1542     if (!out_elem) {
1543         /* nothing in virtqueue */
1544         return;
1545     }
1546 
1547     sz = iov_size(out_elem->out_sg, out_elem->out_num);
1548     if (sz == 0) {
1549         virtio_error(vdev, "Expected non-zero sized request buffer in "
1550                      "virtqueue");
1551         goto cleanup;
1552     }
1553 
1554     in_elem = virtqueue_pop(vq, sizeof(VirtQueueElement));
1555     if (!in_elem) {
1556         virtio_error(vdev, "Expected response buffer after request buffer "
1557                      "in virtqueue");
1558         goto cleanup;
1559     }
1560     if (iov_size(in_elem->in_sg, in_elem->in_num) != NSM_RESPONSE_BUF_SIZE) {
1561         virtio_error(vdev, "Expected response buffer of length 0x3000");
1562         goto cleanup;
1563     }
1564 
1565     req.iov_base = g_malloc(sz);
1566     req.iov_len = iov_to_buf(out_elem->out_sg, out_elem->out_num, 0,
1567                              req.iov_base, sz);
1568     if (req.iov_len != sz) {
1569         virtio_error(vdev, "Failed to copy request buffer");
1570         goto cleanup;
1571     }
1572 
1573     res.iov_base = g_malloc(NSM_RESPONSE_BUF_SIZE);
1574     res.iov_len = NSM_RESPONSE_BUF_SIZE;
1575 
1576     if (!get_nsm_request_response(vnsm, &req, &res, &err)) {
1577         error_report_err(err);
1578         virtio_error(vdev, "Failed to get NSM request response");
1579         goto cleanup;
1580     }
1581 
1582     sz = iov_from_buf(in_elem->in_sg, in_elem->in_num, 0, res.iov_base,
1583                       res.iov_len);
1584     if (sz != res.iov_len) {
1585         virtio_error(vdev, "Failed to copy response buffer");
1586         goto cleanup;
1587     }
1588 
1589     g_free(req.iov_base);
1590     g_free(res.iov_base);
1591     virtqueue_push(vq, out_elem, 0);
1592     virtqueue_push(vq, in_elem, in_elem->in_sg->iov_len);
1593     virtio_notify(vdev, vq);
1594     return;
1595 
1596  cleanup:
1597     g_free(req.iov_base);
1598     g_free(res.iov_base);
1599     if (out_elem) {
1600         virtqueue_detach_element(vq, out_elem, 0);
1601     }
1602     if (in_elem) {
1603         virtqueue_detach_element(vq, in_elem, 0);
1604     }
1605     return;
1606 }
1607 
1608 static uint64_t get_features(VirtIODevice *vdev, uint64_t f, Error **errp)
1609 {
1610     return f;
1611 }
1612 
1613 static bool extend_pcr(VirtIONSM *vnsm, int ind, uint8_t *data, uint16_t len)
1614 {
1615     Error *err = NULL;
1616     struct PCRInfo *pcr = &(vnsm->pcrs[ind]);
1617     size_t digest_len = QCRYPTO_HASH_DIGEST_LEN_SHA384;
1618     uint8_t result[QCRYPTO_HASH_DIGEST_LEN_SHA384];
1619     uint8_t *ptr = result;
1620     struct iovec iov[2] = {
1621         { .iov_base = pcr->data, .iov_len = QCRYPTO_HASH_DIGEST_LEN_SHA384 },
1622         { .iov_base = data, .iov_len = len },
1623     };
1624 
1625     if (qcrypto_hash_bytesv(QCRYPTO_HASH_ALGO_SHA384, iov, 2, &ptr, &digest_len,
1626                             &err) < 0) {
1627         return false;
1628     }
1629 
1630     memcpy(pcr->data, result, QCRYPTO_HASH_DIGEST_LEN_SHA384);
1631     return true;
1632 }
1633 
1634 static void lock_pcr(VirtIONSM *vnsm, int ind)
1635 {
1636     vnsm->pcrs[ind].locked = true;
1637 }
1638 
1639 static void virtio_nsm_device_realize(DeviceState *dev, Error **errp)
1640 {
1641     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
1642     VirtIONSM *vnsm = VIRTIO_NSM(dev);
1643 
1644     vnsm->max_pcrs = NSM_MAX_PCRS;
1645     vnsm->digest = (char *) "SHA384";
1646     if (vnsm->module_id == NULL) {
1647         vnsm->module_id = (char *) "i-234-enc5678";
1648     }
1649     vnsm->version_major = 1;
1650     vnsm->version_minor = 0;
1651     vnsm->version_patch = 0;
1652     vnsm->extend_pcr = extend_pcr;
1653     vnsm->lock_pcr = lock_pcr;
1654 
1655     virtio_init(vdev, VIRTIO_ID_NITRO_SEC_MOD, 0);
1656 
1657     vnsm->vq = virtio_add_queue(vdev, 2, handle_input);
1658 }
1659 
1660 static void virtio_nsm_device_unrealize(DeviceState *dev)
1661 {
1662     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
1663 
1664     virtio_del_queue(vdev, 0);
1665     virtio_cleanup(vdev);
1666 }
1667 
1668 static const VMStateDescription vmstate_pcr_info_entry = {
1669     .name = "pcr_info_entry",
1670     .minimum_version_id = 1,
1671     .version_id = 1,
1672     .fields = (const VMStateField[]) {
1673         VMSTATE_BOOL(locked, struct PCRInfo),
1674         VMSTATE_UINT8_ARRAY(data, struct PCRInfo,
1675                             QCRYPTO_HASH_DIGEST_LEN_SHA384),
1676         VMSTATE_END_OF_LIST()
1677     },
1678 };
1679 
1680 static const VMStateDescription vmstate_virtio_nsm_device = {
1681     .name = "virtio-nsm-device",
1682     .minimum_version_id = 1,
1683     .version_id = 1,
1684     .fields = (const VMStateField[]) {
1685         VMSTATE_STRUCT_ARRAY(pcrs, VirtIONSM, NSM_MAX_PCRS, 1,
1686                              vmstate_pcr_info_entry, struct PCRInfo),
1687         VMSTATE_END_OF_LIST()
1688     },
1689 };
1690 
1691 static const VMStateDescription vmstate_virtio_nsm = {
1692     .name = "virtio-nsm",
1693     .minimum_version_id = 1,
1694     .version_id = 1,
1695     .fields = (const VMStateField[]) {
1696         VMSTATE_VIRTIO_DEVICE,
1697         VMSTATE_END_OF_LIST()
1698     },
1699 };
1700 
1701 static Property virtio_nsm_properties[] = {
1702     DEFINE_PROP_STRING("module-id", VirtIONSM, module_id),
1703     DEFINE_PROP_END_OF_LIST(),
1704 };
1705 
1706 static void virtio_nsm_class_init(ObjectClass *klass, void *data)
1707 {
1708     DeviceClass *dc = DEVICE_CLASS(klass);
1709     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
1710 
1711     device_class_set_props(dc, virtio_nsm_properties);
1712     dc->vmsd = &vmstate_virtio_nsm;
1713     set_bit(DEVICE_CATEGORY_MISC, dc->categories);
1714     vdc->realize = virtio_nsm_device_realize;
1715     vdc->unrealize = virtio_nsm_device_unrealize;
1716     vdc->get_features = get_features;
1717     vdc->vmsd = &vmstate_virtio_nsm_device;
1718 }
1719 
1720 static const TypeInfo virtio_nsm_info = {
1721     .name = TYPE_VIRTIO_NSM,
1722     .parent = TYPE_VIRTIO_DEVICE,
1723     .instance_size = sizeof(VirtIONSM),
1724     .class_init = virtio_nsm_class_init,
1725 };
1726 
1727 static void virtio_register_types(void)
1728 {
1729     type_register_static(&virtio_nsm_info);
1730 }
1731 
1732 type_init(virtio_register_types)
1733