// SPDX-License-Identifier: GPL-2.0 /* * sdsi: Intel Software Defined Silicon tool for provisioning certificates * and activation payloads on supported cpus. * * See https://github.com/intel/intel-sdsi/blob/master/os-interface.rst * for register descriptions. * * Copyright (C) 2022 Intel Corporation. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #define SDSI_DEV "intel_vsec.sdsi" #define AUX_DEV_PATH "/sys/bus/auxiliary/devices/" #define SDSI_PATH (AUX_DEV_DIR SDSI_DEV) #define GUID 0x6dd191 #define REGISTERS_MIN_SIZE 72 #define __round_mask(x, y) ((__typeof__(x))((y) - 1)) #define round_up(x, y) ((((x) - 1) | __round_mask(x, y)) + 1) struct enabled_features { uint64_t reserved:3; uint64_t sdsi:1; uint64_t reserved1:60; }; struct auth_fail_count { uint64_t key_failure_count:3; uint64_t key_failure_threshold:3; uint64_t auth_failure_count:3; uint64_t auth_failure_threshold:3; uint64_t reserved:52; }; struct availability { uint64_t reserved:48; uint64_t available:3; uint64_t threshold:3; }; struct sdsi_regs { uint64_t ppin; uint64_t reserved; struct enabled_features en_features; uint64_t reserved1; struct auth_fail_count auth_fail_count; struct availability prov_avail; uint64_t reserved2; uint64_t reserved3; uint64_t socket_id; }; struct sdsi_dev { struct sdsi_regs regs; char *dev_name; char *dev_path; int guid; }; enum command { CMD_NONE, CMD_SOCKET_INFO, CMD_DUMP_CERT, CMD_PROV_AKC, CMD_PROV_CAP, }; static void sdsi_list_devices(void) { struct dirent *entry; DIR *aux_dir; bool found = false; aux_dir = opendir(AUX_DEV_PATH); if (!aux_dir) { fprintf(stderr, "Cannot open directory %s\n", AUX_DEV_PATH); return; } while ((entry = readdir(aux_dir))) { if (!strncmp(SDSI_DEV, entry->d_name, strlen(SDSI_DEV))) { found = true; printf("%s\n", entry->d_name); } } if (!found) fprintf(stderr, "No sdsi devices found.\n"); } static int sdsi_update_registers(struct sdsi_dev *s) { FILE *regs_ptr; int ret; memset(&s->regs, 0, sizeof(s->regs)); /* Open the registers file */ ret = chdir(s->dev_path); if (ret == -1) { perror("chdir"); return ret; } regs_ptr = fopen("registers", "r"); if (!regs_ptr) { perror("Could not open 'registers' file"); return -1; } if (s->guid != GUID) { fprintf(stderr, "Unrecognized guid, 0x%x\n", s->guid); fclose(regs_ptr); return -1; } /* Update register info for this guid */ ret = fread(&s->regs, sizeof(uint8_t), sizeof(s->regs), regs_ptr); if (ret != sizeof(s->regs)) { fprintf(stderr, "Could not read 'registers' file\n"); fclose(regs_ptr); return -1; } fclose(regs_ptr); return 0; } static int sdsi_read_reg(struct sdsi_dev *s) { int ret; ret = sdsi_update_registers(s); if (ret) return ret; /* Print register info for this guid */ printf("\n"); printf("Socket information for device %s\n", s->dev_name); printf("\n"); printf("PPIN: 0x%lx\n", s->regs.ppin); printf("Enabled Features\n"); printf(" SDSi: %s\n", !!s->regs.en_features.sdsi ? "Enabled" : "Disabled"); printf("Authorization Failure Count\n"); printf(" AKC Failure Count: %d\n", s->regs.auth_fail_count.key_failure_count); printf(" AKC Failure Threshold: %d\n", s->regs.auth_fail_count.key_failure_threshold); printf(" CAP Failure Count: %d\n", s->regs.auth_fail_count.auth_failure_count); printf(" CAP Failure Threshold: %d\n", s->regs.auth_fail_count.auth_failure_threshold); printf("Provisioning Availability\n"); printf(" Updates Available: %d\n", s->regs.prov_avail.available); printf(" Updates Threshold: %d\n", s->regs.prov_avail.threshold); printf("Socket ID: %ld\n", s->regs.socket_id & 0xF); return 0; } static int sdsi_certificate_dump(struct sdsi_dev *s) { uint64_t state_certificate[512] = {0}; bool first_instance; uint64_t previous; FILE *cert_ptr; int i, ret, size; ret = sdsi_update_registers(s); if (ret) return ret; if (!s->regs.en_features.sdsi) { fprintf(stderr, "SDSi feature is present but not enabled."); fprintf(stderr, " Unable to read state certificate"); return -1; } ret = chdir(s->dev_path); if (ret == -1) { perror("chdir"); return ret; } cert_ptr = fopen("state_certificate", "r"); if (!cert_ptr) { perror("Could not open 'state_certificate' file"); return -1; } size = fread(state_certificate, 1, sizeof(state_certificate), cert_ptr); if (!size) { fprintf(stderr, "Could not read 'state_certificate' file\n"); fclose(cert_ptr); return -1; } printf("%3d: 0x%lx\n", 0, state_certificate[0]); previous = state_certificate[0]; first_instance = true; for (i = 1; i < (int)(round_up(size, sizeof(uint64_t))/sizeof(uint64_t)); i++) { if (state_certificate[i] == previous) { if (first_instance) { puts("*"); first_instance = false; } continue; } printf("%3d: 0x%lx\n", i, state_certificate[i]); previous = state_certificate[i]; first_instance = true; } printf("%3d\n", i); fclose(cert_ptr); return 0; } static int sdsi_provision(struct sdsi_dev *s, char *bin_file, enum command command) { int bin_fd, prov_fd, size, ret; char buf[4096] = { 0 }; char cap[] = "provision_cap"; char akc[] = "provision_akc"; char *prov_file; if (!bin_file) { fprintf(stderr, "No binary file provided\n"); return -1; } /* Open the binary */ bin_fd = open(bin_file, O_RDONLY); if (bin_fd == -1) { fprintf(stderr, "Could not open file %s: %s\n", bin_file, strerror(errno)); return bin_fd; } prov_file = (command == CMD_PROV_AKC) ? akc : cap; ret = chdir(s->dev_path); if (ret == -1) { perror("chdir"); close(bin_fd); return ret; } /* Open the provision file */ prov_fd = open(prov_file, O_WRONLY); if (prov_fd == -1) { fprintf(stderr, "Could not open file %s: %s\n", prov_file, strerror(errno)); close(bin_fd); return prov_fd; } /* Read the binary file into the buffer */ size = read(bin_fd, buf, 4096); if (size == -1) { close(bin_fd); close(prov_fd); return -1; } ret = write(prov_fd, buf, size); if (ret == -1) { close(bin_fd); close(prov_fd); perror("Provisioning failed"); return ret; } printf("Provisioned %s file %s successfully\n", prov_file, bin_file); close(bin_fd); close(prov_fd); return 0; } static int sdsi_provision_akc(struct sdsi_dev *s, char *bin_file) { int ret; ret = sdsi_update_registers(s); if (ret) return ret; if (!s->regs.en_features.sdsi) { fprintf(stderr, "SDSi feature is present but not enabled. Unable to provision"); return -1; } if (!s->regs.prov_avail.available) { fprintf(stderr, "Maximum number of updates (%d) has been reached.\n", s->regs.prov_avail.threshold); return -1; } if (s->regs.auth_fail_count.key_failure_count == s->regs.auth_fail_count.key_failure_threshold) { fprintf(stderr, "Maximum number of AKC provision failures (%d) has been reached.\n", s->regs.auth_fail_count.key_failure_threshold); fprintf(stderr, "Power cycle the system to reset the counter\n"); return -1; } return sdsi_provision(s, bin_file, CMD_PROV_AKC); } static int sdsi_provision_cap(struct sdsi_dev *s, char *bin_file) { int ret; ret = sdsi_update_registers(s); if (ret) return ret; if (!s->regs.en_features.sdsi) { fprintf(stderr, "SDSi feature is present but not enabled. Unable to provision"); return -1; } if (!s->regs.prov_avail.available) { fprintf(stderr, "Maximum number of updates (%d) has been reached.\n", s->regs.prov_avail.threshold); return -1; } if (s->regs.auth_fail_count.auth_failure_count == s->regs.auth_fail_count.auth_failure_threshold) { fprintf(stderr, "Maximum number of CAP provision failures (%d) has been reached.\n", s->regs.auth_fail_count.auth_failure_threshold); fprintf(stderr, "Power cycle the system to reset the counter\n"); return -1; } return sdsi_provision(s, bin_file, CMD_PROV_CAP); } static int read_sysfs_data(const char *file, int *value) { char buff[16]; FILE *fp; fp = fopen(file, "r"); if (!fp) { perror(file); return -1; } if (!fgets(buff, 16, fp)) { fprintf(stderr, "Failed to read file '%s'", file); fclose(fp); return -1; } fclose(fp); *value = strtol(buff, NULL, 0); return 0; } static struct sdsi_dev *sdsi_create_dev(char *dev_no) { int dev_name_len = sizeof(SDSI_DEV) + strlen(dev_no) + 1; struct sdsi_dev *s; int guid; DIR *dir; s = (struct sdsi_dev *)malloc(sizeof(*s)); if (!s) { perror("malloc"); return NULL; } s->dev_name = (char *)malloc(sizeof(SDSI_DEV) + strlen(dev_no) + 1); if (!s->dev_name) { perror("malloc"); free(s); return NULL; } snprintf(s->dev_name, dev_name_len, "%s.%s", SDSI_DEV, dev_no); s->dev_path = (char *)malloc(sizeof(AUX_DEV_PATH) + dev_name_len); if (!s->dev_path) { perror("malloc"); free(s->dev_name); free(s); return NULL; } snprintf(s->dev_path, sizeof(AUX_DEV_PATH) + dev_name_len, "%s%s", AUX_DEV_PATH, s->dev_name); dir = opendir(s->dev_path); if (!dir) { fprintf(stderr, "Could not open directory '%s': %s\n", s->dev_path, strerror(errno)); free(s->dev_path); free(s->dev_name); free(s); return NULL; } if (chdir(s->dev_path) == -1) { perror("chdir"); free(s->dev_path); free(s->dev_name); free(s); return NULL; } if (read_sysfs_data("guid", &guid)) { free(s->dev_path); free(s->dev_name); free(s); return NULL; } s->guid = guid; return s; } static void sdsi_free_dev(struct sdsi_dev *s) { free(s->dev_path); free(s->dev_name); free(s); } static void usage(char *prog) { printf("Usage: %s [-l] [-d DEVNO [-iD] [-a FILE] [-c FILE]]\n", prog); } static void show_help(void) { printf("Commands:\n"); printf(" %-18s\t%s\n", "-l, --list", "list available sdsi devices"); printf(" %-18s\t%s\n", "-d, --devno DEVNO", "sdsi device number"); printf(" %-18s\t%s\n", "-i --info", "show socket information"); printf(" %-18s\t%s\n", "-D --dump", "dump state certificate data"); printf(" %-18s\t%s\n", "-a --akc FILE", "provision socket with AKC FILE"); printf(" %-18s\t%s\n", "-c --cap FILE>", "provision socket with CAP FILE"); } int main(int argc, char *argv[]) { char bin_file[PATH_MAX], *dev_no = NULL; char *progname; enum command command = CMD_NONE; struct sdsi_dev *s; int ret = 0, opt; int option_index = 0; static struct option long_options[] = { {"akc", required_argument, 0, 'a'}, {"cap", required_argument, 0, 'c'}, {"devno", required_argument, 0, 'd'}, {"dump", no_argument, 0, 'D'}, {"help", no_argument, 0, 'h'}, {"info", no_argument, 0, 'i'}, {"list", no_argument, 0, 'l'}, {0, 0, 0, 0 } }; progname = argv[0]; while ((opt = getopt_long_only(argc, argv, "+a:c:d:Da:c:h", long_options, &option_index)) != -1) { switch (opt) { case 'd': dev_no = optarg; break; case 'l': sdsi_list_devices(); return 0; case 'i': command = CMD_SOCKET_INFO; break; case 'D': command = CMD_DUMP_CERT; break; case 'a': case 'c': if (!access(optarg, F_OK) == 0) { fprintf(stderr, "Could not open file '%s': %s\n", optarg, strerror(errno)); return -1; } if (!realpath(optarg, bin_file)) { perror("realpath"); return -1; } command = (opt == 'a') ? CMD_PROV_AKC : CMD_PROV_CAP; break; case 'h': usage(progname); show_help(); return 0; default: usage(progname); return -1; } } if (!dev_no) { if (command != CMD_NONE) fprintf(stderr, "Missing device number, DEVNO, for this command\n"); usage(progname); return -1; } s = sdsi_create_dev(dev_no); if (!s) return -1; /* Run the command */ switch (command) { case CMD_NONE: fprintf(stderr, "Missing command for device %s\n", dev_no); usage(progname); break; case CMD_SOCKET_INFO: ret = sdsi_read_reg(s); break; case CMD_DUMP_CERT: ret = sdsi_certificate_dump(s); break; case CMD_PROV_AKC: ret = sdsi_provision_akc(s, bin_file); break; case CMD_PROV_CAP: ret = sdsi_provision_cap(s, bin_file); break; } sdsi_free_dev(s); return ret; }