| /* SPDX-License-Identifier: GPL-2.0 */ |
| /* |
| * Kernel interface for the RISCV arch_random_* functions |
| * |
| * Copyright (c) 2023 Rivos Inc. |
| * |
| */ |
| |
| #ifndef ASM_RISCV_ARCHRANDOM_H |
| #define ASM_RISCV_ARCHRANDOM_H |
| |
| #include <asm/csr.h> |
| #include <asm/processor.h> |
| |
| #define SEED_RETRY_LOOPS 100 |
| |
| static inline bool __must_check csr_seed_long(unsigned long *v) |
| { |
| unsigned int retry = SEED_RETRY_LOOPS, valid_seeds = 0; |
| const int needed_seeds = sizeof(long) / sizeof(u16); |
| u16 *entropy = (u16 *)v; |
| |
| do { |
| /* |
| * The SEED CSR must be accessed with a read-write instruction. |
| */ |
| unsigned long csr_seed = csr_swap(CSR_SEED, 0); |
| unsigned long opst = csr_seed & SEED_OPST_MASK; |
| |
| switch (opst) { |
| case SEED_OPST_ES16: |
| entropy[valid_seeds++] = csr_seed & SEED_ENTROPY_MASK; |
| if (valid_seeds == needed_seeds) |
| return true; |
| break; |
| |
| case SEED_OPST_DEAD: |
| pr_err_once("archrandom: Unrecoverable error\n"); |
| return false; |
| |
| case SEED_OPST_BIST: |
| case SEED_OPST_WAIT: |
| default: |
| cpu_relax(); |
| continue; |
| } |
| } while (--retry); |
| |
| return false; |
| } |
| |
| static inline size_t __must_check arch_get_random_longs(unsigned long *v, size_t max_longs) |
| { |
| return 0; |
| } |
| |
| static inline size_t __must_check arch_get_random_seed_longs(unsigned long *v, size_t max_longs) |
| { |
| if (!max_longs) |
| return 0; |
| |
| /* |
| * If Zkr is supported and csr_seed_long succeeds, we return one long |
| * worth of entropy. |
| */ |
| if (riscv_has_extension_likely(RISCV_ISA_EXT_ZKR) && csr_seed_long(v)) |
| return 1; |
| |
| return 0; |
| } |
| |
| #endif /* ASM_RISCV_ARCHRANDOM_H */ |