| /* Copyright lowRISC contributors. */ |
| /* Licensed under the Apache License, Version 2.0, see LICENSE for details. */ |
| /* SPDX-License-Identifier: Apache-2.0 */ |
| |
| /* Test access to randomness from OTBN. */ |
| |
| .equ CSR_FG0, 0x7c0 |
| .equ CSR_FG1, 0x7c1 |
| .equ CSR_RND_PREFETCH, 0x7d8 |
| .equ CSR_RND, 0xfc0 |
| .equ CSR_URND, 0xfc1 |
| |
| .section .text.start |
| /* Test entry point, no arguments need to be passed in nor results returned. */ |
| .globl main |
| main: |
| /* Init all-zero reg. */ |
| bn.xor w31, w31, w31 |
| |
| jal x1, test_rnd |
| jal x1, test_urnd |
| |
| jal x0, exit_success |
| |
| |
| .text |
| /** |
| * Tests access to cryptographic-strength randomness. |
| * |
| * Test reading from the RND CSR and WSR, as well as prefetching randomness |
| * through RND_PREFETCH. |
| * |
| * This test increases confidence in the functional correctness of the RND |
| * functionality, but isn't free of false positives or negatives. |
| * |
| * - The test considers it a failure if two consecutive values read from RND |
| * are equal. In rare cases, this could happen and the test should be |
| * re-executed. |
| * - The randomness prefetch is an optimization to hide unknown and highly |
| * variable latency from the EDN. This, plus the fact that OTBN software |
| * cannot measure its own execution time, makes it impossible to automate |
| * a test for RND_PREFETCH. The instruction is kept nonetheless to help |
| * debugging with waveforms. |
| */ |
| test_rnd: |
| /* Initial read. */ |
| csrrs x2, CSR_RND, x0 |
| |
| /* Read again, should block for a while. */ |
| csrrs x3, CSR_RND, x0 |
| |
| /* If two consecutive reads return the same random number that's most likely |
| a bug. (But it doesn't have to be a bug! Re-run the test if it fails.) */ |
| addi x31, x0, 1 |
| beq x2, x3, exit_fail |
| |
| /* Request a RND value from the EDN by writing to RND_PREFETCH. */ |
| csrrw x0, CSR_RND_PREFETCH, x0 |
| |
| /* Give the prefetch a while to complete. */ |
| /* An EDN request for a random number can take up to around 5 ms. Do not |
| expect the following RND read to return immediately. */ |
| loopi 1023, 1 |
| nop |
| |
| /* Read RND again, should block less. */ |
| csrrs x2, CSR_RND, x0 |
| |
| /* Compare to previous value again to detect RND getting stuck. */ |
| addi x31, x0, 2 |
| beq x3, x2, exit_fail |
| |
| |
| /* Read the RND WSR. */ |
| bn.wsrr w0, 0x1 /* RND */ |
| |
| /* Write w0 to DMEM. */ |
| la x11, rnd_out |
| bn.sid x0, 0(x11) |
| |
| /* Read the RND WSR again. */ |
| bn.wsrr w1, 0x1 /* RND */ |
| |
| jal x1, error_checking |
| ret |
| |
| /** |
| * Tests access to "unlimited" randomness (URND). |
| * |
| * The test considers it a failure if two consecutive values read from RND |
| * are equal. In rare cases, this could happen and the test should be |
| * re-executed. |
| */ |
| test_urnd: |
| /* Read the URND CSR. */ |
| csrrs x2, CSR_URND, x0 |
| |
| /* Read again. */ |
| csrrs x3, CSR_URND, x0 |
| |
| /* If two consecutive reads return the same random number that's most likely |
| a bug. (But it doesn't have to be a bug! Re-run the test if it fails.) */ |
| addi x31, x0, 32 |
| beq x2, x3, exit_fail |
| |
| |
| /* Read the URND WSR. */ |
| bn.wsrr w0, 0x2 /* URND */ |
| |
| /* Write w0 to DMEM. */ |
| la x11, urnd_out |
| bn.sid x0, 0(x11) |
| |
| /* Read the URND WSR again. */ |
| bn.wsrr w1, 0x2 /* URND */ |
| |
| jal x1, error_checking |
| ret |
| |
| /** |
| * This subroutine performs the following checks: |
| * |
| * `w0 != w1` The two random numbers read shall be different. |
| * `w0 != 0 ` The random numbers shall not have all bits equal to zero. |
| * `w0 != ~0` The random numbers shall not have all bits equal to one. |
| */ |
| error_checking: |
| /* Fail the test if the two numbers read from RND are equal. */ |
| bn.cmp w0, w1 |
| csrrs x2, CSR_FG0, x0 |
| andi x2, x2, 8 /* Extract Z (zero) bit.*/ |
| addi x31, x31, 1 |
| bne x2, x0, exit_fail |
| |
| /* Fail the test if all the bits are zero.*/ |
| bn.cmp w0, w31 |
| csrrs x2, CSR_FG0, x0 |
| andi x2, x2, 8 /* Extract Z (zero) bit.*/ |
| addi x31, x31, 1 |
| bne x2, x0, exit_fail |
| |
| /* Fail the test if all the bits are one. */ |
| bn.not w2, w31 /*`w2 = ~0`*/ |
| bn.cmp w0, w2 |
| csrrs x2, CSR_FG0, x0 |
| andi x2, x2, 8 /* Extract Z (zero) bit.*/ |
| addi x31, x31, 1 |
| bne x2, x0, exit_fail |
| |
| ret |
| |
| /* Terminate the program with a 0 (SUCCESS) return code. */ |
| exit_success: |
| li x10, 0 |
| la x11, rv |
| sw x10, 0(x11) |
| ecall |
| |
| /* Terminate the program with a 1 (FAIL) return code. */ |
| exit_fail: |
| li x10, 1 |
| la x11, rv |
| sw x10, 0(x11) |
| |
| la x11, fail_idx |
| sw x31, 0(x11) |
| |
| ecall |
| |
| .data |
| /* Return value. */ |
| .globl rv |
| .balign 4 |
| rv: |
| .word 0xFFFFFFFF |
| |
| /* Test status. */ |
| .globl fail_idx |
| .balign 4 |
| fail_idx: |
| .word 0x0 |
| |
| /* One of the values read from RND. */ |
| .globl rnd_out |
| .balign 32 |
| rnd_out: |
| .zero 32 |
| |
| /* One of the values read from URND. */ |
| .globl urnd_out |
| .balign 32 |
| urnd_out: |
| .zero 32 |