2323
2424#include " hyperram_memset.h"
2525
26+ // Simulation is much slower than execution on FPGA and these tests are primarily intended for
27+ // FPGA-based testing. Define this to 1 for use in simulation.
28+ #define SIMULATION 1
29+
2630using namespace CHERI ;
2731
2832const int RandTestBlockSize = 256 ;
29- const int HyperramSize = (1024 * 1024 ) / 4 ;
33+ #if SIMULATION
34+ // Note that this means many of the tests will be exercising only small fraction of the mapped
35+ // HyperRAM address range.
36+ const unsigned HyperramSize = HYPERRAM_BOUNDS / 1024 ;
37+ #else
38+ // Number of 32-bit words within the mapped HyperRAM.
39+ const unsigned HyperramSize = HYPERRAM_BOUNDS / 4 ;
40+ #endif
41+
42+ // The amount of HyperRAM that supports capability stores.
43+ const unsigned HyperramTagSize = HYPERRAM_TAG_BOUNDS / 4 ;
44+
45+ // Logging from the exception handling code.
46+ static Log *exc_log = NULL ;
47+
48+ // Signals whether an exception should be trapped and the faulting instruction skipped.
49+ static volatile bool trap_err = false ;
50+
51+ // Records whether an attempt to store a capability to the HyperRAM resulted in a
52+ // TL-UL bus error and thus an exception.
53+ static volatile bool act_err = false ;
54+
55+ // TODO: #429 Presently the debugger cannot perform sub-word writes, so pad the BSS to 4 bytes.
56+ volatile uint16_t dummy;
57+
58+ extern " C" void exception_handler (void ) {
59+ if (trap_err) {
60+ // Record the fact that an exception occurred.
61+ act_err = true ;
62+ // Advance over the failing instruction; this is a `csc` instruction but it may or may not be
63+ // compressed.
64+ __asm volatile (
65+ " cspecialr ct0, mepcc\n "
66+ " lh t2, 0(ct0)\n "
67+ " li t1, 3\n "
68+ " and t2, t2, t1\n "
69+ " bne t2, t1, instr16\n "
70+ " cincoffset ct0, ct0, 2\n "
71+ " instr16: cincoffset ct0, ct0, 2\n "
72+ " update: cspecialw mepcc, ct0" );
73+ } else if (exc_log) {
74+ uint32_t exc_addr = __builtin_cheri_address_get (get_mepcc ());
75+ exc_log->println (" Unexpected exception occurred at {:#x}" , exc_addr);
76+ while (1 ) asm volatile (" " );
77+ }
78+ }
3079
3180// Ensure that all writing of code to memory has completed before commencing execution
3281// of that code. Code has been written to [start, end] with both addresses being
@@ -129,10 +178,10 @@ int rand_cap_test(Capability<volatile uint32_t> hyperram_area,
129178 Capability<volatile uint32_t > read_cap;
130179
131180 do {
132- rand_index = prng () % HyperramSize ;
181+ rand_index = prng () % HyperramTagSize ;
133182
134183 // Capability is double word in size.
135- rand_cap_index = prng () % (HyperramSize / 2 );
184+ rand_cap_index = prng () % (HyperramTagSize / 2 );
136185 } while (rand_index / 2 == rand_cap_index);
137186
138187 rand_val = prng ();
@@ -670,6 +719,110 @@ int linear_execution_test(Capability<volatile uint32_t> hyperram_w_area, ds::xor
670719 return failures;
671720}
672721
722+ // Simple test of whether the full HyperRAM is mapped, as well checking that capabilities can
723+ // only be stored to the intended portion of this mapped range.
724+ int mapped_tagged_range_test (Capability<volatile uint32_t > hyperram_w_area,
725+ Capability<Capability<volatile uint32_t >> hyperram_cap_area, ds::xoroshiro::P64R32 &prng,
726+ Log &log, int iterations = 1 ) {
727+ const bool verbose = false ;
728+ int failures = 0 ;
729+
730+ // In the event that the entire HyperRAM supports capabilities, we must reduce two of our
731+ // directed choices to be within bounds.
732+ uint32_t tag_bounds_plus_8 = HYPERRAM_TAG_BOUNDS + 8 ;
733+ uint32_t tag_bounds = HYPERRAM_TAG_BOUNDS;
734+ if (tag_bounds_plus_8 >= HYPERRAM_BOUNDS) {
735+ tag_bounds_plus_8 = HYPERRAM_BOUNDS - 8 ;
736+ tag_bounds = HYPERRAM_BOUNDS - 16 ;
737+ }
738+
739+ for (int iter = 0 ; iter < iterations; ++iter) {
740+ Capability<volatile uint32_t > read_cap;
741+ unsigned rand_choice = prng () & 7u ;
742+ uint32_t rand_addr;
743+
744+ switch (rand_choice) {
745+ // Directed choices.
746+ case 0u :
747+ rand_addr = tag_bounds;
748+ break ;
749+ case 1u :
750+ rand_addr = HYPERRAM_TAG_BOUNDS - 8 ;
751+ break ;
752+ case 2u :
753+ rand_addr = HYPERRAM_BOUNDS - 8 ;
754+ break ;
755+ case 3u :
756+ rand_addr = tag_bounds_plus_8;
757+ break ;
758+ case 4u :
759+ rand_addr = 0u ;
760+ break ;
761+ // Randomised choices.
762+ default :
763+ rand_addr = prng () & (HYPERRAM_BOUNDS - 8u );
764+ break ;
765+ }
766+
767+ // Predict whether we should see a TL-UL error in response; only the first portion of the
768+ // mapped HyperRAM supports tag bits. Anything at a higher address should raise a TL-UL error.
769+ bool exp_err = (rand_addr >= HYPERRAM_TAG_BOUNDS);
770+ if (verbose) {
771+ log.println (" addr {:#x}" , rand_addr);
772+ }
773+
774+ // We are expecting to generate a TL-UL error with some store operations.
775+ trap_err = true ;
776+ // First store some data that does not constitute a sensible capability.
777+ const uint32_t exp_data1 = 0x87654321u ;
778+ const uint32_t exp_data0 = ~0u ;
779+ hyperram_w_area[rand_addr >> 2 ] = exp_data0;
780+ hyperram_w_area[(rand_addr >> 2 ) + 1 ] = exp_data1;
781+
782+ // Attempt to store a capability to the chosen address.
783+ // The capability stored doesn't really matter; just use the HyperRAM base.
784+ act_err = false ;
785+ hyperram_cap_area[rand_addr >> 3 ] = hyperram_w_area;
786+ trap_err = false ;
787+ if (verbose) {
788+ log.println (" done write" );
789+ }
790+ read_cap = hyperram_cap_area[rand_addr >> 3 ];
791+
792+ // Check that an error occurred iff expected.
793+ failures += (act_err != exp_err);
794+ if (verbose) {
795+ log.println (" Act err {}, exp err {}" , (int )act_err, (int )exp_err);
796+ }
797+
798+ // Check the memory contents.
799+ if (exp_err) {
800+ // If an error occurred then we expect _not_ to have performed the write, so the test data
801+ // should still be intact.
802+ uint32_t act_data1 = hyperram_w_area[(rand_addr >> 2 ) + 1 ];
803+ uint32_t act_data0 = hyperram_w_area[rand_addr >> 2 ];
804+ if (verbose) {
805+ log.println (" Wrote {:#x}:{:#x}, read back {:#x}:{:#x}" , exp_data0, exp_data1, act_data0, act_data1);
806+ }
807+ if (exp_data0 != act_data0 || exp_data1 != act_data1) {
808+ failures++;
809+ }
810+ } else {
811+ // If there was no error, the capability should have been stored as expected.
812+ if (verbose) {
813+ volatile uint32_t *exp = hyperram_w_area.get ();
814+ volatile uint32_t *act = read_cap.get ();
815+ log.println (" Wrote {:#x}, read back {:#x}" , (uint32_t )exp, (uint32_t )act);
816+ }
817+ if (read_cap != hyperram_w_area) {
818+ failures++;
819+ }
820+ }
821+ }
822+
823+ return failures;
824+ }
825+
673826/* *
674827 * C++ entry point for the loader. This is called from assembly, with the
675828 * read-write root in the first argument.
@@ -685,6 +838,8 @@ extern "C" [[noreturn]] void entry_point(void *rwRoot) {
685838 uart0->init (BAUD_RATE);
686839 WriteUart uart{uart0};
687840 Log log (uart);
841+ // Make logging available to the exception handler.
842+ exc_log = &log;
688843
689844 set_console_mode (log, CC_BOLD);
690845 log.print (" \r\n\r\n Get hyped for hyperram!\r\n " );
@@ -694,24 +849,32 @@ extern "C" [[noreturn]] void entry_point(void *rwRoot) {
694849 prng.set_state (0xDEADBEEF , 0xBAADCAFE );
695850
696851 // Default is word-based accesses, which is sufficient for most tests.
852+ //
853+ // Unfortunately it is not possible to construct a capability that covers exactly the 8MiB range
854+ // of the HyperRAM because of the encoding limitations of the CHERIoT capabilities.
855+ //
856+ // Here, in this manually-invoked test, we resort to mapping twice that address range because
857+ // it is more important that we test all of the physical HyperRAM connectivity and logic, rather
858+ // than the CHERIoT capabilities.
697859 Capability<volatile uint32_t > hyperram_area = root.cast <volatile uint32_t >();
860+ uint32_t bounds = 2 * HYPERRAM_BOUNDS;
698861 hyperram_area.address () = HYPERRAM_ADDRESS;
699- hyperram_area.bounds () = HYPERRAM_BOUNDS ;
862+ hyperram_area.bounds () = bounds ;
700863
701864 Capability<Capability<volatile uint32_t >> hyperram_cap_area = root.cast <Capability<volatile uint32_t >>();
702865 hyperram_cap_area.address () = HYPERRAM_ADDRESS;
703- hyperram_cap_area.bounds () = HYPERRAM_BOUNDS ;
866+ hyperram_cap_area.bounds () = bounds ;
704867
705868 // We also want byte, hword and dword access for some tests.
706869 Capability<volatile uint8_t > hyperram_b_area = root.cast <volatile uint8_t >();
707870 hyperram_b_area.address () = HYPERRAM_ADDRESS;
708- hyperram_b_area.bounds () = HYPERRAM_BOUNDS ;
871+ hyperram_b_area.bounds () = bounds ;
709872 Capability<volatile uint16_t > hyperram_h_area = root.cast <volatile uint16_t >();
710873 hyperram_h_area.address () = HYPERRAM_ADDRESS;
711- hyperram_h_area.bounds () = HYPERRAM_BOUNDS ;
874+ hyperram_h_area.bounds () = bounds ;
712875 Capability<volatile uint64_t > hyperram_d_area = root.cast <volatile uint64_t >();
713876 hyperram_d_area.address () = HYPERRAM_ADDRESS;
714- hyperram_d_area.bounds () = HYPERRAM_BOUNDS ;
877+ hyperram_d_area.bounds () = bounds ;
715878
716879 // Run indefinitely, soak testing until we observe one or more failures.
717880 int failures = 0 ;
@@ -821,6 +984,10 @@ extern "C" [[noreturn]] void entry_point(void *rwRoot) {
821984 }
822985 log.print (" result..." );
823986 write_test_result (log, failures);
987+
988+ log.println (" Running mapped/tagged range test..." );
989+ failures += mapped_tagged_range_test (hyperram_area, hyperram_cap_area, prng, log, 0x400u );
990+ write_test_result (log, failures);
824991 }
825992
826993 // Report test failure.
0 commit comments