1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * sdsi: Intel Software Defined Silicon tool for provisioning certificates
4  * and activation payloads on supported cpus.
5  *
6  * See https://github.com/intel/intel-sdsi/blob/master/os-interface.rst
7  * for register descriptions.
8  *
9  * Copyright (C) 2022 Intel Corporation. All rights reserved.
10  */
11 
12 #include <dirent.h>
13 #include <errno.h>
14 #include <fcntl.h>
15 #include <getopt.h>
16 #include <stdbool.h>
17 #include <stdio.h>
18 #include <stdint.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <unistd.h>
22 
23 #include <sys/types.h>
24 
25 #define SDSI_DEV		"intel_vsec.sdsi"
26 #define AUX_DEV_PATH		"/sys/bus/auxiliary/devices/"
27 #define SDSI_PATH		(AUX_DEV_DIR SDSI_DEV)
28 #define GUID			0x6dd191
29 #define REGISTERS_MIN_SIZE	72
30 
31 #define __round_mask(x, y) ((__typeof__(x))((y) - 1))
32 #define round_up(x, y) ((((x) - 1) | __round_mask(x, y)) + 1)
33 
34 struct enabled_features {
35 	uint64_t reserved:3;
36 	uint64_t sdsi:1;
37 	uint64_t reserved1:60;
38 };
39 
40 struct auth_fail_count {
41 	uint64_t key_failure_count:3;
42 	uint64_t key_failure_threshold:3;
43 	uint64_t auth_failure_count:3;
44 	uint64_t auth_failure_threshold:3;
45 	uint64_t reserved:52;
46 };
47 
48 struct availability {
49 	uint64_t reserved:48;
50 	uint64_t available:3;
51 	uint64_t threshold:3;
52 };
53 
54 struct sdsi_regs {
55 	uint64_t ppin;
56 	uint64_t reserved;
57 	struct enabled_features en_features;
58 	uint64_t reserved1;
59 	struct auth_fail_count auth_fail_count;
60 	struct availability prov_avail;
61 	uint64_t reserved2;
62 	uint64_t reserved3;
63 	uint64_t socket_id;
64 };
65 
66 struct sdsi_dev {
67 	struct sdsi_regs regs;
68 	char *dev_name;
69 	char *dev_path;
70 	int guid;
71 };
72 
73 enum command {
74 	CMD_NONE,
75 	CMD_SOCKET_INFO,
76 	CMD_DUMP_CERT,
77 	CMD_PROV_AKC,
78 	CMD_PROV_CAP,
79 };
80 
81 static void sdsi_list_devices(void)
82 {
83 	struct dirent *entry;
84 	DIR *aux_dir;
85 	bool found = false;
86 
87 	aux_dir = opendir(AUX_DEV_PATH);
88 	if (!aux_dir) {
89 		fprintf(stderr, "Cannot open directory %s\n", AUX_DEV_PATH);
90 		return;
91 	}
92 
93 	while ((entry = readdir(aux_dir))) {
94 		if (!strncmp(SDSI_DEV, entry->d_name, strlen(SDSI_DEV))) {
95 			found = true;
96 			printf("%s\n", entry->d_name);
97 		}
98 	}
99 
100 	if (!found)
101 		fprintf(stderr, "No sdsi devices found.\n");
102 }
103 
104 static int sdsi_update_registers(struct sdsi_dev *s)
105 {
106 	FILE *regs_ptr;
107 	int ret;
108 
109 	memset(&s->regs, 0, sizeof(s->regs));
110 
111 	/* Open the registers file */
112 	ret = chdir(s->dev_path);
113 	if (ret == -1) {
114 		perror("chdir");
115 		return ret;
116 	}
117 
118 	regs_ptr = fopen("registers", "r");
119 	if (!regs_ptr) {
120 		perror("Could not open 'registers' file");
121 		return -1;
122 	}
123 
124 	if (s->guid != GUID) {
125 		fprintf(stderr, "Unrecognized guid, 0x%x\n", s->guid);
126 		fclose(regs_ptr);
127 		return -1;
128 	}
129 
130 	/* Update register info for this guid */
131 	ret = fread(&s->regs, sizeof(uint8_t), sizeof(s->regs), regs_ptr);
132 	if (ret != sizeof(s->regs)) {
133 		fprintf(stderr, "Could not read 'registers' file\n");
134 		fclose(regs_ptr);
135 		return -1;
136 	}
137 
138 	fclose(regs_ptr);
139 
140 	return 0;
141 }
142 
143 static int sdsi_read_reg(struct sdsi_dev *s)
144 {
145 	int ret;
146 
147 	ret = sdsi_update_registers(s);
148 	if (ret)
149 		return ret;
150 
151 	/* Print register info for this guid */
152 	printf("\n");
153 	printf("Socket information for device %s\n", s->dev_name);
154 	printf("\n");
155 	printf("PPIN:                           0x%lx\n", s->regs.ppin);
156 	printf("Enabled Features\n");
157 	printf("    SDSi:                       %s\n", !!s->regs.en_features.sdsi ? "Enabled" : "Disabled");
158 	printf("Authorization Failure Count\n");
159 	printf("    AKC Failure Count:          %d\n", s->regs.auth_fail_count.key_failure_count);
160 	printf("    AKC Failure Threshold:      %d\n", s->regs.auth_fail_count.key_failure_threshold);
161 	printf("    CAP Failure Count:          %d\n", s->regs.auth_fail_count.auth_failure_count);
162 	printf("    CAP Failure Threshold:      %d\n", s->regs.auth_fail_count.auth_failure_threshold);
163 	printf("Provisioning Availability\n");
164 	printf("    Updates Available:          %d\n", s->regs.prov_avail.available);
165 	printf("    Updates Threshold:          %d\n", s->regs.prov_avail.threshold);
166 	printf("Socket ID:                      %ld\n", s->regs.socket_id & 0xF);
167 
168 	return 0;
169 }
170 
171 static int sdsi_certificate_dump(struct sdsi_dev *s)
172 {
173 	uint64_t state_certificate[512] = {0};
174 	bool first_instance;
175 	uint64_t previous;
176 	FILE *cert_ptr;
177 	int i, ret, size;
178 
179 	ret = sdsi_update_registers(s);
180 	if (ret)
181 		return ret;
182 
183 	if (!s->regs.en_features.sdsi) {
184 		fprintf(stderr, "SDSi feature is present but not enabled.");
185 		fprintf(stderr, " Unable to read state certificate");
186 		return -1;
187 	}
188 
189 	ret = chdir(s->dev_path);
190 	if (ret == -1) {
191 		perror("chdir");
192 		return ret;
193 	}
194 
195 	cert_ptr = fopen("state_certificate", "r");
196 	if (!cert_ptr) {
197 		perror("Could not open 'state_certificate' file");
198 		return -1;
199 	}
200 
201 	size = fread(state_certificate, 1, sizeof(state_certificate), cert_ptr);
202 	if (!size) {
203 		fprintf(stderr, "Could not read 'state_certificate' file\n");
204 		fclose(cert_ptr);
205 		return -1;
206 	}
207 
208 	printf("%3d: 0x%lx\n", 0, state_certificate[0]);
209 	previous = state_certificate[0];
210 	first_instance = true;
211 
212 	for (i = 1; i < (int)(round_up(size, sizeof(uint64_t))/sizeof(uint64_t)); i++) {
213 		if (state_certificate[i] == previous) {
214 			if (first_instance) {
215 				puts("*");
216 				first_instance = false;
217 			}
218 			continue;
219 		}
220 		printf("%3d: 0x%lx\n", i, state_certificate[i]);
221 		previous = state_certificate[i];
222 		first_instance = true;
223 	}
224 	printf("%3d\n", i);
225 
226 	fclose(cert_ptr);
227 
228 	return 0;
229 }
230 
231 static int sdsi_provision(struct sdsi_dev *s, char *bin_file, enum command command)
232 {
233 	int bin_fd, prov_fd, size, ret;
234 	char buf[4096] = { 0 };
235 	char cap[] = "provision_cap";
236 	char akc[] = "provision_akc";
237 	char *prov_file;
238 
239 	if (!bin_file) {
240 		fprintf(stderr, "No binary file provided\n");
241 		return -1;
242 	}
243 
244 	/* Open the binary */
245 	bin_fd = open(bin_file, O_RDONLY);
246 	if (bin_fd == -1) {
247 		fprintf(stderr, "Could not open file %s: %s\n", bin_file, strerror(errno));
248 		return bin_fd;
249 	}
250 
251 	prov_file = (command == CMD_PROV_AKC) ? akc : cap;
252 
253 	ret = chdir(s->dev_path);
254 	if (ret == -1) {
255 		perror("chdir");
256 		close(bin_fd);
257 		return ret;
258 	}
259 
260 	/* Open the provision file */
261 	prov_fd = open(prov_file, O_WRONLY);
262 	if (prov_fd == -1) {
263 		fprintf(stderr, "Could not open file %s: %s\n", prov_file, strerror(errno));
264 		close(bin_fd);
265 		return prov_fd;
266 	}
267 
268 	/* Read the binary file into the buffer */
269 	size = read(bin_fd, buf, 4096);
270 	if (size == -1) {
271 		close(bin_fd);
272 		close(prov_fd);
273 		return -1;
274 	}
275 
276 	ret = write(prov_fd, buf, size);
277 	if (ret == -1) {
278 		close(bin_fd);
279 		close(prov_fd);
280 		perror("Provisioning failed");
281 		return ret;
282 	}
283 
284 	printf("Provisioned %s file %s successfully\n", prov_file, bin_file);
285 
286 	close(bin_fd);
287 	close(prov_fd);
288 
289 	return 0;
290 }
291 
292 static int sdsi_provision_akc(struct sdsi_dev *s, char *bin_file)
293 {
294 	int ret;
295 
296 	ret = sdsi_update_registers(s);
297 	if (ret)
298 		return ret;
299 
300 	if (!s->regs.en_features.sdsi) {
301 		fprintf(stderr, "SDSi feature is present but not enabled. Unable to provision");
302 		return -1;
303 	}
304 
305 	if (!s->regs.prov_avail.available) {
306 		fprintf(stderr, "Maximum number of updates (%d) has been reached.\n",
307 			s->regs.prov_avail.threshold);
308 		return -1;
309 	}
310 
311 	if (s->regs.auth_fail_count.key_failure_count ==
312 	    s->regs.auth_fail_count.key_failure_threshold) {
313 		fprintf(stderr, "Maximum number of AKC provision failures (%d) has been reached.\n",
314 			s->regs.auth_fail_count.key_failure_threshold);
315 		fprintf(stderr, "Power cycle the system to reset the counter\n");
316 		return -1;
317 	}
318 
319 	return sdsi_provision(s, bin_file, CMD_PROV_AKC);
320 }
321 
322 static int sdsi_provision_cap(struct sdsi_dev *s, char *bin_file)
323 {
324 	int ret;
325 
326 	ret = sdsi_update_registers(s);
327 	if (ret)
328 		return ret;
329 
330 	if (!s->regs.en_features.sdsi) {
331 		fprintf(stderr, "SDSi feature is present but not enabled. Unable to provision");
332 		return -1;
333 	}
334 
335 	if (!s->regs.prov_avail.available) {
336 		fprintf(stderr, "Maximum number of updates (%d) has been reached.\n",
337 			s->regs.prov_avail.threshold);
338 		return -1;
339 	}
340 
341 	if (s->regs.auth_fail_count.auth_failure_count ==
342 	    s->regs.auth_fail_count.auth_failure_threshold) {
343 		fprintf(stderr, "Maximum number of CAP provision failures (%d) has been reached.\n",
344 			s->regs.auth_fail_count.auth_failure_threshold);
345 		fprintf(stderr, "Power cycle the system to reset the counter\n");
346 		return -1;
347 	}
348 
349 	return sdsi_provision(s, bin_file, CMD_PROV_CAP);
350 }
351 
352 static int read_sysfs_data(const char *file, int *value)
353 {
354 	char buff[16];
355 	FILE *fp;
356 
357 	fp = fopen(file, "r");
358 	if (!fp) {
359 		perror(file);
360 		return -1;
361 	}
362 
363 	if (!fgets(buff, 16, fp)) {
364 		fprintf(stderr, "Failed to read file '%s'", file);
365 		fclose(fp);
366 		return -1;
367 	}
368 
369 	fclose(fp);
370 	*value = strtol(buff, NULL, 0);
371 
372 	return 0;
373 }
374 
375 static struct sdsi_dev *sdsi_create_dev(char *dev_no)
376 {
377 	int dev_name_len = sizeof(SDSI_DEV) + strlen(dev_no) + 1;
378 	struct sdsi_dev *s;
379 	int guid;
380 	DIR *dir;
381 
382 	s = (struct sdsi_dev *)malloc(sizeof(*s));
383 	if (!s) {
384 		perror("malloc");
385 		return NULL;
386 	}
387 
388 	s->dev_name = (char *)malloc(sizeof(SDSI_DEV) + strlen(dev_no) + 1);
389 	if (!s->dev_name) {
390 		perror("malloc");
391 		free(s);
392 		return NULL;
393 	}
394 
395 	snprintf(s->dev_name, dev_name_len, "%s.%s", SDSI_DEV, dev_no);
396 
397 	s->dev_path = (char *)malloc(sizeof(AUX_DEV_PATH) + dev_name_len);
398 	if (!s->dev_path) {
399 		perror("malloc");
400 		free(s->dev_name);
401 		free(s);
402 		return NULL;
403 	}
404 
405 	snprintf(s->dev_path, sizeof(AUX_DEV_PATH) + dev_name_len, "%s%s", AUX_DEV_PATH,
406 		 s->dev_name);
407 	dir = opendir(s->dev_path);
408 	if (!dir) {
409 		fprintf(stderr, "Could not open directory '%s': %s\n", s->dev_path,
410 			strerror(errno));
411 		free(s->dev_path);
412 		free(s->dev_name);
413 		free(s);
414 		return NULL;
415 	}
416 
417 	if (chdir(s->dev_path) == -1) {
418 		perror("chdir");
419 		free(s->dev_path);
420 		free(s->dev_name);
421 		free(s);
422 		return NULL;
423 	}
424 
425 	if (read_sysfs_data("guid", &guid)) {
426 		free(s->dev_path);
427 		free(s->dev_name);
428 		free(s);
429 		return NULL;
430 	}
431 
432 	s->guid = guid;
433 
434 	return s;
435 }
436 
437 static void sdsi_free_dev(struct sdsi_dev *s)
438 {
439 	free(s->dev_path);
440 	free(s->dev_name);
441 	free(s);
442 }
443 
444 static void usage(char *prog)
445 {
446 	printf("Usage: %s [-l] [-d DEVNO [-iD] [-a FILE] [-c FILE]]\n", prog);
447 }
448 
449 static void show_help(void)
450 {
451 	printf("Commands:\n");
452 	printf("  %-18s\t%s\n", "-l, --list",		"list available sdsi devices");
453 	printf("  %-18s\t%s\n", "-d, --devno DEVNO",	"sdsi device number");
454 	printf("  %-18s\t%s\n", "-i --info",		"show socket information");
455 	printf("  %-18s\t%s\n", "-D --dump",		"dump state certificate data");
456 	printf("  %-18s\t%s\n", "-a --akc FILE",	"provision socket with AKC FILE");
457 	printf("  %-18s\t%s\n", "-c --cap FILE>",	"provision socket with CAP FILE");
458 }
459 
460 int main(int argc, char *argv[])
461 {
462 	char bin_file[PATH_MAX], *dev_no = NULL;
463 	char *progname;
464 	enum command command = CMD_NONE;
465 	struct sdsi_dev *s;
466 	int ret = 0, opt;
467 	int option_index = 0;
468 
469 	static struct option long_options[] = {
470 		{"akc",		required_argument,	0, 'a'},
471 		{"cap",		required_argument,	0, 'c'},
472 		{"devno",	required_argument,	0, 'd'},
473 		{"dump",	no_argument,		0, 'D'},
474 		{"help",	no_argument,		0, 'h'},
475 		{"info",	no_argument,		0, 'i'},
476 		{"list",	no_argument,		0, 'l'},
477 		{0,		0,			0, 0 }
478 	};
479 
480 
481 	progname = argv[0];
482 
483 	while ((opt = getopt_long_only(argc, argv, "+a:c:d:Da:c:h", long_options,
484 			&option_index)) != -1) {
485 		switch (opt) {
486 		case 'd':
487 			dev_no = optarg;
488 			break;
489 		case 'l':
490 			sdsi_list_devices();
491 			return 0;
492 		case 'i':
493 			command = CMD_SOCKET_INFO;
494 			break;
495 		case 'D':
496 			command = CMD_DUMP_CERT;
497 			break;
498 		case 'a':
499 		case 'c':
500 			if (!access(optarg, F_OK) == 0) {
501 				fprintf(stderr, "Could not open file '%s': %s\n", optarg,
502 					strerror(errno));
503 				return -1;
504 			}
505 
506 			if (!realpath(optarg, bin_file)) {
507 				perror("realpath");
508 				return -1;
509 			}
510 
511 			command = (opt == 'a') ? CMD_PROV_AKC : CMD_PROV_CAP;
512 			break;
513 		case 'h':
514 			usage(progname);
515 			show_help();
516 			return 0;
517 		default:
518 			usage(progname);
519 			return -1;
520 		}
521 	}
522 
523 	if (!dev_no) {
524 		if (command != CMD_NONE)
525 			fprintf(stderr, "Missing device number, DEVNO, for this command\n");
526 		usage(progname);
527 		return -1;
528 	}
529 
530 	s = sdsi_create_dev(dev_no);
531 	if (!s)
532 		return -1;
533 
534 	/* Run the command */
535 	switch (command) {
536 	case CMD_NONE:
537 		fprintf(stderr, "Missing command for device %s\n", dev_no);
538 		usage(progname);
539 		break;
540 	case CMD_SOCKET_INFO:
541 		ret = sdsi_read_reg(s);
542 		break;
543 	case CMD_DUMP_CERT:
544 		ret = sdsi_certificate_dump(s);
545 		break;
546 	case CMD_PROV_AKC:
547 		ret = sdsi_provision_akc(s, bin_file);
548 		break;
549 	case CMD_PROV_CAP:
550 		ret = sdsi_provision_cap(s, bin_file);
551 		break;
552 	}
553 
554 
555 	sdsi_free_dev(s);
556 
557 	return ret;
558 }
559