| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * sdsi: Intel On Demand (formerly 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 <dirent.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <getopt.h> |
| #include <stdbool.h> |
| #include <stdio.h> |
| #include <stdint.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| #include <sys/types.h> |
| |
| #ifndef __packed |
| #define __packed __attribute__((packed)) |
| #endif |
| |
| #define min(x, y) ({ \ |
| typeof(x) _min1 = (x); \ |
| typeof(y) _min2 = (y); \ |
| (void) (&_min1 == &_min2); \ |
| _min1 < _min2 ? _min1 : _min2; }) |
| |
| #define SDSI_DEV "intel_vsec.sdsi" |
| #define AUX_DEV_PATH "/sys/bus/auxiliary/devices/" |
| #define SDSI_PATH (AUX_DEV_DIR SDSI_DEV) |
| #define GUID_V1 0x6dd191 |
| #define REGS_SIZE_GUID_V1 72 |
| #define GUID_V2 0xF210D9EF |
| #define REGS_SIZE_GUID_V2 80 |
| #define STATE_CERT_MAX_SIZE 4096 |
| #define METER_CERT_MAX_SIZE 4096 |
| #define STATE_MAX_NUM_LICENSES 16 |
| #define STATE_MAX_NUM_IN_BUNDLE (uint32_t)8 |
| #define METER_MAX_NUM_BUNDLES 8 |
| |
| #define __round_mask(x, y) ((__typeof__(x))((y) - 1)) |
| #define round_up(x, y) ((((x) - 1) | __round_mask(x, y)) + 1) |
| |
| struct nvram_content_auth_err_sts { |
| uint64_t reserved:3; |
| uint64_t sdsi_content_auth_err:1; |
| uint64_t reserved1:1; |
| uint64_t sdsi_metering_auth_err:1; |
| uint64_t reserved2:58; |
| }; |
| |
| struct enabled_features { |
| uint64_t reserved:3; |
| uint64_t sdsi:1; |
| uint64_t reserved1:8; |
| uint64_t attestation:1; |
| uint64_t reserved2:13; |
| uint64_t metering:1; |
| uint64_t reserved3:37; |
| }; |
| |
| struct key_provision_status { |
| uint64_t reserved:1; |
| uint64_t license_key_provisioned:1; |
| uint64_t reserved2:62; |
| }; |
| |
| 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; |
| uint64_t reserved2:10; |
| }; |
| |
| struct nvram_update_limit { |
| uint64_t reserved:12; |
| uint64_t sdsi_50_pct:1; |
| uint64_t sdsi_75_pct:1; |
| uint64_t sdsi_90_pct:1; |
| uint64_t reserved2:49; |
| }; |
| |
| struct sdsi_regs { |
| uint64_t ppin; |
| struct nvram_content_auth_err_sts auth_err_sts; |
| struct enabled_features en_features; |
| struct key_provision_status key_prov_sts; |
| struct auth_fail_count auth_fail_count; |
| struct availability prov_avail; |
| struct nvram_update_limit limits; |
| uint64_t pcu_cr3_capid_cfg; |
| union { |
| struct { |
| uint64_t socket_id; |
| } v1; |
| struct { |
| uint64_t reserved; |
| uint64_t socket_id; |
| uint64_t reserved2; |
| } v2; |
| } extra; |
| }; |
| #define CONTENT_TYPE_LK_ENC 0xD |
| #define CONTENT_TYPE_LK_BLOB_ENC 0xE |
| |
| struct state_certificate { |
| uint32_t content_type; |
| uint32_t region_rev_id; |
| uint32_t header_size; |
| uint32_t total_size; |
| uint32_t key_size; |
| uint32_t num_licenses; |
| }; |
| |
| struct license_key_info { |
| uint32_t key_rev_id; |
| uint64_t key_image_content[6]; |
| } __packed; |
| |
| #define LICENSE_BLOB_SIZE(l) (((l) & 0x7fffffff) * 4) |
| #define LICENSE_VALID(l) (!!((l) & 0x80000000)) |
| |
| // License Group Types |
| #define LBT_ONE_TIME_UPGRADE 1 |
| #define LBT_METERED_UPGRADE 2 |
| |
| struct license_blob_content { |
| uint32_t type; |
| uint64_t id; |
| uint64_t ppin; |
| uint64_t previous_ppin; |
| uint32_t rev_id; |
| uint32_t num_bundles; |
| } __packed; |
| |
| struct bundle_encoding { |
| uint32_t encoding; |
| uint32_t encoding_rsvd[7]; |
| }; |
| |
| struct meter_certificate { |
| uint32_t block_signature; |
| uint32_t counter_unit; |
| uint64_t ppin; |
| uint32_t bundle_length; |
| uint32_t reserved; |
| uint32_t mmrc_encoding; |
| uint32_t mmrc_counter; |
| }; |
| |
| struct bundle_encoding_counter { |
| uint32_t encoding; |
| uint32_t counter; |
| }; |
| |
| struct sdsi_dev { |
| struct sdsi_regs regs; |
| struct state_certificate sc; |
| char *dev_name; |
| char *dev_path; |
| uint32_t guid; |
| }; |
| |
| enum command { |
| CMD_SOCKET_INFO, |
| CMD_METER_CERT, |
| CMD_STATE_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 On Demand 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_V1 && s->guid != GUID_V2) { |
| 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 ((s->guid == GUID_V1 && ret != REGS_SIZE_GUID_V1) || |
| (s->guid == GUID_V2 && ret != REGS_SIZE_GUID_V2)) { |
| 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("NVRAM Content Authorization Error Status\n"); |
| printf(" SDSi Auth Err Sts: %s\n", !!s->regs.auth_err_sts.sdsi_content_auth_err ? "Error" : "Okay"); |
| |
| if (!!s->regs.en_features.metering) |
| printf(" Metering Auth Err Sts: %s\n", !!s->regs.auth_err_sts.sdsi_metering_auth_err ? "Error" : "Okay"); |
| |
| printf("Enabled Features\n"); |
| printf(" On Demand: %s\n", !!s->regs.en_features.sdsi ? "Enabled" : "Disabled"); |
| printf(" Attestation: %s\n", !!s->regs.en_features.attestation ? "Enabled" : "Disabled"); |
| printf(" On Demand: %s\n", !!s->regs.en_features.sdsi ? "Enabled" : "Disabled"); |
| printf(" Metering: %s\n", !!s->regs.en_features.metering ? "Enabled" : "Disabled"); |
| printf("License Key (AKC) Provisioned: %s\n", !!s->regs.key_prov_sts.license_key_provisioned ? "Yes" : "No"); |
| 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("NVRAM Udate Limit\n"); |
| printf(" 50%% Limit Reached: %s\n", !!s->regs.limits.sdsi_50_pct ? "Yes" : "No"); |
| printf(" 75%% Limit Reached: %s\n", !!s->regs.limits.sdsi_75_pct ? "Yes" : "No"); |
| printf(" 90%% Limit Reached: %s\n", !!s->regs.limits.sdsi_90_pct ? "Yes" : "No"); |
| if (s->guid == GUID_V1) |
| printf("Socket ID: %ld\n", s->regs.extra.v1.socket_id & 0xF); |
| else |
| printf("Socket ID: %ld\n", s->regs.extra.v2.socket_id & 0xF); |
| |
| return 0; |
| } |
| |
| static char *license_blob_type(uint32_t type) |
| { |
| switch (type) { |
| case LBT_ONE_TIME_UPGRADE: |
| return "One time upgrade"; |
| case LBT_METERED_UPGRADE: |
| return "Metered upgrade"; |
| default: |
| return "Unknown license blob type"; |
| } |
| } |
| |
| static char *content_type(uint32_t type) |
| { |
| switch (type) { |
| case CONTENT_TYPE_LK_ENC: |
| return "Licencse key encoding"; |
| case CONTENT_TYPE_LK_BLOB_ENC: |
| return "License key + Blob encoding"; |
| default: |
| return "Unknown content type"; |
| } |
| } |
| |
| static void get_feature(uint32_t encoding, char *feature) |
| { |
| char *name = (char *)&encoding; |
| |
| feature[3] = name[0]; |
| feature[2] = name[1]; |
| feature[1] = name[2]; |
| feature[0] = name[3]; |
| } |
| |
| static int sdsi_meter_cert_show(struct sdsi_dev *s) |
| { |
| char buf[METER_CERT_MAX_SIZE] = {0}; |
| struct bundle_encoding_counter *bec; |
| struct meter_certificate *mc; |
| uint32_t count = 0; |
| FILE *cert_ptr; |
| int 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.\n"); |
| fprintf(stderr, " Unable to read meter certificate\n"); |
| return -1; |
| } |
| |
| if (!s->regs.en_features.metering) { |
| fprintf(stderr, "Metering not supporting on this socket.\n"); |
| return -1; |
| } |
| |
| ret = chdir(s->dev_path); |
| if (ret == -1) { |
| perror("chdir"); |
| return ret; |
| } |
| |
| cert_ptr = fopen("meter_certificate", "r"); |
| if (!cert_ptr) { |
| perror("Could not open 'meter_certificate' file"); |
| return -1; |
| } |
| |
| size = fread(buf, 1, sizeof(buf), cert_ptr); |
| if (!size) { |
| fprintf(stderr, "Could not read 'meter_certificate' file\n"); |
| fclose(cert_ptr); |
| return -1; |
| } |
| fclose(cert_ptr); |
| |
| mc = (struct meter_certificate *)buf; |
| |
| printf("\n"); |
| printf("Meter certificate for device %s\n", s->dev_name); |
| printf("\n"); |
| printf("Block Signature: 0x%x\n", mc->block_signature); |
| printf("Count Unit: %dms\n", mc->counter_unit); |
| printf("PPIN: 0x%lx\n", mc->ppin); |
| printf("Feature Bundle Length: %d\n", mc->bundle_length); |
| printf("MMRC encoding: %d\n", mc->mmrc_encoding); |
| printf("MMRC counter: %d\n", mc->mmrc_counter); |
| if (mc->bundle_length % 8) { |
| fprintf(stderr, "Invalid bundle length\n"); |
| return -1; |
| } |
| |
| if (mc->bundle_length > METER_MAX_NUM_BUNDLES * 8) { |
| fprintf(stderr, "More than %d bundles: %d\n", |
| METER_MAX_NUM_BUNDLES, mc->bundle_length / 8); |
| return -1; |
| } |
| |
| bec = (void *)(mc) + sizeof(mc); |
| |
| printf("Number of Feature Counters: %d\n", mc->bundle_length / 8); |
| while (count++ < mc->bundle_length / 8) { |
| char feature[5]; |
| |
| feature[4] = '\0'; |
| get_feature(bec[count].encoding, feature); |
| printf(" %s: %d\n", feature, bec[count].counter); |
| } |
| |
| return 0; |
| } |
| |
| static int sdsi_state_cert_show(struct sdsi_dev *s) |
| { |
| char buf[STATE_CERT_MAX_SIZE] = {0}; |
| struct state_certificate *sc; |
| struct license_key_info *lki; |
| uint32_t offset = 0; |
| uint32_t count = 0; |
| FILE *cert_ptr; |
| int ret, size; |
| |
| ret = sdsi_update_registers(s); |
| if (ret) |
| return ret; |
| |
| if (!s->regs.en_features.sdsi) { |
| fprintf(stderr, "On Demand 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(buf, 1, sizeof(buf), cert_ptr); |
| if (!size) { |
| fprintf(stderr, "Could not read 'state_certificate' file\n"); |
| fclose(cert_ptr); |
| return -1; |
| } |
| fclose(cert_ptr); |
| |
| sc = (struct state_certificate *)buf; |
| |
| /* Print register info for this guid */ |
| printf("\n"); |
| printf("State certificate for device %s\n", s->dev_name); |
| printf("\n"); |
| printf("Content Type: %s\n", content_type(sc->content_type)); |
| printf("Region Revision ID: %d\n", sc->region_rev_id); |
| printf("Header Size: %d\n", sc->header_size * 4); |
| printf("Total Size: %d\n", sc->total_size); |
| printf("OEM Key Size: %d\n", sc->key_size * 4); |
| printf("Number of Licenses: %d\n", sc->num_licenses); |
| |
| /* Skip over the license sizes 4 bytes per license) to get the license key info */ |
| lki = (void *)sc + sizeof(*sc) + (4 * sc->num_licenses); |
| |
| printf("License blob Info:\n"); |
| printf(" License Key Revision ID: 0x%x\n", lki->key_rev_id); |
| printf(" License Key Image Content: 0x%lx%lx%lx%lx%lx%lx\n", |
| lki->key_image_content[5], lki->key_image_content[4], |
| lki->key_image_content[3], lki->key_image_content[2], |
| lki->key_image_content[1], lki->key_image_content[0]); |
| |
| while (count++ < sc->num_licenses) { |
| uint32_t blob_size_field = *(uint32_t *)(buf + 0x14 + count * 4); |
| uint32_t blob_size = LICENSE_BLOB_SIZE(blob_size_field); |
| bool license_valid = LICENSE_VALID(blob_size_field); |
| struct license_blob_content *lbc = |
| (void *)(sc) + // start of the state certificate |
| sizeof(*sc) + // size of the state certificate |
| (4 * sc->num_licenses) + // total size of the blob size blocks |
| sizeof(*lki) + // size of the license key info |
| offset; // offset to this blob content |
| struct bundle_encoding *bundle = (void *)(lbc) + sizeof(*lbc); |
| char feature[5]; |
| uint32_t i; |
| |
| printf(" Blob %d:\n", count - 1); |
| printf(" License blob size: %u\n", blob_size); |
| printf(" License is valid: %s\n", license_valid ? "Yes" : "No"); |
| printf(" License blob type: %s\n", license_blob_type(lbc->type)); |
| printf(" License blob ID: 0x%lx\n", lbc->id); |
| printf(" PPIN: 0x%lx\n", lbc->ppin); |
| printf(" Previous PPIN: 0x%lx\n", lbc->previous_ppin); |
| printf(" Blob revision ID: %u\n", lbc->rev_id); |
| printf(" Number of Features: %u\n", lbc->num_bundles); |
| |
| feature[4] = '\0'; |
| |
| for (i = 0; i < min(lbc->num_bundles, STATE_MAX_NUM_IN_BUNDLE); i++) { |
| get_feature(bundle[i].encoding, feature); |
| printf(" Feature %d: %s\n", i, feature); |
| } |
| |
| if (lbc->num_bundles > STATE_MAX_NUM_IN_BUNDLE) |
| fprintf(stderr, " Warning: %d > %d licenses in bundle reported.\n", |
| lbc->num_bundles, STATE_MAX_NUM_IN_BUNDLE); |
| |
| offset += blob_size; |
| }; |
| |
| 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[STATE_CERT_MAX_SIZE] = { 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, STATE_CERT_MAX_SIZE); |
| 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, "On Demand 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, "On Demand 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 [-i] [-s] [-m] [-a FILE] [-c FILE]]\n", prog); |
| } |
| |
| static void show_help(void) |
| { |
| printf("Commands:\n"); |
| printf(" %-18s\t%s\n", "-l, --list", "list available On Demand devices"); |
| printf(" %-18s\t%s\n", "-d, --devno DEVNO", "On Demand device number"); |
| printf(" %-18s\t%s\n", "-i, --info", "show socket information"); |
| printf(" %-18s\t%s\n", "-s, --state", "show state certificate"); |
| printf(" %-18s\t%s\n", "-m, --meter", "show meter certificate"); |
| 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; |
| bool device_selected = false; |
| char *progname; |
| enum command command = -1; |
| 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'}, |
| {"help", no_argument, 0, 'h'}, |
| {"info", no_argument, 0, 'i'}, |
| {"list", no_argument, 0, 'l'}, |
| {"meter", no_argument, 0, 'm'}, |
| {"state", no_argument, 0, 's'}, |
| {0, 0, 0, 0 } |
| }; |
| |
| |
| progname = argv[0]; |
| |
| while ((opt = getopt_long_only(argc, argv, "+a:c:d:hilms", long_options, |
| &option_index)) != -1) { |
| switch (opt) { |
| case 'd': |
| dev_no = optarg; |
| device_selected = true; |
| break; |
| case 'l': |
| sdsi_list_devices(); |
| return 0; |
| case 'i': |
| command = CMD_SOCKET_INFO; |
| break; |
| case 'm': |
| command = CMD_METER_CERT; |
| break; |
| case 's': |
| command = CMD_STATE_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 (device_selected) { |
| s = sdsi_create_dev(dev_no); |
| if (!s) |
| return -1; |
| |
| switch (command) { |
| case CMD_SOCKET_INFO: |
| ret = sdsi_read_reg(s); |
| break; |
| case CMD_METER_CERT: |
| ret = sdsi_meter_cert_show(s); |
| break; |
| case CMD_STATE_CERT: |
| ret = sdsi_state_cert_show(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; |
| default: |
| fprintf(stderr, "No command specified\n"); |
| return -1; |
| } |
| |
| sdsi_free_dev(s); |
| |
| } else { |
| fprintf(stderr, "No device specified\n"); |
| return -1; |
| } |
| |
| return ret; |
| } |