12e192b24SSimon Glass /* 22e192b24SSimon Glass * Copyright (c) 2013 The Chromium OS Authors. 32e192b24SSimon Glass * 42e192b24SSimon Glass * SPDX-License-Identifier: GPL-2.0+ 52e192b24SSimon Glass */ 62e192b24SSimon Glass 72e192b24SSimon Glass #include <common.h> 82e192b24SSimon Glass #include <command.h> 92e192b24SSimon Glass #include <dm.h> 102e192b24SSimon Glass #include <malloc.h> 112e192b24SSimon Glass #include <tpm.h> 122e192b24SSimon Glass #include <asm/unaligned.h> 132e192b24SSimon Glass #include <linux/string.h> 142e192b24SSimon Glass 152e192b24SSimon Glass /* Useful constants */ 162e192b24SSimon Glass enum { 172e192b24SSimon Glass DIGEST_LENGTH = 20, 182e192b24SSimon Glass /* max lengths, valid for RSA keys <= 2048 bits */ 192e192b24SSimon Glass TPM_PUBKEY_MAX_LENGTH = 288, 202e192b24SSimon Glass }; 212e192b24SSimon Glass 222e192b24SSimon Glass /** 232e192b24SSimon Glass * Print a byte string in hexdecimal format, 16-bytes per line. 242e192b24SSimon Glass * 252e192b24SSimon Glass * @param data byte string to be printed 262e192b24SSimon Glass * @param count number of bytes to be printed 272e192b24SSimon Glass */ 282e192b24SSimon Glass static void print_byte_string(uint8_t *data, size_t count) 292e192b24SSimon Glass { 302e192b24SSimon Glass int i, print_newline = 0; 312e192b24SSimon Glass 322e192b24SSimon Glass for (i = 0; i < count; i++) { 332e192b24SSimon Glass printf(" %02x", data[i]); 342e192b24SSimon Glass print_newline = (i % 16 == 15); 352e192b24SSimon Glass if (print_newline) 362e192b24SSimon Glass putc('\n'); 372e192b24SSimon Glass } 382e192b24SSimon Glass /* Avoid duplicated newline at the end */ 392e192b24SSimon Glass if (!print_newline) 402e192b24SSimon Glass putc('\n'); 412e192b24SSimon Glass } 422e192b24SSimon Glass 432e192b24SSimon Glass /** 442e192b24SSimon Glass * Convert a text string of hexdecimal values into a byte string. 452e192b24SSimon Glass * 462e192b24SSimon Glass * @param bytes text string of hexdecimal values with no space 472e192b24SSimon Glass * between them 482e192b24SSimon Glass * @param data output buffer for byte string. The caller has to make 492e192b24SSimon Glass * sure it is large enough for storing the output. If 502e192b24SSimon Glass * NULL is passed, a large enough buffer will be allocated, 512e192b24SSimon Glass * and the caller must free it. 522e192b24SSimon Glass * @param count_ptr output variable for the length of byte string 532e192b24SSimon Glass * @return pointer to output buffer 542e192b24SSimon Glass */ 552e192b24SSimon Glass static void *parse_byte_string(char *bytes, uint8_t *data, size_t *count_ptr) 562e192b24SSimon Glass { 572e192b24SSimon Glass char byte[3]; 582e192b24SSimon Glass size_t count, length; 592e192b24SSimon Glass int i; 602e192b24SSimon Glass 612e192b24SSimon Glass if (!bytes) 622e192b24SSimon Glass return NULL; 632e192b24SSimon Glass length = strlen(bytes); 642e192b24SSimon Glass count = length / 2; 652e192b24SSimon Glass 662e192b24SSimon Glass if (!data) 672e192b24SSimon Glass data = malloc(count); 682e192b24SSimon Glass if (!data) 692e192b24SSimon Glass return NULL; 702e192b24SSimon Glass 712e192b24SSimon Glass byte[2] = '\0'; 722e192b24SSimon Glass for (i = 0; i < length; i += 2) { 732e192b24SSimon Glass byte[0] = bytes[i]; 742e192b24SSimon Glass byte[1] = bytes[i + 1]; 752e192b24SSimon Glass data[i / 2] = (uint8_t)simple_strtoul(byte, NULL, 16); 762e192b24SSimon Glass } 772e192b24SSimon Glass 782e192b24SSimon Glass if (count_ptr) 792e192b24SSimon Glass *count_ptr = count; 802e192b24SSimon Glass 812e192b24SSimon Glass return data; 822e192b24SSimon Glass } 832e192b24SSimon Glass 842e192b24SSimon Glass /** 852e192b24SSimon Glass * report_return_code() - Report any error and return failure or success 862e192b24SSimon Glass * 872e192b24SSimon Glass * @param return_code TPM command return code 882e192b24SSimon Glass * @return value of enum command_ret_t 892e192b24SSimon Glass */ 902e192b24SSimon Glass static int report_return_code(int return_code) 912e192b24SSimon Glass { 922e192b24SSimon Glass if (return_code) { 932e192b24SSimon Glass printf("Error: %d\n", return_code); 942e192b24SSimon Glass return CMD_RET_FAILURE; 952e192b24SSimon Glass } else { 962e192b24SSimon Glass return CMD_RET_SUCCESS; 972e192b24SSimon Glass } 982e192b24SSimon Glass } 992e192b24SSimon Glass 1002e192b24SSimon Glass /** 1012e192b24SSimon Glass * Return number of values defined by a type string. 1022e192b24SSimon Glass * 1032e192b24SSimon Glass * @param type_str type string 1042e192b24SSimon Glass * @return number of values of type string 1052e192b24SSimon Glass */ 1062e192b24SSimon Glass static int type_string_get_num_values(const char *type_str) 1072e192b24SSimon Glass { 1082e192b24SSimon Glass return strlen(type_str); 1092e192b24SSimon Glass } 1102e192b24SSimon Glass 1112e192b24SSimon Glass /** 1122e192b24SSimon Glass * Return total size of values defined by a type string. 1132e192b24SSimon Glass * 1142e192b24SSimon Glass * @param type_str type string 1152e192b24SSimon Glass * @return total size of values of type string, or 0 if type string 1162e192b24SSimon Glass * contains illegal type character. 1172e192b24SSimon Glass */ 1182e192b24SSimon Glass static size_t type_string_get_space_size(const char *type_str) 1192e192b24SSimon Glass { 1202e192b24SSimon Glass size_t size; 1212e192b24SSimon Glass 1222e192b24SSimon Glass for (size = 0; *type_str; type_str++) { 1232e192b24SSimon Glass switch (*type_str) { 1242e192b24SSimon Glass case 'b': 1252e192b24SSimon Glass size += 1; 1262e192b24SSimon Glass break; 1272e192b24SSimon Glass case 'w': 1282e192b24SSimon Glass size += 2; 1292e192b24SSimon Glass break; 1302e192b24SSimon Glass case 'd': 1312e192b24SSimon Glass size += 4; 1322e192b24SSimon Glass break; 1332e192b24SSimon Glass default: 1342e192b24SSimon Glass return 0; 1352e192b24SSimon Glass } 1362e192b24SSimon Glass } 1372e192b24SSimon Glass 1382e192b24SSimon Glass return size; 1392e192b24SSimon Glass } 1402e192b24SSimon Glass 1412e192b24SSimon Glass /** 1422e192b24SSimon Glass * Allocate a buffer large enough to hold values defined by a type 1432e192b24SSimon Glass * string. The caller has to free the buffer. 1442e192b24SSimon Glass * 1452e192b24SSimon Glass * @param type_str type string 1462e192b24SSimon Glass * @param count pointer for storing size of buffer 1472e192b24SSimon Glass * @return pointer to buffer or NULL on error 1482e192b24SSimon Glass */ 1492e192b24SSimon Glass static void *type_string_alloc(const char *type_str, uint32_t *count) 1502e192b24SSimon Glass { 1512e192b24SSimon Glass void *data; 1522e192b24SSimon Glass size_t size; 1532e192b24SSimon Glass 1542e192b24SSimon Glass size = type_string_get_space_size(type_str); 1552e192b24SSimon Glass if (!size) 1562e192b24SSimon Glass return NULL; 1572e192b24SSimon Glass data = malloc(size); 1582e192b24SSimon Glass if (data) 1592e192b24SSimon Glass *count = size; 1602e192b24SSimon Glass 1612e192b24SSimon Glass return data; 1622e192b24SSimon Glass } 1632e192b24SSimon Glass 1642e192b24SSimon Glass /** 1652e192b24SSimon Glass * Pack values defined by a type string into a buffer. The buffer must have 1662e192b24SSimon Glass * large enough space. 1672e192b24SSimon Glass * 1682e192b24SSimon Glass * @param type_str type string 1692e192b24SSimon Glass * @param values text strings of values to be packed 1702e192b24SSimon Glass * @param data output buffer of values 1712e192b24SSimon Glass * @return 0 on success, non-0 on error 1722e192b24SSimon Glass */ 1732e192b24SSimon Glass static int type_string_pack(const char *type_str, char * const values[], 1742e192b24SSimon Glass uint8_t *data) 1752e192b24SSimon Glass { 1762e192b24SSimon Glass size_t offset; 1772e192b24SSimon Glass uint32_t value; 1782e192b24SSimon Glass 1792e192b24SSimon Glass for (offset = 0; *type_str; type_str++, values++) { 1802e192b24SSimon Glass value = simple_strtoul(values[0], NULL, 0); 1812e192b24SSimon Glass switch (*type_str) { 1822e192b24SSimon Glass case 'b': 1832e192b24SSimon Glass data[offset] = value; 1842e192b24SSimon Glass offset += 1; 1852e192b24SSimon Glass break; 1862e192b24SSimon Glass case 'w': 1872e192b24SSimon Glass put_unaligned_be16(value, data + offset); 1882e192b24SSimon Glass offset += 2; 1892e192b24SSimon Glass break; 1902e192b24SSimon Glass case 'd': 1912e192b24SSimon Glass put_unaligned_be32(value, data + offset); 1922e192b24SSimon Glass offset += 4; 1932e192b24SSimon Glass break; 1942e192b24SSimon Glass default: 1952e192b24SSimon Glass return -1; 1962e192b24SSimon Glass } 1972e192b24SSimon Glass } 1982e192b24SSimon Glass 1992e192b24SSimon Glass return 0; 2002e192b24SSimon Glass } 2012e192b24SSimon Glass 2022e192b24SSimon Glass /** 2032e192b24SSimon Glass * Read values defined by a type string from a buffer, and write these values 2042e192b24SSimon Glass * to environment variables. 2052e192b24SSimon Glass * 2062e192b24SSimon Glass * @param type_str type string 2072e192b24SSimon Glass * @param data input buffer of values 2082e192b24SSimon Glass * @param vars names of environment variables 2092e192b24SSimon Glass * @return 0 on success, non-0 on error 2102e192b24SSimon Glass */ 2112e192b24SSimon Glass static int type_string_write_vars(const char *type_str, uint8_t *data, 2122e192b24SSimon Glass char * const vars[]) 2132e192b24SSimon Glass { 2142e192b24SSimon Glass size_t offset; 2152e192b24SSimon Glass uint32_t value; 2162e192b24SSimon Glass 2172e192b24SSimon Glass for (offset = 0; *type_str; type_str++, vars++) { 2182e192b24SSimon Glass switch (*type_str) { 2192e192b24SSimon Glass case 'b': 2202e192b24SSimon Glass value = data[offset]; 2212e192b24SSimon Glass offset += 1; 2222e192b24SSimon Glass break; 2232e192b24SSimon Glass case 'w': 2242e192b24SSimon Glass value = get_unaligned_be16(data + offset); 2252e192b24SSimon Glass offset += 2; 2262e192b24SSimon Glass break; 2272e192b24SSimon Glass case 'd': 2282e192b24SSimon Glass value = get_unaligned_be32(data + offset); 2292e192b24SSimon Glass offset += 4; 2302e192b24SSimon Glass break; 2312e192b24SSimon Glass default: 2322e192b24SSimon Glass return -1; 2332e192b24SSimon Glass } 2342e192b24SSimon Glass if (setenv_ulong(*vars, value)) 2352e192b24SSimon Glass return -1; 2362e192b24SSimon Glass } 2372e192b24SSimon Glass 2382e192b24SSimon Glass return 0; 2392e192b24SSimon Glass } 2402e192b24SSimon Glass 2412e192b24SSimon Glass static int do_tpm_startup(cmd_tbl_t *cmdtp, int flag, 2422e192b24SSimon Glass int argc, char * const argv[]) 2432e192b24SSimon Glass { 2442e192b24SSimon Glass enum tpm_startup_type mode; 2452e192b24SSimon Glass 2462e192b24SSimon Glass if (argc != 2) 2472e192b24SSimon Glass return CMD_RET_USAGE; 2482e192b24SSimon Glass if (!strcasecmp("TPM_ST_CLEAR", argv[1])) { 2492e192b24SSimon Glass mode = TPM_ST_CLEAR; 2502e192b24SSimon Glass } else if (!strcasecmp("TPM_ST_STATE", argv[1])) { 2512e192b24SSimon Glass mode = TPM_ST_STATE; 2522e192b24SSimon Glass } else if (!strcasecmp("TPM_ST_DEACTIVATED", argv[1])) { 2532e192b24SSimon Glass mode = TPM_ST_DEACTIVATED; 2542e192b24SSimon Glass } else { 2552e192b24SSimon Glass printf("Couldn't recognize mode string: %s\n", argv[1]); 2562e192b24SSimon Glass return CMD_RET_FAILURE; 2572e192b24SSimon Glass } 2582e192b24SSimon Glass 2592e192b24SSimon Glass return report_return_code(tpm_startup(mode)); 2602e192b24SSimon Glass } 2612e192b24SSimon Glass 2622e192b24SSimon Glass static int do_tpm_nv_define_space(cmd_tbl_t *cmdtp, int flag, 2632e192b24SSimon Glass int argc, char * const argv[]) 2642e192b24SSimon Glass { 2652e192b24SSimon Glass uint32_t index, perm, size; 2662e192b24SSimon Glass 2672e192b24SSimon Glass if (argc != 4) 2682e192b24SSimon Glass return CMD_RET_USAGE; 2692e192b24SSimon Glass index = simple_strtoul(argv[1], NULL, 0); 2702e192b24SSimon Glass perm = simple_strtoul(argv[2], NULL, 0); 2712e192b24SSimon Glass size = simple_strtoul(argv[3], NULL, 0); 2722e192b24SSimon Glass 2732e192b24SSimon Glass return report_return_code(tpm_nv_define_space(index, perm, size)); 2742e192b24SSimon Glass } 2752e192b24SSimon Glass 2762e192b24SSimon Glass static int do_tpm_nv_read_value(cmd_tbl_t *cmdtp, int flag, 2772e192b24SSimon Glass int argc, char * const argv[]) 2782e192b24SSimon Glass { 2792e192b24SSimon Glass uint32_t index, count, rc; 2802e192b24SSimon Glass void *data; 2812e192b24SSimon Glass 2822e192b24SSimon Glass if (argc != 4) 2832e192b24SSimon Glass return CMD_RET_USAGE; 2842e192b24SSimon Glass index = simple_strtoul(argv[1], NULL, 0); 2852e192b24SSimon Glass data = (void *)simple_strtoul(argv[2], NULL, 0); 2862e192b24SSimon Glass count = simple_strtoul(argv[3], NULL, 0); 2872e192b24SSimon Glass 2882e192b24SSimon Glass rc = tpm_nv_read_value(index, data, count); 2892e192b24SSimon Glass if (!rc) { 2902e192b24SSimon Glass puts("area content:\n"); 2912e192b24SSimon Glass print_byte_string(data, count); 2922e192b24SSimon Glass } 2932e192b24SSimon Glass 2942e192b24SSimon Glass return report_return_code(rc); 2952e192b24SSimon Glass } 2962e192b24SSimon Glass 2972e192b24SSimon Glass static int do_tpm_nv_write_value(cmd_tbl_t *cmdtp, int flag, 2982e192b24SSimon Glass int argc, char * const argv[]) 2992e192b24SSimon Glass { 3002e192b24SSimon Glass uint32_t index, rc; 3012e192b24SSimon Glass size_t count; 3022e192b24SSimon Glass void *data; 3032e192b24SSimon Glass 3042e192b24SSimon Glass if (argc != 3) 3052e192b24SSimon Glass return CMD_RET_USAGE; 3062e192b24SSimon Glass index = simple_strtoul(argv[1], NULL, 0); 3072e192b24SSimon Glass data = parse_byte_string(argv[2], NULL, &count); 3082e192b24SSimon Glass if (!data) { 3092e192b24SSimon Glass printf("Couldn't parse byte string %s\n", argv[2]); 3102e192b24SSimon Glass return CMD_RET_FAILURE; 3112e192b24SSimon Glass } 3122e192b24SSimon Glass 3132e192b24SSimon Glass rc = tpm_nv_write_value(index, data, count); 3142e192b24SSimon Glass free(data); 3152e192b24SSimon Glass 3162e192b24SSimon Glass return report_return_code(rc); 3172e192b24SSimon Glass } 3182e192b24SSimon Glass 3192e192b24SSimon Glass static int do_tpm_extend(cmd_tbl_t *cmdtp, int flag, 3202e192b24SSimon Glass int argc, char * const argv[]) 3212e192b24SSimon Glass { 3222e192b24SSimon Glass uint32_t index, rc; 3232e192b24SSimon Glass uint8_t in_digest[20], out_digest[20]; 3242e192b24SSimon Glass 3252e192b24SSimon Glass if (argc != 3) 3262e192b24SSimon Glass return CMD_RET_USAGE; 3272e192b24SSimon Glass index = simple_strtoul(argv[1], NULL, 0); 3282e192b24SSimon Glass if (!parse_byte_string(argv[2], in_digest, NULL)) { 3292e192b24SSimon Glass printf("Couldn't parse byte string %s\n", argv[2]); 3302e192b24SSimon Glass return CMD_RET_FAILURE; 3312e192b24SSimon Glass } 3322e192b24SSimon Glass 3332e192b24SSimon Glass rc = tpm_extend(index, in_digest, out_digest); 3342e192b24SSimon Glass if (!rc) { 3352e192b24SSimon Glass puts("PCR value after execution of the command:\n"); 3362e192b24SSimon Glass print_byte_string(out_digest, sizeof(out_digest)); 3372e192b24SSimon Glass } 3382e192b24SSimon Glass 3392e192b24SSimon Glass return report_return_code(rc); 3402e192b24SSimon Glass } 3412e192b24SSimon Glass 3422e192b24SSimon Glass static int do_tpm_pcr_read(cmd_tbl_t *cmdtp, int flag, 3432e192b24SSimon Glass int argc, char * const argv[]) 3442e192b24SSimon Glass { 3452e192b24SSimon Glass uint32_t index, count, rc; 3462e192b24SSimon Glass void *data; 3472e192b24SSimon Glass 3482e192b24SSimon Glass if (argc != 4) 3492e192b24SSimon Glass return CMD_RET_USAGE; 3502e192b24SSimon Glass index = simple_strtoul(argv[1], NULL, 0); 3512e192b24SSimon Glass data = (void *)simple_strtoul(argv[2], NULL, 0); 3522e192b24SSimon Glass count = simple_strtoul(argv[3], NULL, 0); 3532e192b24SSimon Glass 3542e192b24SSimon Glass rc = tpm_pcr_read(index, data, count); 3552e192b24SSimon Glass if (!rc) { 3562e192b24SSimon Glass puts("Named PCR content:\n"); 3572e192b24SSimon Glass print_byte_string(data, count); 3582e192b24SSimon Glass } 3592e192b24SSimon Glass 3602e192b24SSimon Glass return report_return_code(rc); 3612e192b24SSimon Glass } 3622e192b24SSimon Glass 3632e192b24SSimon Glass static int do_tpm_tsc_physical_presence(cmd_tbl_t *cmdtp, int flag, 3642e192b24SSimon Glass int argc, char * const argv[]) 3652e192b24SSimon Glass { 3662e192b24SSimon Glass uint16_t presence; 3672e192b24SSimon Glass 3682e192b24SSimon Glass if (argc != 2) 3692e192b24SSimon Glass return CMD_RET_USAGE; 3702e192b24SSimon Glass presence = (uint16_t)simple_strtoul(argv[1], NULL, 0); 3712e192b24SSimon Glass 3722e192b24SSimon Glass return report_return_code(tpm_tsc_physical_presence(presence)); 3732e192b24SSimon Glass } 3742e192b24SSimon Glass 3752e192b24SSimon Glass static int do_tpm_read_pubek(cmd_tbl_t *cmdtp, int flag, 3762e192b24SSimon Glass int argc, char * const argv[]) 3772e192b24SSimon Glass { 3782e192b24SSimon Glass uint32_t count, rc; 3792e192b24SSimon Glass void *data; 3802e192b24SSimon Glass 3812e192b24SSimon Glass if (argc != 3) 3822e192b24SSimon Glass return CMD_RET_USAGE; 3832e192b24SSimon Glass data = (void *)simple_strtoul(argv[1], NULL, 0); 3842e192b24SSimon Glass count = simple_strtoul(argv[2], NULL, 0); 3852e192b24SSimon Glass 3862e192b24SSimon Glass rc = tpm_read_pubek(data, count); 3872e192b24SSimon Glass if (!rc) { 3882e192b24SSimon Glass puts("pubek value:\n"); 3892e192b24SSimon Glass print_byte_string(data, count); 3902e192b24SSimon Glass } 3912e192b24SSimon Glass 3922e192b24SSimon Glass return report_return_code(rc); 3932e192b24SSimon Glass } 3942e192b24SSimon Glass 3952e192b24SSimon Glass static int do_tpm_physical_set_deactivated(cmd_tbl_t *cmdtp, int flag, 3962e192b24SSimon Glass int argc, char * const argv[]) 3972e192b24SSimon Glass { 3982e192b24SSimon Glass uint8_t state; 3992e192b24SSimon Glass 4002e192b24SSimon Glass if (argc != 2) 4012e192b24SSimon Glass return CMD_RET_USAGE; 4022e192b24SSimon Glass state = (uint8_t)simple_strtoul(argv[1], NULL, 0); 4032e192b24SSimon Glass 4042e192b24SSimon Glass return report_return_code(tpm_physical_set_deactivated(state)); 4052e192b24SSimon Glass } 4062e192b24SSimon Glass 4072e192b24SSimon Glass static int do_tpm_get_capability(cmd_tbl_t *cmdtp, int flag, 4082e192b24SSimon Glass int argc, char * const argv[]) 4092e192b24SSimon Glass { 4102e192b24SSimon Glass uint32_t cap_area, sub_cap, rc; 4112e192b24SSimon Glass void *cap; 4122e192b24SSimon Glass size_t count; 4132e192b24SSimon Glass 4142e192b24SSimon Glass if (argc != 5) 4152e192b24SSimon Glass return CMD_RET_USAGE; 4162e192b24SSimon Glass cap_area = simple_strtoul(argv[1], NULL, 0); 4172e192b24SSimon Glass sub_cap = simple_strtoul(argv[2], NULL, 0); 4182e192b24SSimon Glass cap = (void *)simple_strtoul(argv[3], NULL, 0); 4192e192b24SSimon Glass count = simple_strtoul(argv[4], NULL, 0); 4202e192b24SSimon Glass 4212e192b24SSimon Glass rc = tpm_get_capability(cap_area, sub_cap, cap, count); 4222e192b24SSimon Glass if (!rc) { 4232e192b24SSimon Glass puts("capability information:\n"); 4242e192b24SSimon Glass print_byte_string(cap, count); 4252e192b24SSimon Glass } 4262e192b24SSimon Glass 4272e192b24SSimon Glass return report_return_code(rc); 4282e192b24SSimon Glass } 4292e192b24SSimon Glass 4302e192b24SSimon Glass #define TPM_COMMAND_NO_ARG(cmd) \ 4312e192b24SSimon Glass static int do_##cmd(cmd_tbl_t *cmdtp, int flag, \ 4322e192b24SSimon Glass int argc, char * const argv[]) \ 4332e192b24SSimon Glass { \ 4342e192b24SSimon Glass if (argc != 1) \ 4352e192b24SSimon Glass return CMD_RET_USAGE; \ 4362e192b24SSimon Glass return report_return_code(cmd()); \ 4372e192b24SSimon Glass } 4382e192b24SSimon Glass 4392e192b24SSimon Glass TPM_COMMAND_NO_ARG(tpm_init) 4402e192b24SSimon Glass TPM_COMMAND_NO_ARG(tpm_self_test_full) 4412e192b24SSimon Glass TPM_COMMAND_NO_ARG(tpm_continue_self_test) 4422e192b24SSimon Glass TPM_COMMAND_NO_ARG(tpm_force_clear) 4432e192b24SSimon Glass TPM_COMMAND_NO_ARG(tpm_physical_enable) 4442e192b24SSimon Glass TPM_COMMAND_NO_ARG(tpm_physical_disable) 4452e192b24SSimon Glass 4462e192b24SSimon Glass static int get_tpm(struct udevice **devp) 4472e192b24SSimon Glass { 4482e192b24SSimon Glass int rc; 4492e192b24SSimon Glass 450*3f603cbbSSimon Glass rc = uclass_first_device_err(UCLASS_TPM, devp); 451*3f603cbbSSimon Glass if (rc) { 4522e192b24SSimon Glass printf("Could not find TPM (ret=%d)\n", rc); 4532e192b24SSimon Glass return CMD_RET_FAILURE; 4542e192b24SSimon Glass } 4552e192b24SSimon Glass 4562e192b24SSimon Glass return 0; 4572e192b24SSimon Glass } 4582e192b24SSimon Glass 4592e192b24SSimon Glass static int do_tpm_info(cmd_tbl_t *cmdtp, int flag, int argc, 4602e192b24SSimon Glass char *const argv[]) 4612e192b24SSimon Glass { 4622e192b24SSimon Glass struct udevice *dev; 4632e192b24SSimon Glass char buf[80]; 4642e192b24SSimon Glass int rc; 4652e192b24SSimon Glass 4662e192b24SSimon Glass rc = get_tpm(&dev); 4672e192b24SSimon Glass if (rc) 4682e192b24SSimon Glass return rc; 4692e192b24SSimon Glass rc = tpm_get_desc(dev, buf, sizeof(buf)); 4702e192b24SSimon Glass if (rc < 0) { 4712e192b24SSimon Glass printf("Couldn't get TPM info (%d)\n", rc); 4722e192b24SSimon Glass return CMD_RET_FAILURE; 4732e192b24SSimon Glass } 4742e192b24SSimon Glass printf("%s\n", buf); 4752e192b24SSimon Glass 4762e192b24SSimon Glass return 0; 4772e192b24SSimon Glass } 4782e192b24SSimon Glass 4792e192b24SSimon Glass static int do_tpm_raw_transfer(cmd_tbl_t *cmdtp, int flag, 4802e192b24SSimon Glass int argc, char * const argv[]) 4812e192b24SSimon Glass { 4822e192b24SSimon Glass struct udevice *dev; 4832e192b24SSimon Glass void *command; 4842e192b24SSimon Glass uint8_t response[1024]; 4852e192b24SSimon Glass size_t count, response_length = sizeof(response); 4862e192b24SSimon Glass uint32_t rc; 4872e192b24SSimon Glass 4882e192b24SSimon Glass command = parse_byte_string(argv[1], NULL, &count); 4892e192b24SSimon Glass if (!command) { 4902e192b24SSimon Glass printf("Couldn't parse byte string %s\n", argv[1]); 4912e192b24SSimon Glass return CMD_RET_FAILURE; 4922e192b24SSimon Glass } 4932e192b24SSimon Glass 4942e192b24SSimon Glass rc = get_tpm(&dev); 4952e192b24SSimon Glass if (rc) 4962e192b24SSimon Glass return rc; 4972e192b24SSimon Glass 4982e192b24SSimon Glass rc = tpm_xfer(dev, command, count, response, &response_length); 4992e192b24SSimon Glass free(command); 5002e192b24SSimon Glass if (!rc) { 5012e192b24SSimon Glass puts("tpm response:\n"); 5022e192b24SSimon Glass print_byte_string(response, response_length); 5032e192b24SSimon Glass } 5042e192b24SSimon Glass 5052e192b24SSimon Glass return report_return_code(rc); 5062e192b24SSimon Glass } 5072e192b24SSimon Glass 5082e192b24SSimon Glass static int do_tpm_nv_define(cmd_tbl_t *cmdtp, int flag, 5092e192b24SSimon Glass int argc, char * const argv[]) 5102e192b24SSimon Glass { 5112e192b24SSimon Glass uint32_t index, perm, size; 5122e192b24SSimon Glass 5132e192b24SSimon Glass if (argc != 4) 5142e192b24SSimon Glass return CMD_RET_USAGE; 5152e192b24SSimon Glass size = type_string_get_space_size(argv[1]); 5162e192b24SSimon Glass if (!size) { 5172e192b24SSimon Glass printf("Couldn't parse arguments\n"); 5182e192b24SSimon Glass return CMD_RET_USAGE; 5192e192b24SSimon Glass } 5202e192b24SSimon Glass index = simple_strtoul(argv[2], NULL, 0); 5212e192b24SSimon Glass perm = simple_strtoul(argv[3], NULL, 0); 5222e192b24SSimon Glass 5232e192b24SSimon Glass return report_return_code(tpm_nv_define_space(index, perm, size)); 5242e192b24SSimon Glass } 5252e192b24SSimon Glass 5262e192b24SSimon Glass static int do_tpm_nv_read(cmd_tbl_t *cmdtp, int flag, 5272e192b24SSimon Glass int argc, char * const argv[]) 5282e192b24SSimon Glass { 5292e192b24SSimon Glass uint32_t index, count, err; 5302e192b24SSimon Glass void *data; 5312e192b24SSimon Glass 5322e192b24SSimon Glass if (argc < 3) 5332e192b24SSimon Glass return CMD_RET_USAGE; 5342e192b24SSimon Glass if (argc != 3 + type_string_get_num_values(argv[1])) 5352e192b24SSimon Glass return CMD_RET_USAGE; 5362e192b24SSimon Glass index = simple_strtoul(argv[2], NULL, 0); 5372e192b24SSimon Glass data = type_string_alloc(argv[1], &count); 5382e192b24SSimon Glass if (!data) { 5392e192b24SSimon Glass printf("Couldn't parse arguments\n"); 5402e192b24SSimon Glass return CMD_RET_USAGE; 5412e192b24SSimon Glass } 5422e192b24SSimon Glass 5432e192b24SSimon Glass err = tpm_nv_read_value(index, data, count); 5442e192b24SSimon Glass if (!err) { 5452e192b24SSimon Glass if (type_string_write_vars(argv[1], data, argv + 3)) { 5462e192b24SSimon Glass printf("Couldn't write to variables\n"); 5472e192b24SSimon Glass err = ~0; 5482e192b24SSimon Glass } 5492e192b24SSimon Glass } 5502e192b24SSimon Glass free(data); 5512e192b24SSimon Glass 5522e192b24SSimon Glass return report_return_code(err); 5532e192b24SSimon Glass } 5542e192b24SSimon Glass 5552e192b24SSimon Glass static int do_tpm_nv_write(cmd_tbl_t *cmdtp, int flag, 5562e192b24SSimon Glass int argc, char * const argv[]) 5572e192b24SSimon Glass { 5582e192b24SSimon Glass uint32_t index, count, err; 5592e192b24SSimon Glass void *data; 5602e192b24SSimon Glass 5612e192b24SSimon Glass if (argc < 3) 5622e192b24SSimon Glass return CMD_RET_USAGE; 5632e192b24SSimon Glass if (argc != 3 + type_string_get_num_values(argv[1])) 5642e192b24SSimon Glass return CMD_RET_USAGE; 5652e192b24SSimon Glass index = simple_strtoul(argv[2], NULL, 0); 5662e192b24SSimon Glass data = type_string_alloc(argv[1], &count); 5672e192b24SSimon Glass if (!data) { 5682e192b24SSimon Glass printf("Couldn't parse arguments\n"); 5692e192b24SSimon Glass return CMD_RET_USAGE; 5702e192b24SSimon Glass } 5712e192b24SSimon Glass if (type_string_pack(argv[1], argv + 3, data)) { 5722e192b24SSimon Glass printf("Couldn't parse arguments\n"); 5732e192b24SSimon Glass free(data); 5742e192b24SSimon Glass return CMD_RET_USAGE; 5752e192b24SSimon Glass } 5762e192b24SSimon Glass 5772e192b24SSimon Glass err = tpm_nv_write_value(index, data, count); 5782e192b24SSimon Glass free(data); 5792e192b24SSimon Glass 5802e192b24SSimon Glass return report_return_code(err); 5812e192b24SSimon Glass } 5822e192b24SSimon Glass 5832e192b24SSimon Glass #ifdef CONFIG_TPM_AUTH_SESSIONS 5842e192b24SSimon Glass 5852e192b24SSimon Glass static int do_tpm_oiap(cmd_tbl_t *cmdtp, int flag, 5862e192b24SSimon Glass int argc, char * const argv[]) 5872e192b24SSimon Glass { 5882e192b24SSimon Glass uint32_t auth_handle, err; 5892e192b24SSimon Glass 5902e192b24SSimon Glass err = tpm_oiap(&auth_handle); 5912e192b24SSimon Glass 5922e192b24SSimon Glass return report_return_code(err); 5932e192b24SSimon Glass } 5942e192b24SSimon Glass 5952e192b24SSimon Glass static int do_tpm_load_key2_oiap(cmd_tbl_t *cmdtp, int flag, 5962e192b24SSimon Glass int argc, char * const argv[]) 5972e192b24SSimon Glass { 5982e192b24SSimon Glass uint32_t parent_handle, key_len, key_handle, err; 5992e192b24SSimon Glass uint8_t usage_auth[DIGEST_LENGTH]; 6002e192b24SSimon Glass void *key; 6012e192b24SSimon Glass 6022e192b24SSimon Glass if (argc < 5) 6032e192b24SSimon Glass return CMD_RET_USAGE; 6042e192b24SSimon Glass 6052e192b24SSimon Glass parent_handle = simple_strtoul(argv[1], NULL, 0); 6062e192b24SSimon Glass key = (void *)simple_strtoul(argv[2], NULL, 0); 6072e192b24SSimon Glass key_len = simple_strtoul(argv[3], NULL, 0); 6082e192b24SSimon Glass if (strlen(argv[4]) != 2 * DIGEST_LENGTH) 6092e192b24SSimon Glass return CMD_RET_FAILURE; 6102e192b24SSimon Glass parse_byte_string(argv[4], usage_auth, NULL); 6112e192b24SSimon Glass 6122e192b24SSimon Glass err = tpm_load_key2_oiap(parent_handle, key, key_len, usage_auth, 6132e192b24SSimon Glass &key_handle); 6142e192b24SSimon Glass if (!err) 6152e192b24SSimon Glass printf("Key handle is 0x%x\n", key_handle); 6162e192b24SSimon Glass 6172e192b24SSimon Glass return report_return_code(err); 6182e192b24SSimon Glass } 6192e192b24SSimon Glass 6202e192b24SSimon Glass static int do_tpm_get_pub_key_oiap(cmd_tbl_t *cmdtp, int flag, 6212e192b24SSimon Glass int argc, char * const argv[]) 6222e192b24SSimon Glass { 6232e192b24SSimon Glass uint32_t key_handle, err; 6242e192b24SSimon Glass uint8_t usage_auth[DIGEST_LENGTH]; 6252e192b24SSimon Glass uint8_t pub_key_buffer[TPM_PUBKEY_MAX_LENGTH]; 6262e192b24SSimon Glass size_t pub_key_len = sizeof(pub_key_buffer); 6272e192b24SSimon Glass 6282e192b24SSimon Glass if (argc < 3) 6292e192b24SSimon Glass return CMD_RET_USAGE; 6302e192b24SSimon Glass 6312e192b24SSimon Glass key_handle = simple_strtoul(argv[1], NULL, 0); 6322e192b24SSimon Glass if (strlen(argv[2]) != 2 * DIGEST_LENGTH) 6332e192b24SSimon Glass return CMD_RET_FAILURE; 6342e192b24SSimon Glass parse_byte_string(argv[2], usage_auth, NULL); 6352e192b24SSimon Glass 6362e192b24SSimon Glass err = tpm_get_pub_key_oiap(key_handle, usage_auth, 6372e192b24SSimon Glass pub_key_buffer, &pub_key_len); 6382e192b24SSimon Glass if (!err) { 6392e192b24SSimon Glass printf("dump of received pub key structure:\n"); 6402e192b24SSimon Glass print_byte_string(pub_key_buffer, pub_key_len); 6412e192b24SSimon Glass } 6422e192b24SSimon Glass return report_return_code(err); 6432e192b24SSimon Glass } 6442e192b24SSimon Glass 6452e192b24SSimon Glass TPM_COMMAND_NO_ARG(tpm_end_oiap) 6462e192b24SSimon Glass 6472e192b24SSimon Glass #endif /* CONFIG_TPM_AUTH_SESSIONS */ 6482e192b24SSimon Glass 6492e192b24SSimon Glass #define MAKE_TPM_CMD_ENTRY(cmd) \ 6502e192b24SSimon Glass U_BOOT_CMD_MKENT(cmd, 0, 1, do_tpm_ ## cmd, "", "") 6512e192b24SSimon Glass 6522e192b24SSimon Glass static cmd_tbl_t tpm_commands[] = { 6532e192b24SSimon Glass U_BOOT_CMD_MKENT(info, 0, 1, do_tpm_info, "", ""), 6542e192b24SSimon Glass U_BOOT_CMD_MKENT(init, 0, 1, 6552e192b24SSimon Glass do_tpm_init, "", ""), 6562e192b24SSimon Glass U_BOOT_CMD_MKENT(startup, 0, 1, 6572e192b24SSimon Glass do_tpm_startup, "", ""), 6582e192b24SSimon Glass U_BOOT_CMD_MKENT(self_test_full, 0, 1, 6592e192b24SSimon Glass do_tpm_self_test_full, "", ""), 6602e192b24SSimon Glass U_BOOT_CMD_MKENT(continue_self_test, 0, 1, 6612e192b24SSimon Glass do_tpm_continue_self_test, "", ""), 6622e192b24SSimon Glass U_BOOT_CMD_MKENT(force_clear, 0, 1, 6632e192b24SSimon Glass do_tpm_force_clear, "", ""), 6642e192b24SSimon Glass U_BOOT_CMD_MKENT(physical_enable, 0, 1, 6652e192b24SSimon Glass do_tpm_physical_enable, "", ""), 6662e192b24SSimon Glass U_BOOT_CMD_MKENT(physical_disable, 0, 1, 6672e192b24SSimon Glass do_tpm_physical_disable, "", ""), 6682e192b24SSimon Glass U_BOOT_CMD_MKENT(nv_define_space, 0, 1, 6692e192b24SSimon Glass do_tpm_nv_define_space, "", ""), 6702e192b24SSimon Glass U_BOOT_CMD_MKENT(nv_read_value, 0, 1, 6712e192b24SSimon Glass do_tpm_nv_read_value, "", ""), 6722e192b24SSimon Glass U_BOOT_CMD_MKENT(nv_write_value, 0, 1, 6732e192b24SSimon Glass do_tpm_nv_write_value, "", ""), 6742e192b24SSimon Glass U_BOOT_CMD_MKENT(extend, 0, 1, 6752e192b24SSimon Glass do_tpm_extend, "", ""), 6762e192b24SSimon Glass U_BOOT_CMD_MKENT(pcr_read, 0, 1, 6772e192b24SSimon Glass do_tpm_pcr_read, "", ""), 6782e192b24SSimon Glass U_BOOT_CMD_MKENT(tsc_physical_presence, 0, 1, 6792e192b24SSimon Glass do_tpm_tsc_physical_presence, "", ""), 6802e192b24SSimon Glass U_BOOT_CMD_MKENT(read_pubek, 0, 1, 6812e192b24SSimon Glass do_tpm_read_pubek, "", ""), 6822e192b24SSimon Glass U_BOOT_CMD_MKENT(physical_set_deactivated, 0, 1, 6832e192b24SSimon Glass do_tpm_physical_set_deactivated, "", ""), 6842e192b24SSimon Glass U_BOOT_CMD_MKENT(get_capability, 0, 1, 6852e192b24SSimon Glass do_tpm_get_capability, "", ""), 6862e192b24SSimon Glass U_BOOT_CMD_MKENT(raw_transfer, 0, 1, 6872e192b24SSimon Glass do_tpm_raw_transfer, "", ""), 6882e192b24SSimon Glass U_BOOT_CMD_MKENT(nv_define, 0, 1, 6892e192b24SSimon Glass do_tpm_nv_define, "", ""), 6902e192b24SSimon Glass U_BOOT_CMD_MKENT(nv_read, 0, 1, 6912e192b24SSimon Glass do_tpm_nv_read, "", ""), 6922e192b24SSimon Glass U_BOOT_CMD_MKENT(nv_write, 0, 1, 6932e192b24SSimon Glass do_tpm_nv_write, "", ""), 6942e192b24SSimon Glass #ifdef CONFIG_TPM_AUTH_SESSIONS 6952e192b24SSimon Glass U_BOOT_CMD_MKENT(oiap, 0, 1, 6962e192b24SSimon Glass do_tpm_oiap, "", ""), 6972e192b24SSimon Glass U_BOOT_CMD_MKENT(end_oiap, 0, 1, 6982e192b24SSimon Glass do_tpm_end_oiap, "", ""), 6992e192b24SSimon Glass U_BOOT_CMD_MKENT(load_key2_oiap, 0, 1, 7002e192b24SSimon Glass do_tpm_load_key2_oiap, "", ""), 7012e192b24SSimon Glass U_BOOT_CMD_MKENT(get_pub_key_oiap, 0, 1, 7022e192b24SSimon Glass do_tpm_get_pub_key_oiap, "", ""), 7032e192b24SSimon Glass #endif /* CONFIG_TPM_AUTH_SESSIONS */ 7042e192b24SSimon Glass }; 7052e192b24SSimon Glass 7062e192b24SSimon Glass static int do_tpm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 7072e192b24SSimon Glass { 7082e192b24SSimon Glass cmd_tbl_t *tpm_cmd; 7092e192b24SSimon Glass 7102e192b24SSimon Glass if (argc < 2) 7112e192b24SSimon Glass return CMD_RET_USAGE; 7122e192b24SSimon Glass tpm_cmd = find_cmd_tbl(argv[1], tpm_commands, ARRAY_SIZE(tpm_commands)); 7132e192b24SSimon Glass if (!tpm_cmd) 7142e192b24SSimon Glass return CMD_RET_USAGE; 7152e192b24SSimon Glass 7162e192b24SSimon Glass return tpm_cmd->cmd(cmdtp, flag, argc - 1, argv + 1); 7172e192b24SSimon Glass } 7182e192b24SSimon Glass 7192e192b24SSimon Glass U_BOOT_CMD(tpm, CONFIG_SYS_MAXARGS, 1, do_tpm, 7202e192b24SSimon Glass "Issue a TPM command", 7212e192b24SSimon Glass "cmd args...\n" 7222e192b24SSimon Glass " - Issue TPM command <cmd> with arguments <args...>.\n" 7232e192b24SSimon Glass "Admin Startup and State Commands:\n" 7242e192b24SSimon Glass " info - Show information about the TPM\n" 7252e192b24SSimon Glass " init\n" 7262e192b24SSimon Glass " - Put TPM into a state where it waits for 'startup' command.\n" 7272e192b24SSimon Glass " startup mode\n" 7282e192b24SSimon Glass " - Issue TPM_Starup command. <mode> is one of TPM_ST_CLEAR,\n" 7292e192b24SSimon Glass " TPM_ST_STATE, and TPM_ST_DEACTIVATED.\n" 7302e192b24SSimon Glass "Admin Testing Commands:\n" 7312e192b24SSimon Glass " self_test_full\n" 7322e192b24SSimon Glass " - Test all of the TPM capabilities.\n" 7332e192b24SSimon Glass " continue_self_test\n" 7342e192b24SSimon Glass " - Inform TPM that it should complete the self-test.\n" 7352e192b24SSimon Glass "Admin Opt-in Commands:\n" 7362e192b24SSimon Glass " physical_enable\n" 7372e192b24SSimon Glass " - Set the PERMANENT disable flag to FALSE using physical presence as\n" 7382e192b24SSimon Glass " authorization.\n" 7392e192b24SSimon Glass " physical_disable\n" 7402e192b24SSimon Glass " - Set the PERMANENT disable flag to TRUE using physical presence as\n" 7412e192b24SSimon Glass " authorization.\n" 7422e192b24SSimon Glass " physical_set_deactivated 0|1\n" 7432e192b24SSimon Glass " - Set deactivated flag.\n" 7442e192b24SSimon Glass "Admin Ownership Commands:\n" 7452e192b24SSimon Glass " force_clear\n" 7462e192b24SSimon Glass " - Issue TPM_ForceClear command.\n" 7472e192b24SSimon Glass " tsc_physical_presence flags\n" 7482e192b24SSimon Glass " - Set TPM device's Physical Presence flags to <flags>.\n" 7492e192b24SSimon Glass "The Capability Commands:\n" 7502e192b24SSimon Glass " get_capability cap_area sub_cap addr count\n" 7512e192b24SSimon Glass " - Read <count> bytes of TPM capability indexed by <cap_area> and\n" 7522e192b24SSimon Glass " <sub_cap> to memory address <addr>.\n" 7532e192b24SSimon Glass #ifdef CONFIG_TPM_AUTH_SESSIONS 7542e192b24SSimon Glass "Storage functions\n" 7552e192b24SSimon Glass " loadkey2_oiap parent_handle key_addr key_len usage_auth\n" 7562e192b24SSimon Glass " - loads a key data from memory address <key_addr>, <key_len> bytes\n" 7572e192b24SSimon Glass " into TPM using the parent key <parent_handle> with authorization\n" 7582e192b24SSimon Glass " <usage_auth> (20 bytes hex string).\n" 7592e192b24SSimon Glass " get_pub_key_oiap key_handle usage_auth\n" 7602e192b24SSimon Glass " - get the public key portion of a loaded key <key_handle> using\n" 7612e192b24SSimon Glass " authorization <usage auth> (20 bytes hex string)\n" 7622e192b24SSimon Glass #endif /* CONFIG_TPM_AUTH_SESSIONS */ 7632e192b24SSimon Glass "Endorsement Key Handling Commands:\n" 7642e192b24SSimon Glass " read_pubek addr count\n" 7652e192b24SSimon Glass " - Read <count> bytes of the public endorsement key to memory\n" 7662e192b24SSimon Glass " address <addr>\n" 7672e192b24SSimon Glass "Integrity Collection and Reporting Commands:\n" 7682e192b24SSimon Glass " extend index digest_hex_string\n" 7692e192b24SSimon Glass " - Add a new measurement to a PCR. Update PCR <index> with the 20-bytes\n" 7702e192b24SSimon Glass " <digest_hex_string>\n" 7712e192b24SSimon Glass " pcr_read index addr count\n" 7722e192b24SSimon Glass " - Read <count> bytes from PCR <index> to memory address <addr>.\n" 7732e192b24SSimon Glass #ifdef CONFIG_TPM_AUTH_SESSIONS 7742e192b24SSimon Glass "Authorization Sessions\n" 7752e192b24SSimon Glass " oiap\n" 7762e192b24SSimon Glass " - setup an OIAP session\n" 7772e192b24SSimon Glass " end_oiap\n" 7782e192b24SSimon Glass " - terminates an active OIAP session\n" 7792e192b24SSimon Glass #endif /* CONFIG_TPM_AUTH_SESSIONS */ 7802e192b24SSimon Glass "Non-volatile Storage Commands:\n" 7812e192b24SSimon Glass " nv_define_space index permission size\n" 7822e192b24SSimon Glass " - Establish a space at index <index> with <permission> of <size> bytes.\n" 7832e192b24SSimon Glass " nv_read_value index addr count\n" 7842e192b24SSimon Glass " - Read <count> bytes from space <index> to memory address <addr>.\n" 7852e192b24SSimon Glass " nv_write_value index addr count\n" 7862e192b24SSimon Glass " - Write <count> bytes from memory address <addr> to space <index>.\n" 7872e192b24SSimon Glass "Miscellaneous helper functions:\n" 7882e192b24SSimon Glass " raw_transfer byte_string\n" 7892e192b24SSimon Glass " - Send a byte string <byte_string> to TPM and print the response.\n" 7902e192b24SSimon Glass " Non-volatile storage helper functions:\n" 7912e192b24SSimon Glass " These helper functions treat a non-volatile space as a non-padded\n" 7922e192b24SSimon Glass " sequence of integer values. These integer values are defined by a type\n" 7932e192b24SSimon Glass " string, which is a text string of 'bwd' characters: 'b' means a 8-bit\n" 7942e192b24SSimon Glass " value, 'w' 16-bit value, 'd' 32-bit value. All helper functions take\n" 7952e192b24SSimon Glass " a type string as their first argument.\n" 7962e192b24SSimon Glass " nv_define type_string index perm\n" 7972e192b24SSimon Glass " - Define a space <index> with permission <perm>.\n" 7982e192b24SSimon Glass " nv_read types_string index vars...\n" 7992e192b24SSimon Glass " - Read from space <index> to environment variables <vars...>.\n" 8002e192b24SSimon Glass " nv_write types_string index values...\n" 8012e192b24SSimon Glass " - Write to space <index> from values <values...>.\n" 8022e192b24SSimon Glass ); 803