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 0
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+ // Signals whether an exception should be trapped and the faulting instruction skipped.
46+ static volatile bool trap_err = false ;
47+
48+ // Records whether an attempt to store a capability to the HyperRAM resulted in a
49+ // TL-UL bus error and thus an exception.
50+ static volatile bool act_err = false ;
51+
52+ // TODO: #429 Presently the debugger cannot perform sub-word writes, so pad the BSS to 4 bytes.
53+ volatile uint16_t dummy;
54+
55+ extern " C" void exception_handler (void ) {
56+ if (trap_err) {
57+ // Record the fact that an exception occurred.
58+ act_err = true ;
59+ // Advance over the failing instruction; this is a `csc` instruction but it may or may not be
60+ // compressed.
61+ __asm volatile (
62+ " cspecialr ct0, mepcc\n "
63+ " lh t2, 0(ct0)\n "
64+ " li t1, 3\n "
65+ " and t2, t2, t1\n "
66+ " bne t2, t1, instr16\n "
67+ " cincoffset ct0, ct0, 2\n "
68+ " instr16: cincoffset ct0, ct0, 2\n "
69+ " update: cspecialw mepcc, ct0" );
70+ }
71+ }
3072
3173// Ensure that all writing of code to memory has completed before commencing execution
3274// of that code. Code has been written to [start, end] with both addresses being
@@ -129,10 +171,10 @@ int rand_cap_test(Capability<volatile uint32_t> hyperram_area,
129171 Capability<volatile uint32_t > read_cap;
130172
131173 do {
132- rand_index = prng () % HyperramSize ;
174+ rand_index = prng () % HyperramTagSize ;
133175
134176 // Capability is double word in size.
135- rand_cap_index = prng () % (HyperramSize / 2 );
177+ rand_cap_index = prng () % (HyperramTagSize / 2 );
136178 } while (rand_index / 2 == rand_cap_index);
137179
138180 rand_val = prng ();
@@ -670,6 +712,110 @@ int linear_execution_test(Capability<volatile uint32_t> hyperram_w_area, ds::xor
670712 return failures;
671713}
672714
715+ // Simple test of whether the full HyperRAM is mapped, as well checking that capabilities can
716+ // only be stored to the intended portion of this mapped range.
717+ int mapped_tagged_range_test (Capability<volatile uint32_t > hyperram_w_area,
718+ Capability<Capability<volatile uint32_t >> hyperram_cap_area, ds::xoroshiro::P64R32 &prng,
719+ Log &log, int iterations = 1 ) {
720+ const bool verbose = false ;
721+ int failures = 0 ;
722+
723+ // In the event that the entire HyperRAM supports capabilities, we must reduce two of our
724+ // directed choices to be within bounds.
725+ uint32_t tag_bounds_plus_8 = HYPERRAM_TAG_BOUNDS + 8 ;
726+ uint32_t tag_bounds = HYPERRAM_TAG_BOUNDS;
727+ if (tag_bounds_plus_8 >= HYPERRAM_BOUNDS) {
728+ tag_bounds_plus_8 = HYPERRAM_BOUNDS - 8 ;
729+ tag_bounds = HYPERRAM_BOUNDS - 16 ;
730+ }
731+
732+ for (int iter = 0 ; iter < iterations; ++iter) {
733+ Capability<volatile uint32_t > read_cap;
734+ unsigned rand_choice = prng () & 7u ;
735+ uint32_t rand_addr;
736+
737+ switch (rand_choice) {
738+ // Directed choices.
739+ case 0u :
740+ rand_addr = tag_bounds;
741+ break ;
742+ case 1u :
743+ rand_addr = HYPERRAM_TAG_BOUNDS - 8 ;
744+ break ;
745+ case 2u :
746+ rand_addr = HYPERRAM_BOUNDS - 8 ;
747+ break ;
748+ case 3u :
749+ rand_addr = tag_bounds_plus_8;
750+ break ;
751+ case 4u :
752+ rand_addr = 0u ;
753+ break ;
754+ // Randomised choices.
755+ default :
756+ rand_addr = prng () & (HYPERRAM_BOUNDS - 8u );
757+ break ;
758+ }
759+
760+ // Predict whether we should see a TL-UL error in response; only the first portion of the
761+ // mapped HyperRAM supports tag bits. Anything at a higher address should raise a TL-UL error.
762+ bool exp_err = (rand_addr >= HYPERRAM_TAG_BOUNDS);
763+ if (verbose) {
764+ log.println (" addr {:#x}" , rand_addr);
765+ }
766+
767+ // We are expecting to generate a TL-UL error with some store operations.
768+ trap_err = true ;
769+ // First store some data that does not constitute a sensible capability.
770+ const uint32_t exp_data1 = 0x87654321u ;
771+ const uint32_t exp_data0 = ~0u ;
772+ hyperram_w_area[rand_addr >> 2 ] = exp_data0;
773+ hyperram_w_area[(rand_addr >> 2 ) + 1 ] = exp_data1;
774+
775+ // Attempt to store a capability to the chosen address.
776+ // The capability stored doesn't really matter; just use the HyperRAM base.
777+ act_err = false ;
778+ hyperram_cap_area[rand_addr >> 3 ] = hyperram_w_area;
779+ trap_err = false ;
780+ if (verbose) {
781+ log.println (" done write" );
782+ }
783+ read_cap = hyperram_cap_area[rand_addr >> 3 ];
784+
785+ // Check that an error occurred iff expected.
786+ failures += (act_err != exp_err);
787+ if (verbose) {
788+ log.println (" Act err {}, exp err {}" , (int )act_err, (int )exp_err);
789+ }
790+
791+ // Check the memory contents.
792+ if (exp_err) {
793+ // If an error occurred then we expect _not_ to have performed the write, so the test data
794+ // should still be intact.
795+ uint32_t act_data1 = hyperram_w_area[(rand_addr >> 2 ) + 1 ];
796+ uint32_t act_data0 = hyperram_w_area[rand_addr >> 2 ];
797+ if (verbose) {
798+ log.println (" Wrote {:#x}:{:#x}, read back {:#x}:{:#x}" , exp_data0, exp_data1, act_data0, act_data1);
799+ }
800+ if (exp_data0 != act_data0 || exp_data1 != act_data1) {
801+ failures++;
802+ }
803+ } else {
804+ // If there was no error, the capability should have been stored as expected.
805+ if (verbose) {
806+ volatile uint32_t *exp = hyperram_w_area.get ();
807+ volatile uint32_t *act = read_cap.get ();
808+ log.println (" Wrote {:#x}, read back {:#x}" , (uint32_t )exp, (uint32_t )act);
809+ }
810+ if (read_cap != hyperram_w_area) {
811+ failures++;
812+ }
813+ }
814+ }
815+
816+ return failures;
817+ }
818+
673819/* *
674820 * C++ entry point for the loader. This is called from assembly, with the
675821 * read-write root in the first argument.
@@ -695,23 +841,52 @@ extern "C" [[noreturn]] void entry_point(void *rwRoot) {
695841
696842 // Default is word-based accesses, which is sufficient for most tests.
697843 Capability<volatile uint32_t > hyperram_area = root.cast <volatile uint32_t >();
698- hyperram_area.address () = HYPERRAM_ADDRESS;
699- hyperram_area.bounds () = HYPERRAM_BOUNDS;
844+ uint32_t bounds = HYPERRAM_BOUNDS;
845+ // Unfortunately it is not possible to construct a capability that covers exactly the 8MiB range
846+ // of the HyperRAM because of the encoding limitations of the CHERIoT capabilities.
847+ switch (1 ) {
848+ // Courtesy of Leo; CHERIoT can construct a capability that covers 8MiB - 16KiB.
849+ case 0 :
850+ hyperram_area.address () = HYPERRAM_ADDRESS;
851+ bounds = HYPERRAM_BOUNDS - 0x4000 ;
852+ break ;
853+ // Cover only the tagged region.
854+ case 1 :
855+ hyperram_area.address () = HYPERRAM_ADDRESS;
856+ bounds = HYPERRAM_TAG_BOUNDS;
857+ break ;
858+ // Cover just the untagged region.
859+ case 2 :
860+ hyperram_area.address () = HYPERRAM_ADDRESS + HYPERRAM_TAG_BOUNDS;
861+ bounds = HYPERRAM_BOUNDS - HYPERRAM_TAG_BOUNDS;
862+ break ;
863+ // Map twice the valid region.
864+ case 3 :
865+ hyperram_area.address () = HYPERRAM_ADDRESS;
866+ bounds = 2 * HYPERRAM_BOUNDS;
867+ break ;
868+ // This case does not work because the exponent becomes too large to be able to represent the
869+ // 8MiB range, and instead we end up with bounds of 16MiB and an invalid capability.
870+ default :
871+ hyperram_area.address () = HYPERRAM_ADDRESS;
872+ bounds = HYPERRAM_TAG_BOUNDS;
873+ break ;
874+ }
700875
701876 Capability<Capability<volatile uint32_t >> hyperram_cap_area = root.cast <Capability<volatile uint32_t >>();
702877 hyperram_cap_area.address () = HYPERRAM_ADDRESS;
703- hyperram_cap_area.bounds () = HYPERRAM_BOUNDS ;
878+ hyperram_cap_area.bounds () = bounds ;
704879
705880 // We also want byte, hword and dword access for some tests.
706881 Capability<volatile uint8_t > hyperram_b_area = root.cast <volatile uint8_t >();
707882 hyperram_b_area.address () = HYPERRAM_ADDRESS;
708- hyperram_b_area.bounds () = HYPERRAM_BOUNDS ;
883+ hyperram_b_area.bounds () = bounds ;
709884 Capability<volatile uint16_t > hyperram_h_area = root.cast <volatile uint16_t >();
710885 hyperram_h_area.address () = HYPERRAM_ADDRESS;
711- hyperram_h_area.bounds () = HYPERRAM_BOUNDS ;
886+ hyperram_h_area.bounds () = bounds ;
712887 Capability<volatile uint64_t > hyperram_d_area = root.cast <volatile uint64_t >();
713888 hyperram_d_area.address () = HYPERRAM_ADDRESS;
714- hyperram_d_area.bounds () = HYPERRAM_BOUNDS ;
889+ hyperram_d_area.bounds () = bounds ;
715890
716891 // Run indefinitely, soak testing until we observe one or more failures.
717892 int failures = 0 ;
@@ -821,6 +996,10 @@ extern "C" [[noreturn]] void entry_point(void *rwRoot) {
821996 }
822997 log.print (" result..." );
823998 write_test_result (log, failures);
999+
1000+ log.println (" Running mapped/tagged range test..." );
1001+ failures += mapped_tagged_range_test (hyperram_area, hyperram_cap_area, prng, log, 0x400u );
1002+ write_test_result (log, failures);
8241003 }
8251004
8261005 // Report test failure.
0 commit comments