From: Alison Schofield <alison.schofield@xxxxxxxxx> Finally, the keys are programmed into the hardware via each lead CPU. Every package has to be programmed successfully. There is no partial success allowed here. Here a retry scheme is included for two errors that may succeed on retry: MKTME_DEVICE_BUSY and MKTME_ENTROPY_ERROR. However, it's not clear if even those errors should be retried at this level. Perhaps they too, should be returned to user space for handling. Signed-off-by: Alison Schofield <alison.schofield@xxxxxxxxx> Signed-off-by: Kirill A. Shutemov <kirill.shutemov@xxxxxxxxxxxxxxx> --- security/keys/mktme_keys.c | 92 +++++++++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) diff --git a/security/keys/mktme_keys.c b/security/keys/mktme_keys.c index b5b44decfd3e..f70533b1a7fd 100644 --- a/security/keys/mktme_keys.c +++ b/security/keys/mktme_keys.c @@ -102,6 +102,96 @@ struct mktme_payload { u8 tweak_key[MKTME_AES_XTS_SIZE]; }; +struct mktme_hw_program_info { + struct mktme_key_program *key_program; + int *status; +}; + +struct mktme_err_table { + const char *msg; + bool retry; +}; + +static const struct mktme_err_table mktme_error[] = { +/* MKTME_PROG_SUCCESS */ {"KeyID was successfully programmed", false}, +/* MKTME_INVALID_PROG_CMD */ {"Invalid KeyID programming command", false}, +/* MKTME_ENTROPY_ERROR */ {"Insufficient entropy", true}, +/* MKTME_INVALID_KEYID */ {"KeyID not valid", false}, +/* MKTME_INVALID_ENC_ALG */ {"Invalid encryption algorithm chosen", false}, +/* MKTME_DEVICE_BUSY */ {"Failure to access key table", true}, +}; + +static int mktme_parse_program_status(int status[]) +{ + int cpu, sum = 0; + + /* Success: all CPU(s) programmed all key table(s) */ + for_each_cpu(cpu, mktme_leadcpus) + sum += status[cpu]; + if (!sum) + return MKTME_PROG_SUCCESS; + + /* Invalid Parameters: log the error and return the error. */ + for_each_cpu(cpu, mktme_leadcpus) { + switch (status[cpu]) { + case MKTME_INVALID_KEYID: + case MKTME_INVALID_PROG_CMD: + case MKTME_INVALID_ENC_ALG: + pr_err("mktme: %s\n", mktme_error[status[cpu]].msg); + return status[cpu]; + + default: + break; + } + } + /* + * Device Busy or Insufficient Entropy: do not log the + * error. These will be retried and if retries (time or + * count runs out) caller will log the error. + */ + for_each_cpu(cpu, mktme_leadcpus) { + if (status[cpu] == MKTME_DEVICE_BUSY) + return status[cpu]; + } + return MKTME_ENTROPY_ERROR; +} + +/* Program a single key using one CPU. */ +static void mktme_do_program(void *hw_program_info) +{ + struct mktme_hw_program_info *info = hw_program_info; + int cpu; + + cpu = smp_processor_id(); + info->status[cpu] = mktme_key_program(info->key_program); +} + +static int mktme_program_all_keytables(struct mktme_key_program *key_program) +{ + struct mktme_hw_program_info info; + int err, retries = 10; /* Maybe users should handle retries */ + + info.key_program = key_program; + info.status = kcalloc(num_possible_cpus(), sizeof(info.status[0]), + GFP_KERNEL); + + while (retries--) { + get_online_cpus(); + on_each_cpu_mask(mktme_leadcpus, mktme_do_program, + &info, 1); + put_online_cpus(); + + err = mktme_parse_program_status(info.status); + if (!err) /* Success */ + return err; + else if (!mktme_error[err].retry) /* Error no retry */ + return -ENOKEY; + } + /* Ran out of retries */ + pr_err("mktme: %s\n", mktme_error[err].msg); + return err; +} + /* Copy the payload to the HW programming structure and program this KeyID */ static int mktme_program_keyid(int keyid, struct mktme_payload *payload) { @@ -127,7 +217,7 @@ static int mktme_program_keyid(int keyid, struct mktme_payload *payload) kprog->key_field_2[i] ^= kern_entropy[i]; } } - ret = MKTME_PROG_SUCCESS; /* Future programming call */ + ret = mktme_program_all_keytables(kprog); kmem_cache_free(mktme_prog_cache, kprog); return ret; } -- 2.20.1