xref: /linux/samples/nitro_enclaves/ne_ioctl_sample.c (revision ec8a42e7343234802b9054874fe01810880289ce)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
4  */
5 
6 /**
7  * DOC: Sample flow of using the ioctl interface provided by the Nitro Enclaves (NE)
8  * kernel driver.
9  *
10  * Usage
11  * -----
12  *
13  * Load the nitro_enclaves module, setting also the enclave CPU pool. The
14  * enclave CPUs need to be full cores from the same NUMA node. CPU 0 and its
15  * siblings have to remain available for the primary / parent VM, so they
16  * cannot be included in the enclave CPU pool.
17  *
18  * See the cpu list section from the kernel documentation.
19  * https://www.kernel.org/doc/html/latest/admin-guide/kernel-parameters.html#cpu-lists
20  *
21  *	insmod drivers/virt/nitro_enclaves/nitro_enclaves.ko
22  *	lsmod
23  *
24  *	The CPU pool can be set at runtime, after the kernel module is loaded.
25  *
26  *	echo <cpu-list> > /sys/module/nitro_enclaves/parameters/ne_cpus
27  *
28  *	NUMA and CPU siblings information can be found using:
29  *
30  *	lscpu
31  *	/proc/cpuinfo
32  *
33  * Check the online / offline CPU list. The CPUs from the pool should be
34  * offlined.
35  *
36  *	lscpu
37  *
38  * Check dmesg for any warnings / errors through the NE driver lifetime / usage.
39  * The NE logs contain the "nitro_enclaves" or "pci 0000:00:02.0" pattern.
40  *
41  *	dmesg
42  *
43  * Setup hugetlbfs huge pages. The memory needs to be from the same NUMA node as
44  * the enclave CPUs.
45  *
46  * https://www.kernel.org/doc/html/latest/admin-guide/mm/hugetlbpage.html
47  *
48  * By default, the allocation of hugetlb pages are distributed on all possible
49  * NUMA nodes. Use the following configuration files to set the number of huge
50  * pages from a NUMA node:
51  *
52  *	/sys/devices/system/node/node<X>/hugepages/hugepages-2048kB/nr_hugepages
53  *	/sys/devices/system/node/node<X>/hugepages/hugepages-1048576kB/nr_hugepages
54  *
55  *	or, if not on a system with multiple NUMA nodes, can also set the number
56  *	of 2 MiB / 1 GiB huge pages using
57  *
58  *	/sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
59  *	/sys/kernel/mm/hugepages/hugepages-1048576kB/nr_hugepages
60  *
61  *	In this example 256 hugepages of 2 MiB are used.
62  *
63  * Build and run the NE sample.
64  *
65  *	make -C samples/nitro_enclaves clean
66  *	make -C samples/nitro_enclaves
67  *	./samples/nitro_enclaves/ne_ioctl_sample <path_to_enclave_image>
68  *
69  * Unload the nitro_enclaves module.
70  *
71  *	rmmod nitro_enclaves
72  *	lsmod
73  */
74 
75 #include <stdio.h>
76 #include <stdlib.h>
77 #include <errno.h>
78 #include <fcntl.h>
79 #include <limits.h>
80 #include <poll.h>
81 #include <pthread.h>
82 #include <string.h>
83 #include <sys/eventfd.h>
84 #include <sys/ioctl.h>
85 #include <sys/mman.h>
86 #include <sys/socket.h>
87 #include <sys/stat.h>
88 #include <sys/types.h>
89 #include <unistd.h>
90 
91 #include <linux/mman.h>
92 #include <linux/nitro_enclaves.h>
93 #include <linux/vm_sockets.h>
94 
95 /**
96  * NE_DEV_NAME - Nitro Enclaves (NE) misc device that provides the ioctl interface.
97  */
98 #define NE_DEV_NAME			"/dev/nitro_enclaves"
99 
100 /**
101  * NE_POLL_WAIT_TIME - Timeout in seconds for each poll event.
102  */
103 #define NE_POLL_WAIT_TIME		(60)
104 /**
105  * NE_POLL_WAIT_TIME_MS - Timeout in milliseconds for each poll event.
106  */
107 #define NE_POLL_WAIT_TIME_MS		(NE_POLL_WAIT_TIME * 1000)
108 
109 /**
110  * NE_SLEEP_TIME - Amount of time in seconds for the process to keep the enclave alive.
111  */
112 #define NE_SLEEP_TIME			(300)
113 
114 /**
115  * NE_DEFAULT_NR_VCPUS - Default number of vCPUs set for an enclave.
116  */
117 #define NE_DEFAULT_NR_VCPUS		(2)
118 
119 /**
120  * NE_MIN_MEM_REGION_SIZE - Minimum size of a memory region - 2 MiB.
121  */
122 #define NE_MIN_MEM_REGION_SIZE		(2 * 1024 * 1024)
123 
124 /**
125  * NE_DEFAULT_NR_MEM_REGIONS - Default number of memory regions of 2 MiB set for
126  *			       an enclave.
127  */
128 #define NE_DEFAULT_NR_MEM_REGIONS	(256)
129 
130 /**
131  * NE_IMAGE_LOAD_HEARTBEAT_CID - Vsock CID for enclave image loading heartbeat logic.
132  */
133 #define NE_IMAGE_LOAD_HEARTBEAT_CID	(3)
134 /**
135  * NE_IMAGE_LOAD_HEARTBEAT_PORT - Vsock port for enclave image loading heartbeat logic.
136  */
137 #define NE_IMAGE_LOAD_HEARTBEAT_PORT	(9000)
138 /**
139  * NE_IMAGE_LOAD_HEARTBEAT_VALUE - Heartbeat value for enclave image loading.
140  */
141 #define NE_IMAGE_LOAD_HEARTBEAT_VALUE	(0xb7)
142 
143 /**
144  * struct ne_user_mem_region - User space memory region set for an enclave.
145  * @userspace_addr:	Address of the user space memory region.
146  * @memory_size:	Size of the user space memory region.
147  */
148 struct ne_user_mem_region {
149 	void	*userspace_addr;
150 	size_t	memory_size;
151 };
152 
153 /**
154  * ne_create_vm() - Create a slot for the enclave VM.
155  * @ne_dev_fd:		The file descriptor of the NE misc device.
156  * @slot_uid:		The generated slot uid for the enclave.
157  * @enclave_fd :	The generated file descriptor for the enclave.
158  *
159  * Context: Process context.
160  * Return:
161  * * 0 on success.
162  * * Negative return value on failure.
163  */
164 static int ne_create_vm(int ne_dev_fd, unsigned long *slot_uid, int *enclave_fd)
165 {
166 	int rc = -EINVAL;
167 	*enclave_fd = ioctl(ne_dev_fd, NE_CREATE_VM, slot_uid);
168 
169 	if (*enclave_fd < 0) {
170 		rc = *enclave_fd;
171 		switch (errno) {
172 		case NE_ERR_NO_CPUS_AVAIL_IN_POOL: {
173 			printf("Error in create VM, no CPUs available in the NE CPU pool\n");
174 
175 			break;
176 		}
177 
178 		default:
179 			printf("Error in create VM [%m]\n");
180 		}
181 
182 		return rc;
183 	}
184 
185 	return 0;
186 }
187 
188 
189 /**
190  * ne_poll_enclave_fd() - Thread function for polling the enclave fd.
191  * @data:	Argument provided for the polling function.
192  *
193  * Context: Process context.
194  * Return:
195  * * NULL on success / failure.
196  */
197 void *ne_poll_enclave_fd(void *data)
198 {
199 	int enclave_fd = *(int *)data;
200 	struct pollfd fds[1] = {};
201 	int i = 0;
202 	int rc = -EINVAL;
203 
204 	printf("Running from poll thread, enclave fd %d\n", enclave_fd);
205 
206 	fds[0].fd = enclave_fd;
207 	fds[0].events = POLLIN | POLLERR | POLLHUP;
208 
209 	/* Keep on polling until the current process is terminated. */
210 	while (1) {
211 		printf("[iter %d] Polling ...\n", i);
212 
213 		rc = poll(fds, 1, NE_POLL_WAIT_TIME_MS);
214 		if (rc < 0) {
215 			printf("Error in poll [%m]\n");
216 
217 			return NULL;
218 		}
219 
220 		i++;
221 
222 		if (!rc) {
223 			printf("Poll: %d seconds elapsed\n",
224 			       i * NE_POLL_WAIT_TIME);
225 
226 			continue;
227 		}
228 
229 		printf("Poll received value 0x%x\n", fds[0].revents);
230 
231 		if (fds[0].revents & POLLHUP) {
232 			printf("Received POLLHUP\n");
233 
234 			return NULL;
235 		}
236 
237 		if (fds[0].revents & POLLNVAL) {
238 			printf("Received POLLNVAL\n");
239 
240 			return NULL;
241 		}
242 	}
243 
244 	return NULL;
245 }
246 
247 /**
248  * ne_alloc_user_mem_region() - Allocate a user space memory region for an enclave.
249  * @ne_user_mem_region:	User space memory region allocated using hugetlbfs.
250  *
251  * Context: Process context.
252  * Return:
253  * * 0 on success.
254  * * Negative return value on failure.
255  */
256 static int ne_alloc_user_mem_region(struct ne_user_mem_region *ne_user_mem_region)
257 {
258 	/**
259 	 * Check available hugetlb encodings for different huge page sizes in
260 	 * include/uapi/linux/mman.h.
261 	 */
262 	ne_user_mem_region->userspace_addr = mmap(NULL, ne_user_mem_region->memory_size,
263 						  PROT_READ | PROT_WRITE,
264 						  MAP_PRIVATE | MAP_ANONYMOUS |
265 						  MAP_HUGETLB | MAP_HUGE_2MB, -1, 0);
266 	if (ne_user_mem_region->userspace_addr == MAP_FAILED) {
267 		printf("Error in mmap memory [%m]\n");
268 
269 		return -1;
270 	}
271 
272 	return 0;
273 }
274 
275 /**
276  * ne_load_enclave_image() - Place the enclave image in the enclave memory.
277  * @enclave_fd :		The file descriptor associated with the enclave.
278  * @ne_user_mem_regions:	User space memory regions allocated for the enclave.
279  * @enclave_image_path :	The file path of the enclave image.
280  *
281  * Context: Process context.
282  * Return:
283  * * 0 on success.
284  * * Negative return value on failure.
285  */
286 static int ne_load_enclave_image(int enclave_fd, struct ne_user_mem_region ne_user_mem_regions[],
287 				 char *enclave_image_path)
288 {
289 	unsigned char *enclave_image = NULL;
290 	int enclave_image_fd = -1;
291 	size_t enclave_image_size = 0;
292 	size_t enclave_memory_size = 0;
293 	unsigned long i = 0;
294 	size_t image_written_bytes = 0;
295 	struct ne_image_load_info image_load_info = {
296 		.flags = NE_EIF_IMAGE,
297 	};
298 	struct stat image_stat_buf = {};
299 	int rc = -EINVAL;
300 	size_t temp_image_offset = 0;
301 
302 	for (i = 0; i < NE_DEFAULT_NR_MEM_REGIONS; i++)
303 		enclave_memory_size += ne_user_mem_regions[i].memory_size;
304 
305 	rc = stat(enclave_image_path, &image_stat_buf);
306 	if (rc < 0) {
307 		printf("Error in get image stat info [%m]\n");
308 
309 		return rc;
310 	}
311 
312 	enclave_image_size = image_stat_buf.st_size;
313 
314 	if (enclave_memory_size < enclave_image_size) {
315 		printf("The enclave memory is smaller than the enclave image size\n");
316 
317 		return -ENOMEM;
318 	}
319 
320 	rc = ioctl(enclave_fd, NE_GET_IMAGE_LOAD_INFO, &image_load_info);
321 	if (rc < 0) {
322 		switch (errno) {
323 		case NE_ERR_NOT_IN_INIT_STATE: {
324 			printf("Error in get image load info, enclave not in init state\n");
325 
326 			break;
327 		}
328 
329 		case NE_ERR_INVALID_FLAG_VALUE: {
330 			printf("Error in get image load info, provided invalid flag\n");
331 
332 			break;
333 		}
334 
335 		default:
336 			printf("Error in get image load info [%m]\n");
337 		}
338 
339 		return rc;
340 	}
341 
342 	printf("Enclave image offset in enclave memory is %lld\n",
343 	       image_load_info.memory_offset);
344 
345 	enclave_image_fd = open(enclave_image_path, O_RDONLY);
346 	if (enclave_image_fd < 0) {
347 		printf("Error in open enclave image file [%m]\n");
348 
349 		return enclave_image_fd;
350 	}
351 
352 	enclave_image = mmap(NULL, enclave_image_size, PROT_READ,
353 			     MAP_PRIVATE, enclave_image_fd, 0);
354 	if (enclave_image == MAP_FAILED) {
355 		printf("Error in mmap enclave image [%m]\n");
356 
357 		return -1;
358 	}
359 
360 	temp_image_offset = image_load_info.memory_offset;
361 
362 	for (i = 0; i < NE_DEFAULT_NR_MEM_REGIONS; i++) {
363 		size_t bytes_to_write = 0;
364 		size_t memory_offset = 0;
365 		size_t memory_size = ne_user_mem_regions[i].memory_size;
366 		size_t remaining_bytes = 0;
367 		void *userspace_addr = ne_user_mem_regions[i].userspace_addr;
368 
369 		if (temp_image_offset >= memory_size) {
370 			temp_image_offset -= memory_size;
371 
372 			continue;
373 		} else if (temp_image_offset != 0) {
374 			memory_offset = temp_image_offset;
375 			memory_size -= temp_image_offset;
376 			temp_image_offset = 0;
377 		}
378 
379 		remaining_bytes = enclave_image_size - image_written_bytes;
380 		bytes_to_write = memory_size < remaining_bytes ?
381 				 memory_size : remaining_bytes;
382 
383 		memcpy(userspace_addr + memory_offset,
384 		       enclave_image + image_written_bytes, bytes_to_write);
385 
386 		image_written_bytes += bytes_to_write;
387 
388 		if (image_written_bytes == enclave_image_size)
389 			break;
390 	}
391 
392 	munmap(enclave_image, enclave_image_size);
393 
394 	close(enclave_image_fd);
395 
396 	return 0;
397 }
398 
399 /**
400  * ne_set_user_mem_region() - Set a user space memory region for the given enclave.
401  * @enclave_fd :		The file descriptor associated with the enclave.
402  * @ne_user_mem_region :	User space memory region to be set for the enclave.
403  *
404  * Context: Process context.
405  * Return:
406  * * 0 on success.
407  * * Negative return value on failure.
408  */
409 static int ne_set_user_mem_region(int enclave_fd, struct ne_user_mem_region ne_user_mem_region)
410 {
411 	struct ne_user_memory_region mem_region = {
412 		.flags = NE_DEFAULT_MEMORY_REGION,
413 		.memory_size = ne_user_mem_region.memory_size,
414 		.userspace_addr = (__u64)ne_user_mem_region.userspace_addr,
415 	};
416 	int rc = -EINVAL;
417 
418 	rc = ioctl(enclave_fd, NE_SET_USER_MEMORY_REGION, &mem_region);
419 	if (rc < 0) {
420 		switch (errno) {
421 		case NE_ERR_NOT_IN_INIT_STATE: {
422 			printf("Error in set user memory region, enclave not in init state\n");
423 
424 			break;
425 		}
426 
427 		case NE_ERR_INVALID_MEM_REGION_SIZE: {
428 			printf("Error in set user memory region, mem size not multiple of 2 MiB\n");
429 
430 			break;
431 		}
432 
433 		case NE_ERR_INVALID_MEM_REGION_ADDR: {
434 			printf("Error in set user memory region, invalid user space address\n");
435 
436 			break;
437 		}
438 
439 		case NE_ERR_UNALIGNED_MEM_REGION_ADDR: {
440 			printf("Error in set user memory region, unaligned user space address\n");
441 
442 			break;
443 		}
444 
445 		case NE_ERR_MEM_REGION_ALREADY_USED: {
446 			printf("Error in set user memory region, memory region already used\n");
447 
448 			break;
449 		}
450 
451 		case NE_ERR_MEM_NOT_HUGE_PAGE: {
452 			printf("Error in set user memory region, not backed by huge pages\n");
453 
454 			break;
455 		}
456 
457 		case NE_ERR_MEM_DIFFERENT_NUMA_NODE: {
458 			printf("Error in set user memory region, different NUMA node than CPUs\n");
459 
460 			break;
461 		}
462 
463 		case NE_ERR_MEM_MAX_REGIONS: {
464 			printf("Error in set user memory region, max memory regions reached\n");
465 
466 			break;
467 		}
468 
469 		case NE_ERR_INVALID_PAGE_SIZE: {
470 			printf("Error in set user memory region, has page not multiple of 2 MiB\n");
471 
472 			break;
473 		}
474 
475 		case NE_ERR_INVALID_FLAG_VALUE: {
476 			printf("Error in set user memory region, provided invalid flag\n");
477 
478 			break;
479 		}
480 
481 		default:
482 			printf("Error in set user memory region [%m]\n");
483 		}
484 
485 		return rc;
486 	}
487 
488 	return 0;
489 }
490 
491 /**
492  * ne_free_mem_regions() - Unmap all the user space memory regions that were set
493  *			   aside for the enclave.
494  * @ne_user_mem_regions:	The user space memory regions associated with an enclave.
495  *
496  * Context: Process context.
497  */
498 static void ne_free_mem_regions(struct ne_user_mem_region ne_user_mem_regions[])
499 {
500 	unsigned int i = 0;
501 
502 	for (i = 0; i < NE_DEFAULT_NR_MEM_REGIONS; i++)
503 		munmap(ne_user_mem_regions[i].userspace_addr,
504 		       ne_user_mem_regions[i].memory_size);
505 }
506 
507 /**
508  * ne_add_vcpu() - Add a vCPU to the given enclave.
509  * @enclave_fd :	The file descriptor associated with the enclave.
510  * @vcpu_id:		vCPU id to be set for the enclave, either provided or
511  *			auto-generated (if provided vCPU id is 0).
512  *
513  * Context: Process context.
514  * Return:
515  * * 0 on success.
516  * * Negative return value on failure.
517  */
518 static int ne_add_vcpu(int enclave_fd, unsigned int *vcpu_id)
519 {
520 	int rc = -EINVAL;
521 
522 	rc = ioctl(enclave_fd, NE_ADD_VCPU, vcpu_id);
523 	if (rc < 0) {
524 		switch (errno) {
525 		case NE_ERR_NO_CPUS_AVAIL_IN_POOL: {
526 			printf("Error in add vcpu, no CPUs available in the NE CPU pool\n");
527 
528 			break;
529 		}
530 
531 		case NE_ERR_VCPU_ALREADY_USED: {
532 			printf("Error in add vcpu, the provided vCPU is already used\n");
533 
534 			break;
535 		}
536 
537 		case NE_ERR_VCPU_NOT_IN_CPU_POOL: {
538 			printf("Error in add vcpu, the provided vCPU is not in the NE CPU pool\n");
539 
540 			break;
541 		}
542 
543 		case NE_ERR_VCPU_INVALID_CPU_CORE: {
544 			printf("Error in add vcpu, the core id of the provided vCPU is invalid\n");
545 
546 			break;
547 		}
548 
549 		case NE_ERR_NOT_IN_INIT_STATE: {
550 			printf("Error in add vcpu, enclave not in init state\n");
551 
552 			break;
553 		}
554 
555 		case NE_ERR_INVALID_VCPU: {
556 			printf("Error in add vcpu, the provided vCPU is out of avail CPUs range\n");
557 
558 			break;
559 		}
560 
561 		default:
562 			printf("Error in add vcpu [%m]\n");
563 
564 		}
565 		return rc;
566 	}
567 
568 	return 0;
569 }
570 
571 /**
572  * ne_start_enclave() - Start the given enclave.
573  * @enclave_fd :		The file descriptor associated with the enclave.
574  * @enclave_start_info :	Enclave metadata used for starting e.g. vsock CID.
575  *
576  * Context: Process context.
577  * Return:
578  * * 0 on success.
579  * * Negative return value on failure.
580  */
581 static int ne_start_enclave(int enclave_fd,  struct ne_enclave_start_info *enclave_start_info)
582 {
583 	int rc = -EINVAL;
584 
585 	rc = ioctl(enclave_fd, NE_START_ENCLAVE, enclave_start_info);
586 	if (rc < 0) {
587 		switch (errno) {
588 		case NE_ERR_NOT_IN_INIT_STATE: {
589 			printf("Error in start enclave, enclave not in init state\n");
590 
591 			break;
592 		}
593 
594 		case NE_ERR_NO_MEM_REGIONS_ADDED: {
595 			printf("Error in start enclave, no memory regions have been added\n");
596 
597 			break;
598 		}
599 
600 		case NE_ERR_NO_VCPUS_ADDED: {
601 			printf("Error in start enclave, no vCPUs have been added\n");
602 
603 			break;
604 		}
605 
606 		case NE_ERR_FULL_CORES_NOT_USED: {
607 			printf("Error in start enclave, enclave has no full cores set\n");
608 
609 			break;
610 		}
611 
612 		case NE_ERR_ENCLAVE_MEM_MIN_SIZE: {
613 			printf("Error in start enclave, enclave memory is less than min size\n");
614 
615 			break;
616 		}
617 
618 		case NE_ERR_INVALID_FLAG_VALUE: {
619 			printf("Error in start enclave, provided invalid flag\n");
620 
621 			break;
622 		}
623 
624 		case NE_ERR_INVALID_ENCLAVE_CID: {
625 			printf("Error in start enclave, provided invalid enclave CID\n");
626 
627 			break;
628 		}
629 
630 		default:
631 			printf("Error in start enclave [%m]\n");
632 		}
633 
634 		return rc;
635 	}
636 
637 	return 0;
638 }
639 
640 /**
641  * ne_start_enclave_check_booted() - Start the enclave and wait for a hearbeat
642  *				     from it, on a newly created vsock channel,
643  *				     to check it has booted.
644  * @enclave_fd :	The file descriptor associated with the enclave.
645  *
646  * Context: Process context.
647  * Return:
648  * * 0 on success.
649  * * Negative return value on failure.
650  */
651 static int ne_start_enclave_check_booted(int enclave_fd)
652 {
653 	struct sockaddr_vm client_vsock_addr = {};
654 	int client_vsock_fd = -1;
655 	socklen_t client_vsock_len = sizeof(client_vsock_addr);
656 	struct ne_enclave_start_info enclave_start_info = {};
657 	struct pollfd fds[1] = {};
658 	int rc = -EINVAL;
659 	unsigned char recv_buf = 0;
660 	struct sockaddr_vm server_vsock_addr = {
661 		.svm_family = AF_VSOCK,
662 		.svm_cid = NE_IMAGE_LOAD_HEARTBEAT_CID,
663 		.svm_port = NE_IMAGE_LOAD_HEARTBEAT_PORT,
664 	};
665 	int server_vsock_fd = -1;
666 
667 	server_vsock_fd = socket(AF_VSOCK, SOCK_STREAM, 0);
668 	if (server_vsock_fd < 0) {
669 		rc = server_vsock_fd;
670 
671 		printf("Error in socket [%m]\n");
672 
673 		return rc;
674 	}
675 
676 	rc = bind(server_vsock_fd, (struct sockaddr *)&server_vsock_addr,
677 		  sizeof(server_vsock_addr));
678 	if (rc < 0) {
679 		printf("Error in bind [%m]\n");
680 
681 		goto out;
682 	}
683 
684 	rc = listen(server_vsock_fd, 1);
685 	if (rc < 0) {
686 		printf("Error in listen [%m]\n");
687 
688 		goto out;
689 	}
690 
691 	rc = ne_start_enclave(enclave_fd, &enclave_start_info);
692 	if (rc < 0)
693 		goto out;
694 
695 	printf("Enclave started, CID %llu\n", enclave_start_info.enclave_cid);
696 
697 	fds[0].fd = server_vsock_fd;
698 	fds[0].events = POLLIN;
699 
700 	rc = poll(fds, 1, NE_POLL_WAIT_TIME_MS);
701 	if (rc < 0) {
702 		printf("Error in poll [%m]\n");
703 
704 		goto out;
705 	}
706 
707 	if (!rc) {
708 		printf("Poll timeout, %d seconds elapsed\n", NE_POLL_WAIT_TIME);
709 
710 		rc = -ETIMEDOUT;
711 
712 		goto out;
713 	}
714 
715 	if ((fds[0].revents & POLLIN) == 0) {
716 		printf("Poll received value %d\n", fds[0].revents);
717 
718 		rc = -EINVAL;
719 
720 		goto out;
721 	}
722 
723 	rc = accept(server_vsock_fd, (struct sockaddr *)&client_vsock_addr,
724 		    &client_vsock_len);
725 	if (rc < 0) {
726 		printf("Error in accept [%m]\n");
727 
728 		goto out;
729 	}
730 
731 	client_vsock_fd = rc;
732 
733 	/*
734 	 * Read the heartbeat value that the init process in the enclave sends
735 	 * after vsock connect.
736 	 */
737 	rc = read(client_vsock_fd, &recv_buf, sizeof(recv_buf));
738 	if (rc < 0) {
739 		printf("Error in read [%m]\n");
740 
741 		goto out;
742 	}
743 
744 	if (rc != sizeof(recv_buf) || recv_buf != NE_IMAGE_LOAD_HEARTBEAT_VALUE) {
745 		printf("Read %d instead of %d\n", recv_buf,
746 		       NE_IMAGE_LOAD_HEARTBEAT_VALUE);
747 
748 		goto out;
749 	}
750 
751 	/* Write the heartbeat value back. */
752 	rc = write(client_vsock_fd, &recv_buf, sizeof(recv_buf));
753 	if (rc < 0) {
754 		printf("Error in write [%m]\n");
755 
756 		goto out;
757 	}
758 
759 	rc = 0;
760 
761 out:
762 	close(server_vsock_fd);
763 
764 	return rc;
765 }
766 
767 int main(int argc, char *argv[])
768 {
769 	int enclave_fd = -1;
770 	unsigned int i = 0;
771 	int ne_dev_fd = -1;
772 	struct ne_user_mem_region ne_user_mem_regions[NE_DEFAULT_NR_MEM_REGIONS] = {};
773 	unsigned int ne_vcpus[NE_DEFAULT_NR_VCPUS] = {};
774 	int rc = -EINVAL;
775 	pthread_t thread_id = 0;
776 	unsigned long slot_uid = 0;
777 
778 	if (argc != 2) {
779 		printf("Usage: %s <path_to_enclave_image>\n", argv[0]);
780 
781 		exit(EXIT_FAILURE);
782 	}
783 
784 	if (strlen(argv[1]) >= PATH_MAX) {
785 		printf("The size of the path to enclave image is higher than max path\n");
786 
787 		exit(EXIT_FAILURE);
788 	}
789 
790 	ne_dev_fd = open(NE_DEV_NAME, O_RDWR | O_CLOEXEC);
791 	if (ne_dev_fd < 0) {
792 		printf("Error in open NE device [%m]\n");
793 
794 		exit(EXIT_FAILURE);
795 	}
796 
797 	printf("Creating enclave slot ...\n");
798 
799 	rc = ne_create_vm(ne_dev_fd, &slot_uid, &enclave_fd);
800 
801 	close(ne_dev_fd);
802 
803 	if (rc < 0)
804 		exit(EXIT_FAILURE);
805 
806 	printf("Enclave fd %d\n", enclave_fd);
807 
808 	rc = pthread_create(&thread_id, NULL, ne_poll_enclave_fd, (void *)&enclave_fd);
809 	if (rc < 0) {
810 		printf("Error in thread create [%m]\n");
811 
812 		close(enclave_fd);
813 
814 		exit(EXIT_FAILURE);
815 	}
816 
817 	for (i = 0; i < NE_DEFAULT_NR_MEM_REGIONS; i++) {
818 		ne_user_mem_regions[i].memory_size = NE_MIN_MEM_REGION_SIZE;
819 
820 		rc = ne_alloc_user_mem_region(&ne_user_mem_regions[i]);
821 		if (rc < 0) {
822 			printf("Error in alloc userspace memory region, iter %d\n", i);
823 
824 			goto release_enclave_fd;
825 		}
826 	}
827 
828 	rc = ne_load_enclave_image(enclave_fd, ne_user_mem_regions, argv[1]);
829 	if (rc < 0)
830 		goto release_enclave_fd;
831 
832 	for (i = 0; i < NE_DEFAULT_NR_MEM_REGIONS; i++) {
833 		rc = ne_set_user_mem_region(enclave_fd, ne_user_mem_regions[i]);
834 		if (rc < 0) {
835 			printf("Error in set memory region, iter %d\n", i);
836 
837 			goto release_enclave_fd;
838 		}
839 	}
840 
841 	printf("Enclave memory regions were added\n");
842 
843 	for (i = 0; i < NE_DEFAULT_NR_VCPUS; i++) {
844 		/*
845 		 * The vCPU is chosen from the enclave vCPU pool, if the value
846 		 * of the vcpu_id is 0.
847 		 */
848 		ne_vcpus[i] = 0;
849 		rc = ne_add_vcpu(enclave_fd, &ne_vcpus[i]);
850 		if (rc < 0) {
851 			printf("Error in add vcpu, iter %d\n", i);
852 
853 			goto release_enclave_fd;
854 		}
855 
856 		printf("Added vCPU %d to the enclave\n", ne_vcpus[i]);
857 	}
858 
859 	printf("Enclave vCPUs were added\n");
860 
861 	rc = ne_start_enclave_check_booted(enclave_fd);
862 	if (rc < 0) {
863 		printf("Error in the enclave start / image loading heartbeat logic [rc=%d]\n", rc);
864 
865 		goto release_enclave_fd;
866 	}
867 
868 	printf("Entering sleep for %d seconds ...\n", NE_SLEEP_TIME);
869 
870 	sleep(NE_SLEEP_TIME);
871 
872 	close(enclave_fd);
873 
874 	ne_free_mem_regions(ne_user_mem_regions);
875 
876 	exit(EXIT_SUCCESS);
877 
878 release_enclave_fd:
879 	close(enclave_fd);
880 	ne_free_mem_regions(ne_user_mem_regions);
881 
882 	exit(EXIT_FAILURE);
883 }
884