From eb2461a488e1ed69e9db893ac233c3fb927ee560 Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Thu, 21 Sep 2023 14:13:57 +1000 Subject: [PATCH] boards: opentitan: Fixup existing test cases Signed-off-by: Alistair Francis --- boards/opentitan/README.md | 38 +- boards/opentitan/src/tests/flash.rs | 475 ++++++++++++++++++++++ boards/opentitan/src/tests/flash_ctrl.rs | 418 ------------------- boards/opentitan/src/tests/mod.rs | 7 +- boards/opentitan/src/tests/multi_alarm.rs | 1 - boards/opentitan/src/tests/sip_hash.rs | 2 +- boards/opentitan/src/tests/tickv_test.rs | 63 --- 7 files changed, 490 insertions(+), 514 deletions(-) create mode 100644 boards/opentitan/src/tests/flash.rs delete mode 100644 boards/opentitan/src/tests/flash_ctrl.rs delete mode 100644 boards/opentitan/src/tests/tickv_test.rs diff --git a/boards/opentitan/README.md b/boards/opentitan/README.md index c83c2aaf25..af3ca4473f 100644 --- a/boards/opentitan/README.md +++ b/boards/opentitan/README.md @@ -3,7 +3,7 @@ OpenTitan RISC-V Board - https://opentitan.org/ -OpenTitan is the first open source project building a transparent, +OpenTitan is an open source project building a transparent, high-quality reference design and integration guidelines for silicon root of trust (RoT) chips. @@ -28,24 +28,28 @@ or simulator built from that version of the OpenTitan codebase. Programming ----------- -The latest supported version (commit SHA) of OpenTitan is specified in the [OPENTITAN_SUPPORTED_SHA](https://github.com/tock/tock/blob/master/boards/opentitan/earlgrey-cw310/Makefile) make variable found: -> boards/opentitan/earlgrey-cw310/Makefile +The latest supported version (commit SHA) of OpenTitan is specified in the +[OPENTITAN_SUPPORTED_SHA](https://github.com/tock/tock/blob/master/boards/opentitan/earlgrey-cw310/Makefile) +make variable found in `boards/opentitan/earlgrey-cw310/Makefile` In *general* it is recommended that users start with the commit specified by `OPENTITAN_SUPPORTED_SHA` as newer versions **have not been** tested. > Note: when building, you can pass in `SKIP_OT_VERSION_CHECK=yes` to skip the trivial OpenTitan version check, this maybe useful when developing or testing across multiple versions of OpenTitan. -Unfortunately the OpenTitan documentation is out of sync with the Tock setup. -For instructions that match the OpenTitan version Tock supports you will need -to read the -[raw docs via git](https://github.com/lowRISC/opentitan/tree/d072ac505f82152678d6e04be95c72b728a347b8/doc/ug). - Setup ----- ### Setup OpenTitan +You can follow the steps at https://opentitan.org/book/doc/getting_started/index.html +for a guide to setup the OpenTitan repo. + +There are more details for setting up dependencies available at +https://opentitan.org/book/doc/getting_started/unofficial/index.html + +The quick steps for setting up the OpenTitan repo are + ```shell git clone https://github.com/lowRISC/opentitan.git cd opentitan @@ -54,22 +58,6 @@ cd opentitan git checkout pip3 install --user -r python-requirements.txt ``` -Make sure to follow [OpenTitan getting started instructions](https://docs.opentitan.org/doc/getting_started/) to setup required dependencies/toolchains. - -### **Fedora dependencies quick install** - -Note: the OpenTitan documentation provides an easy installation for packages for Ubuntu based distributions. This is an equivalent command to install the (mostly) same packages for Fedora users. - -```shell -sudo dnf install autoconf bison make automake gcc gcc-c++ kernel-devel \ - clang-tools-extra clang cmake curl \ - doxygen flex g++ git golang lcov elfutils-libelf \ - libftdi libftdi-devel ncurses-compat-libs openssl-devel \ - systemd-devel libusb redhat-lsb-core \ - make ninja-build perl pkgconf python3 python3-pip python3-setuptools \ - python3-urllib3 python3-wheel srecord tree xsltproc zlib-devel xz clang-tools-extra \ - clang11-libs clang-devel elfutils-libelf-devel -``` ChipWhisper CW310 ----------------- @@ -100,7 +88,7 @@ Then you need to flash the bitstream with: ```shell -./bazel-bin/sw/host/opentitantool/opentitantool.runfiles/lowrisc_opentitan/sw/host/opentitantool/opentitantool --interface=cw310 fpga load-bitstream lowrisc_systems_chip_earlgrey_cw310_0.1.bit +./bazel-bin/sw/host/opentitantool/opentitantool.runfiles/lowrisc_opentitan/sw/host/opentitantool/opentitantool --interface=cw310 fpga load-bitstream lowrisc_systems_chip_earlgrey_cw310_0.1.bit.orig ``` After which you should see some output in the serial window. diff --git a/boards/opentitan/src/tests/flash.rs b/boards/opentitan/src/tests/flash.rs new file mode 100644 index 0000000000..85fe59eaa7 --- /dev/null +++ b/boards/opentitan/src/tests/flash.rs @@ -0,0 +1,475 @@ +// Licensed under the Apache License, Version 2.0 or the MIT License. +// SPDX-License-Identifier: Apache-2.0 OR MIT +// Copyright Tock Contributors 2022. + +//! Test the opentitan flash implementation + +mod key_value { + use crate::tests::run_kernel_op; + use crate::{SIPHASH, TICKV}; + use capsules_core::virtualizers::virtual_flash::FlashUser; + use capsules_extra::test::kv_system::KVSystemTest; + use capsules_extra::tickv::KVSystem; + use capsules_extra::tickv::{TicKVKeyType, TicKVSystem}; + use kernel::debug; + use kernel::hil::hasher::Hasher; + use kernel::static_init; + use kernel::utilities::leasable_buffer::SubSliceMut; + + #[test_case] + fn tickv_append_key() { + debug!("start TicKV append key test..."); + + unsafe { + let tickv = TICKV.unwrap(); + let sip_hasher = SIPHASH.unwrap(); + + let key_input = static_init!( + [u8; 16], + [ + 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, + 0xBC, 0xDE, 0xF0 + ] + ); + let key = static_init!([u8; 8], [0; 8]); + let value = static_init!([u8; 3], [0x10, 0x20, 0x30]); + let ret = static_init!([u8; 4], [0; 4]); + + let test = static_init!( + KVSystemTest< + 'static, + TicKVSystem< + 'static, + FlashUser<'static, lowrisc::flash_ctrl::FlashCtrl<'static>>, + capsules_extra::sip_hash::SipHasher24, + 2048, + >, + TicKVKeyType, + >, + KVSystemTest::new(tickv, SubSliceMut::new(value), ret) + ); + + sip_hasher.set_client(tickv); + tickv.set_client(test); + + // Kick start the tests by generating a key + tickv + .generate_key(SubSliceMut::new(key_input), key) + .unwrap(); + } + run_kernel_op(100000); + + debug!(" [ok]"); + run_kernel_op(100); + } +} + +mod protections_and_controller { + use crate::tests::run_kernel_op; + use crate::PERIPHERALS; + use core::cell::Cell; + use kernel::debug; + use kernel::errorcode::ErrorCode; + use kernel::hil; + use kernel::hil::flash::Flash; + use kernel::hil::flash::HasClient; + use kernel::static_init; + use kernel::utilities::cells::TakeCell; + use lowrisc::flash_ctrl::FlashMPConfig; + use lowrisc::flash_ctrl::FLASH_ADDR_OFFSET; + use lowrisc::flash_ctrl::PAGE_SIZE; + + struct FlashCtlCallBack { + read_pending: Cell, + write_pending: Cell, + // A lowrisc page to for reads/writes + read_in_page: TakeCell<'static, lowrisc::flash_ctrl::LowRiscPage>, + write_in_page: TakeCell<'static, lowrisc::flash_ctrl::LowRiscPage>, + // We recover the callback returned buffer into these + read_out_buf: TakeCell<'static, [u8]>, + write_out_buf: TakeCell<'static, [u8]>, + // Flag if an MP fault was detected + mp_fault_detect: Cell, + } + + impl<'a> FlashCtlCallBack { + fn new( + read_in_page: &'static mut lowrisc::flash_ctrl::LowRiscPage, + write_in_page: &'static mut lowrisc::flash_ctrl::LowRiscPage, + ) -> Self { + FlashCtlCallBack { + read_pending: Cell::new(false), + write_pending: Cell::new(false), + read_in_page: TakeCell::new(read_in_page), + write_in_page: TakeCell::new(write_in_page), + read_out_buf: TakeCell::empty(), + write_out_buf: TakeCell::empty(), + mp_fault_detect: Cell::new(false), + } + } + + fn reset(&self) { + self.read_pending.set(false); + self.write_pending.set(false); + self.mp_fault_detect.set(false); + } + } + + impl<'a, F: hil::flash::Flash> hil::flash::Client for FlashCtlCallBack { + fn read_complete(&self, page: &'static mut F::Page, error: hil::flash::Error) { + if self.read_pending.get() { + if error == hil::flash::Error::FlashMemoryProtectionError { + self.mp_fault_detect.set(true); + } else { + assert_eq!(error, hil::flash::Error::CommandComplete); + } + self.read_out_buf.replace(page.as_mut()); + self.read_pending.set(false); + } + } + + fn write_complete(&self, page: &'static mut F::Page, error: hil::flash::Error) { + if self.write_pending.get() { + if error == hil::flash::Error::FlashMemoryProtectionError { + self.mp_fault_detect.set(true); + } else { + assert_eq!(error, hil::flash::Error::CommandComplete); + } + self.write_out_buf.replace(page.as_mut()); + self.write_pending.set(false); + } + } + + fn erase_complete(&self, error: hil::flash::Error) { + // Caller may check by a successive page read to assert the erased + // page is composed of 0xFF (all erased bits should be 1) + if error == hil::flash::Error::FlashMemoryProtectionError { + self.mp_fault_detect.set(true); + } else { + assert_eq!(error, hil::flash::Error::CommandComplete); + } + } + } + + macro_rules! static_init_test { + () => {{ + let r_in_page = static_init!( + lowrisc::flash_ctrl::LowRiscPage, + lowrisc::flash_ctrl::LowRiscPage::default() + ); + let w_in_page = static_init!( + lowrisc::flash_ctrl::LowRiscPage, + lowrisc::flash_ctrl::LowRiscPage::default() + ); + let mut val: u8 = 0; + + for i in 0..lowrisc::flash_ctrl::PAGE_SIZE { + val = val.wrapping_add(10); + r_in_page[i] = 0x00; + w_in_page[i] = 0xAA; // Arbitrary Data + } + static_init!( + FlashCtlCallBack, + FlashCtlCallBack::new(r_in_page, w_in_page) + ) + }}; + } + + /// The only 'test_case' for flash_ctrl as directly invoked by the test runner, + /// this calls all the other tests, preserving the order in which they must + /// be ran. + #[test_case] + fn flash_ctrl_tester() { + flash_ctrl_read_write_page(); + flash_ctrl_erase_page(); + flash_ctrl_mp_basic(); + flash_ctrl_mp_functionality(); + } + + // Note: the tests below need to run in a particular order, hence the a, b, c... + // function name prefix (test runner seems to invoke them alphabetically). + + /// Tests: Erase Page -> Write Page -> Read Page + /// + /// Compare the data we wrote is stored in flash with a + /// successive read. + fn flash_ctrl_read_write_page() { + let perf = unsafe { PERIPHERALS.unwrap() }; + let flash_ctl = &perf.flash_ctrl; + + let cb = unsafe { static_init_test!() }; + flash_ctl.set_client(cb); + cb.reset(); + + debug!("[FLASH_CTRL] Test page read/write...."); + + #[cfg(feature = "hardware_tests")] + { + let page_num: usize = 511; + run_kernel_op(100); + // Lets do a page erase + assert!(flash_ctl.erase_page(page_num).is_ok()); + run_kernel_op(100); + + // Do Page Write + let write_page = cb.write_in_page.take().unwrap(); + assert!(flash_ctl.write_page(page_num, write_page).is_ok()); + cb.write_pending.set(true); + run_kernel_op(100); + // OP Complete, buffer recovered. + assert!(!cb.write_pending.get()); + cb.reset(); + + // Read the same page + let read_page = cb.read_in_page.take().unwrap(); + assert!(flash_ctl.read_page(page_num, read_page).is_ok()); + cb.read_pending.set(true); + run_kernel_op(100); + assert!(!cb.read_pending.get()); + cb.reset(); + + // Compare r/w buffer + let write_in = cb.write_out_buf.take().unwrap(); // Recovered buffer is saved here as &[u8] + let read_out = cb.read_out_buf.take().unwrap(); + + assert_eq!(write_in.len(), read_out.len()); + assert!( + write_in.iter().zip(read_out.iter()).all(|(i, j)| i == j), + "[ERR] Read data indicates flash write error on page {}", + page_num + ); + + cb.write_out_buf.replace(write_in); + cb.read_out_buf.replace(read_out); + } + + run_kernel_op(100); + debug!(" [ok]"); + run_kernel_op(100); + } + + /// Tests: Erase Page -> Write Page -> Erase Page -> Read Page + /// A page erased should set all bits to `1`s or all bytes in page to + /// `0xFF`. Assert this is true after writing data to a page and erasing + /// the page. + fn flash_ctrl_erase_page() { + let perf = unsafe { PERIPHERALS.unwrap() }; + let flash_ctl = &perf.flash_ctrl; + + let cb = unsafe { static_init_test!() }; + cb.reset(); + flash_ctl.set_client(cb); + + debug!("[FLASH_CTRL] Test page erase...."); + + #[cfg(feature = "hardware_tests")] + { + let page_num: usize = 500; + run_kernel_op(100); + // Lets do a page erase + assert!(flash_ctl.erase_page(page_num).is_ok()); + run_kernel_op(100); + + // Do Page Write + let write_page = cb.write_in_page.take().unwrap(); + assert!(flash_ctl.write_page(page_num, write_page).is_ok()); + cb.write_pending.set(true); + run_kernel_op(100); + // OP Complete, buffer recovered. + assert!(!cb.write_pending.get()); + cb.reset(); + + // Erase again + assert!(flash_ctl.erase_page(page_num).is_ok()); + run_kernel_op(100); + + // Read Page + let read_page = cb.read_in_page.take().unwrap(); + assert!(flash_ctl.read_page(page_num, read_page).is_ok()); + cb.read_pending.set(true); + run_kernel_op(100); + assert!(!cb.read_pending.get()); + cb.reset(); + + // Check that the erased paged is all `0xFF` bytes + let read_out = cb.read_out_buf.take().unwrap(); + assert!( + read_out.iter().all(|&a| a == 0xFF), + "[ERR] Read data indicates erase failure on page {}", + page_num + ); + + cb.read_out_buf.replace(read_out); + } + run_kernel_op(100); + debug!(" [ok]"); + run_kernel_op(100); + } + + /// Tests: The basic api functionality and error handling of invalid arguments. + fn flash_ctrl_mp_basic() { + debug!("[FLASH_CTRL] Test memory protection api...."); + + #[cfg(feature = "hardware_tests")] + { + let perf = unsafe { PERIPHERALS.unwrap() }; + let flash_ctl = &perf.flash_ctrl; + // BANK1 + let base_page_addr: usize = (400 * PAGE_SIZE).saturating_add(FLASH_ADDR_OFFSET); + // Pages indexing starts with 0 and we have 512 pages. + let invalid_page_addr: usize = (512 * PAGE_SIZE).saturating_add(FLASH_ADDR_OFFSET); + let valid_num_pages: usize = 10; + // Note: Region 0 is occupied by board setup + let valid_region: usize = 6; + let invalid_region: usize = 8; + let num_regions = flash_ctl.mp_get_num_regions().unwrap(); + // WARN: Revisit these tests if cfgs have changed in HW + assert_eq!(num_regions, lowrisc::flash_ctrl::FLASH_MP_MAX_CFGS as u32); + + for region_num in 0..8 { + // All 8 regions should be unlocked at reset + assert!(flash_ctl.mp_is_region_locked(region_num).is_ok()) + } + + let cfg_set = FlashMPConfig { + read_en: false, + write_en: true, + erase_en: false, + scramble_en: false, + ecc_en: true, + he_en: false, + }; + // Expect Fail + assert_eq!( + flash_ctl.mp_set_region_perms( + base_page_addr, + invalid_page_addr, + valid_region, + &cfg_set + ), + Err(ErrorCode::NOSUPPORT) + ); + assert_eq!( + flash_ctl.mp_set_region_perms( + invalid_page_addr, + base_page_addr, + valid_region, + &cfg_set + ), + Err(ErrorCode::NOSUPPORT) + ); + assert_eq!( + flash_ctl.mp_set_region_perms( + base_page_addr, + base_page_addr.saturating_add(valid_num_pages * PAGE_SIZE), + invalid_region, + &cfg_set + ), + Err(ErrorCode::NOSUPPORT) + ); + // Set Perms + assert!(flash_ctl + .mp_set_region_perms( + base_page_addr, + base_page_addr.saturating_add(valid_num_pages * PAGE_SIZE), + valid_region, + &cfg_set + ) + .is_ok()); + // Check Perms + assert_eq!( + flash_ctl.mp_read_region_perms(valid_region).unwrap(), + cfg_set + ); + // Lock region + assert!(flash_ctl.mp_lock_region_cfg(valid_region).is_ok()); + assert!(flash_ctl.mp_is_region_locked(valid_region).unwrap()); + } + + run_kernel_op(100); + debug!(" [ok]"); + run_kernel_op(100); + } + + /// Tests the memory protection functionality of the flash_ctrl + /// Test: Setup memory protection -> Do bad OP/cause an MP Fault -> Expect fail/assert Err(FlashMPFault) + fn flash_ctrl_mp_functionality() { + debug!("[FLASH_CTRL] Test memory protection functionality...."); + + #[cfg(feature = "hardware_tests")] + { + let perf = unsafe { PERIPHERALS.unwrap() }; + let flash_ctl = &perf.flash_ctrl; + let cb = unsafe { static_init_test!() }; + cb.reset(); + flash_ctl.set_client(cb); + + // BANK1 + let page_num: usize = 450; + let base_page_addr: usize = (page_num * PAGE_SIZE).saturating_add(FLASH_ADDR_OFFSET); + let num_pages: usize = 25; + // Note: Region 0 is occupied by board setup + let region: usize = 7; + let invalid_region: usize = 142; + + for region_num in 0..8 { + // All 8 regions should be unlocked at reset + assert!(flash_ctl.mp_is_region_locked(region_num).is_ok()) + } + + let cfg_set = FlashMPConfig { + // NOTE: We disable read access, then later try to read to trigger the fault + read_en: false, + write_en: true, + // NOTE: We disable erase perms, then later try to erase and trigger the fault + erase_en: false, + scramble_en: true, + ecc_en: false, + he_en: true, + }; + // Set Perms + assert!(flash_ctl + .mp_set_region_perms( + base_page_addr, + base_page_addr.saturating_add(num_pages * PAGE_SIZE), + region, + &cfg_set + ) + .is_ok()); + // Check Perms + assert_eq!(flash_ctl.mp_read_region_perms(region).unwrap(), cfg_set); + // Lock Config - Expect Fail + assert_eq!( + flash_ctl.mp_lock_region_cfg(invalid_region), + Err(ErrorCode::NOSUPPORT) + ); + // Lock Config + assert!(flash_ctl.mp_lock_region_cfg(region).is_ok()); + assert!(flash_ctl.mp_is_region_locked(region).unwrap()); + + // Functionality Test 1: We disabled erase for this region, lets try to erase + assert!(flash_ctl.erase_page(page_num).is_ok()); + run_kernel_op(100); + // Ensure that a MP violation was detected + assert!(cb.mp_fault_detect.get()); + // Clear the fault + cb.reset(); + + // Functionality Test 2: We disabled read for this region, lets try to read + // This should trigger an MP fault + // Read Page + let read_page = cb.read_in_page.take().unwrap(); + assert!(flash_ctl.read_page(page_num, read_page).is_ok()); + cb.read_pending.set(true); + run_kernel_op(100); + assert!(!cb.read_pending.get()); + // Ensure that a MP violation was detected + assert!(cb.mp_fault_detect.get()); + cb.reset(); + } + + run_kernel_op(100); + debug!(" [ok]"); + run_kernel_op(100); + } +} diff --git a/boards/opentitan/src/tests/flash_ctrl.rs b/boards/opentitan/src/tests/flash_ctrl.rs deleted file mode 100644 index 4c0876c4f2..0000000000 --- a/boards/opentitan/src/tests/flash_ctrl.rs +++ /dev/null @@ -1,418 +0,0 @@ -// Licensed under the Apache License, Version 2.0 or the MIT License. -// SPDX-License-Identifier: Apache-2.0 OR MIT -// Copyright Tock Contributors 2022. - -//! Test the opentitan Flash Controller -//! Tests: read_page, write_page, erase_page -use crate::tests::run_kernel_op; -use crate::PERIPHERALS; -use core::cell::Cell; -use kernel::debug; -#[allow(unused_imports)] -use kernel::errorcode::ErrorCode; -use kernel::hil; -#[allow(unused_imports)] -use kernel::hil::flash::Flash; -use kernel::hil::flash::HasClient; -use kernel::static_init; -use kernel::utilities::cells::TakeCell; -#[allow(unused_imports)] -use lowrisc::flash_ctrl::FlashMPConfig; -#[allow(unused_imports)] -use lowrisc::flash_ctrl::FLASH_ADDR_OFFSET; -#[allow(unused_imports)] -use lowrisc::flash_ctrl::PAGE_SIZE; -#[allow(dead_code)] -struct FlashCtlCallBack { - read_pending: Cell, - write_pending: Cell, - // A lowrisc page to for reads/writes - read_in_page: TakeCell<'static, lowrisc::flash_ctrl::LowRiscPage>, - write_in_page: TakeCell<'static, lowrisc::flash_ctrl::LowRiscPage>, - // We recover the callback returned buffer into these - read_out_buf: TakeCell<'static, [u8]>, - write_out_buf: TakeCell<'static, [u8]>, - // Flag if an MP fault was detected - mp_fault_detect: Cell, -} - -impl<'a> FlashCtlCallBack { - fn new( - read_in_page: &'static mut lowrisc::flash_ctrl::LowRiscPage, - write_in_page: &'static mut lowrisc::flash_ctrl::LowRiscPage, - ) -> Self { - FlashCtlCallBack { - read_pending: Cell::new(false), - write_pending: Cell::new(false), - read_in_page: TakeCell::new(read_in_page), - write_in_page: TakeCell::new(write_in_page), - read_out_buf: TakeCell::empty(), - write_out_buf: TakeCell::empty(), - mp_fault_detect: Cell::new(false), - } - } - - fn reset(&self) { - self.read_pending.set(false); - self.write_pending.set(false); - self.mp_fault_detect.set(false); - } -} - -impl<'a, F: hil::flash::Flash> hil::flash::Client for FlashCtlCallBack { - fn read_complete(&self, page: &'static mut F::Page, error: hil::flash::Error) { - if self.read_pending.get() { - if error == hil::flash::Error::FlashMemoryProtectionError { - self.mp_fault_detect.set(true); - } else { - assert_eq!(error, hil::flash::Error::CommandComplete); - } - self.read_out_buf.replace(page.as_mut()); - self.read_pending.set(false); - } - } - - fn write_complete(&self, page: &'static mut F::Page, error: hil::flash::Error) { - if self.write_pending.get() { - if error == hil::flash::Error::FlashMemoryProtectionError { - self.mp_fault_detect.set(true); - } else { - assert_eq!(error, hil::flash::Error::CommandComplete); - } - self.write_out_buf.replace(page.as_mut()); - self.write_pending.set(false); - } - } - - fn erase_complete(&self, error: hil::flash::Error) { - // Caller may check by a successive page read to assert the erased - // page is composed of 0xFF (all erased bits should be 1) - if error == hil::flash::Error::FlashMemoryProtectionError { - self.mp_fault_detect.set(true); - } else { - assert_eq!(error, hil::flash::Error::CommandComplete); - } - } -} - -macro_rules! static_init_test { - () => {{ - let r_in_page = static_init!( - lowrisc::flash_ctrl::LowRiscPage, - lowrisc::flash_ctrl::LowRiscPage::default() - ); - let w_in_page = static_init!( - lowrisc::flash_ctrl::LowRiscPage, - lowrisc::flash_ctrl::LowRiscPage::default() - ); - let mut val: u8 = 0; - - for i in 0..lowrisc::flash_ctrl::PAGE_SIZE { - val = val.wrapping_add(10); - r_in_page[i] = 0x00; - w_in_page[i] = 0xAA; // Arbitrary Data - } - static_init!( - FlashCtlCallBack, - FlashCtlCallBack::new(r_in_page, w_in_page) - ) - }}; -} - -/// The only 'test_case' for flash_ctrl as directly invoked by the test runner, -/// this calls all the other tests, preserving the order in which they must -/// be ran. -#[test_case] -fn flash_ctrl_tester() { - flash_ctrl_read_write_page(); - flash_ctrl_erase_page(); - flash_ctrl_mp_basic(); - flash_ctrl_mp_functionality(); -} - -// Note: the tests below need to run in a particular order, hence the a, b, c... -// function name prefix (test runner seems to invoke them alphabetically). - -/// Tests: Erase Page -> Write Page -> Read Page -/// -/// Compare the data we wrote is stored in flash with a -/// successive read. -fn flash_ctrl_read_write_page() { - let perf = unsafe { PERIPHERALS.unwrap() }; - let flash_ctl = &perf.flash_ctrl; - - let cb = unsafe { static_init_test!() }; - flash_ctl.set_client(cb); - cb.reset(); - - debug!("[FLASH_CTRL] Test page read/write...."); - - #[cfg(feature = "hardware_tests")] - { - let page_num: usize = 511; - run_kernel_op(100); - // Lets do a page erase - assert!(flash_ctl.erase_page(page_num).is_ok()); - run_kernel_op(100); - - // Do Page Write - let write_page = cb.write_in_page.take().unwrap(); - assert!(flash_ctl.write_page(page_num, write_page).is_ok()); - cb.write_pending.set(true); - run_kernel_op(100); - // OP Complete, buffer recovered. - assert!(!cb.write_pending.get()); - cb.reset(); - - // Read the same page - let read_page = cb.read_in_page.take().unwrap(); - assert!(flash_ctl.read_page(page_num, read_page).is_ok()); - cb.read_pending.set(true); - run_kernel_op(100); - assert!(!cb.read_pending.get()); - cb.reset(); - - // Compare r/w buffer - let write_in = cb.write_out_buf.take().unwrap(); // Recovered buffer is saved here as &[u8] - let read_out = cb.read_out_buf.take().unwrap(); - - assert_eq!(write_in.len(), read_out.len()); - assert!( - write_in.iter().zip(read_out.iter()).all(|(i, j)| i == j), - "[ERR] Read data indicates flash write error on page {}", - page_num - ); - - cb.write_out_buf.replace(write_in); - cb.read_out_buf.replace(read_out); - } - - run_kernel_op(100); - debug!(" [ok]"); - run_kernel_op(100); -} - -/// Tests: Erase Page -> Write Page -> Erase Page -> Read Page -/// A page erased should set all bits to `1`s or all bytes in page to -/// `0xFF`. Assert this is true after writing data to a page and erasing -/// the page. -fn flash_ctrl_erase_page() { - let perf = unsafe { PERIPHERALS.unwrap() }; - let flash_ctl = &perf.flash_ctrl; - - let cb = unsafe { static_init_test!() }; - cb.reset(); - flash_ctl.set_client(cb); - - debug!("[FLASH_CTRL] Test page erase...."); - - #[cfg(feature = "hardware_tests")] - { - let page_num: usize = 500; - run_kernel_op(100); - // Lets do a page erase - assert!(flash_ctl.erase_page(page_num).is_ok()); - run_kernel_op(100); - - // Do Page Write - let write_page = cb.write_in_page.take().unwrap(); - assert!(flash_ctl.write_page(page_num, write_page).is_ok()); - cb.write_pending.set(true); - run_kernel_op(100); - // OP Complete, buffer recovered. - assert!(!cb.write_pending.get()); - cb.reset(); - - // Erase again - assert!(flash_ctl.erase_page(page_num).is_ok()); - run_kernel_op(100); - - // Read Page - let read_page = cb.read_in_page.take().unwrap(); - assert!(flash_ctl.read_page(page_num, read_page).is_ok()); - cb.read_pending.set(true); - run_kernel_op(100); - assert!(!cb.read_pending.get()); - cb.reset(); - - // Check that the erased paged is all `0xFF` bytes - let read_out = cb.read_out_buf.take().unwrap(); - assert!( - read_out.iter().all(|&a| a == 0xFF), - "[ERR] Read data indicates erase failure on page {}", - page_num - ); - - cb.read_out_buf.replace(read_out); - } - run_kernel_op(100); - debug!(" [ok]"); - run_kernel_op(100); -} - -/// Tests: The basic api functionality and error handling of invalid arguments. -fn flash_ctrl_mp_basic() { - debug!("[FLASH_CTRL] Test memory protection api...."); - - #[cfg(feature = "hardware_tests")] - { - let perf = unsafe { PERIPHERALS.unwrap() }; - let flash_ctl = &perf.flash_ctrl; - // BANK1 - let base_page_addr: usize = (400 * PAGE_SIZE).saturating_add(FLASH_ADDR_OFFSET); - // Pages indexing starts with 0 and we have 512 pages. - let invalid_page_addr: usize = (512 * PAGE_SIZE).saturating_add(FLASH_ADDR_OFFSET); - let valid_num_pages: usize = 10; - // Note: Region 0 is occupied by board setup - let valid_region: usize = 6; - let invalid_region: usize = 8; - let num_regions = flash_ctl.mp_get_num_regions().unwrap(); - // WARN: Revisit these tests if cfgs have changed in HW - assert_eq!(num_regions, lowrisc::flash_ctrl::FLASH_MP_MAX_CFGS as u32); - - for region_num in 0..8 { - // All 8 regions should be unlocked at reset - assert!(flash_ctl.mp_is_region_locked(region_num).is_ok()) - } - - let cfg_set = FlashMPConfig { - read_en: false, - write_en: true, - erase_en: false, - scramble_en: false, - ecc_en: true, - he_en: false, - }; - // Expect Fail - assert_eq!( - flash_ctl.mp_set_region_perms( - base_page_addr, - invalid_page_addr, - valid_region, - &cfg_set - ), - Err(ErrorCode::NOSUPPORT) - ); - assert_eq!( - flash_ctl.mp_set_region_perms( - invalid_page_addr, - base_page_addr, - valid_region, - &cfg_set - ), - Err(ErrorCode::NOSUPPORT) - ); - assert_eq!( - flash_ctl.mp_set_region_perms( - base_page_addr, - base_page_addr.saturating_add(valid_num_pages * PAGE_SIZE), - invalid_region, - &cfg_set - ), - Err(ErrorCode::NOSUPPORT) - ); - // Set Perms - assert!(flash_ctl - .mp_set_region_perms( - base_page_addr, - base_page_addr.saturating_add(valid_num_pages * PAGE_SIZE), - valid_region, - &cfg_set - ) - .is_ok()); - // Check Perms - assert_eq!( - flash_ctl.mp_read_region_perms(valid_region).unwrap(), - cfg_set - ); - // Lock region - assert!(flash_ctl.mp_lock_region_cfg(valid_region).is_ok()); - assert!(flash_ctl.mp_is_region_locked(valid_region).unwrap()); - } - - run_kernel_op(100); - debug!(" [ok]"); - run_kernel_op(100); -} - -/// Tests the memory protection functionality of the flash_ctrl -/// Test: Setup memory protection -> Do bad OP/cause an MP Fault -> Expect fail/assert Err(FlashMPFault) -fn flash_ctrl_mp_functionality() { - debug!("[FLASH_CTRL] Test memory protection functionality...."); - - #[cfg(feature = "hardware_tests")] - { - let perf = unsafe { PERIPHERALS.unwrap() }; - let flash_ctl = &perf.flash_ctrl; - let cb = unsafe { static_init_test!() }; - cb.reset(); - flash_ctl.set_client(cb); - - // BANK1 - let page_num: usize = 450; - let base_page_addr: usize = (page_num * PAGE_SIZE).saturating_add(FLASH_ADDR_OFFSET); - let num_pages: usize = 25; - // Note: Region 0 is occupied by board setup - let region: usize = 7; - let invalid_region: usize = 142; - - for region_num in 0..8 { - // All 8 regions should be unlocked at reset - assert!(flash_ctl.mp_is_region_locked(region_num).is_ok()) - } - - let cfg_set = FlashMPConfig { - // NOTE: We disable read access, then later try to read to trigger the fault - read_en: false, - write_en: true, - // NOTE: We disable erase perms, then later try to erase and trigger the fault - erase_en: false, - scramble_en: true, - ecc_en: false, - he_en: true, - }; - // Set Perms - assert!(flash_ctl - .mp_set_region_perms( - base_page_addr, - base_page_addr.saturating_add(num_pages * PAGE_SIZE), - region, - &cfg_set - ) - .is_ok()); - // Check Perms - assert_eq!(flash_ctl.mp_read_region_perms(region).unwrap(), cfg_set); - // Lock Config - Expect Fail - assert_eq!( - flash_ctl.mp_lock_region_cfg(invalid_region), - Err(ErrorCode::NOSUPPORT) - ); - // Lock Config - assert!(flash_ctl.mp_lock_region_cfg(region).is_ok()); - assert!(flash_ctl.mp_is_region_locked(region).unwrap()); - - // Functionality Test 1: We disabled erase for this region, lets try to erase - assert!(flash_ctl.erase_page(page_num).is_ok()); - run_kernel_op(100); - // Ensure that a MP violation was detected - assert!(cb.mp_fault_detect.get()); - // Clear the fault - cb.reset(); - - // Functionality Test 2: We disabled read for this region, lets try to read - // This should trigger an MP fault - // Read Page - let read_page = cb.read_in_page.take().unwrap(); - assert!(flash_ctl.read_page(page_num, read_page).is_ok()); - cb.read_pending.set(true); - run_kernel_op(100); - assert!(!cb.read_pending.get()); - // Ensure that a MP violation was detected - assert!(cb.mp_fault_detect.get()); - cb.reset(); - } - - run_kernel_op(100); - debug!(" [ok]"); - run_kernel_op(100); -} diff --git a/boards/opentitan/src/tests/mod.rs b/boards/opentitan/src/tests/mod.rs index 119c975d0b..9e47cab984 100644 --- a/boards/opentitan/src/tests/mod.rs +++ b/boards/opentitan/src/tests/mod.rs @@ -55,6 +55,7 @@ fn trivial_assertion() { mod aes_test; mod csrng; +mod flash; mod hmac; mod multi_alarm; mod otbn; @@ -63,9 +64,3 @@ mod rsa_4096; mod sha256soft_test; // Test software SHA capsule mod sip_hash; mod spi_host; -mod tickv_test; -/// Only run the flash_ctrl tests last, as testing memory protection -/// may deny access to flash pages for other tests depending -/// on flash (i.e tickV). -#[rustfmt::skip] -mod flash_ctrl; diff --git a/boards/opentitan/src/tests/multi_alarm.rs b/boards/opentitan/src/tests/multi_alarm.rs index 9b7c3394f2..ff187a4dcc 100644 --- a/boards/opentitan/src/tests/multi_alarm.rs +++ b/boards/opentitan/src/tests/multi_alarm.rs @@ -19,7 +19,6 @@ use crate::tests::run_kernel_op; use crate::ALARM; use capsules_core::test::random_alarm::TestRandomAlarm; use capsules_core::virtualizers::virtual_alarm::{MuxAlarm, VirtualMuxAlarm}; -use earlgrey::chip_config::EarlGreyConfig; use earlgrey::timer::RvTimer; use kernel::debug; use kernel::hil::time::Alarm; diff --git a/boards/opentitan/src/tests/sip_hash.rs b/boards/opentitan/src/tests/sip_hash.rs index 75890765f8..f439081cd9 100644 --- a/boards/opentitan/src/tests/sip_hash.rs +++ b/boards/opentitan/src/tests/sip_hash.rs @@ -51,7 +51,7 @@ impl<'a> hasher::Client<8> for SipHashTestCallback { unimplemented!() } - fn add_mut_data_done(&self, result: Result<(), ErrorCode>, mut data: SubSliceMut<'static, u8>) { + fn add_mut_data_done(&self, result: Result<(), ErrorCode>, data: SubSliceMut<'static, u8>) { assert_eq!(result, Ok(())); self.data_add_done.set(true); diff --git a/boards/opentitan/src/tests/tickv_test.rs b/boards/opentitan/src/tests/tickv_test.rs deleted file mode 100644 index b8a7ecf30c..0000000000 --- a/boards/opentitan/src/tests/tickv_test.rs +++ /dev/null @@ -1,63 +0,0 @@ -// Licensed under the Apache License, Version 2.0 or the MIT License. -// SPDX-License-Identifier: Apache-2.0 OR MIT -// Copyright Tock Contributors 2022. - -//! Test TicKV - -use crate::tests::run_kernel_op; -use crate::{SIPHASH, TICKV}; -use capsules_core::virtualizers::virtual_flash::FlashUser; -use capsules_extra::test::kv_system::KVSystemTest; -use capsules_extra::tickv::KVSystem; -use capsules_extra::tickv::{TicKVKeyType, TicKVSystem}; -use kernel::debug; -use kernel::hil::hasher::Hasher; -use kernel::static_init; -use kernel::utilities::leasable_buffer::SubSliceMut; - -#[test_case] -fn tickv_append_key() { - debug!("start TicKV append key test..."); - - unsafe { - let tickv = TICKV.unwrap(); - let sip_hasher = SIPHASH.unwrap(); - - let key_input = static_init!( - [u8; 16], - [ - 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0, 0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, - 0xDE, 0xF0 - ] - ); - let key = static_init!([u8; 8], [0; 8]); - let value = static_init!([u8; 3], [0x10, 0x20, 0x30]); - let ret = static_init!([u8; 4], [0; 4]); - - let test = static_init!( - KVSystemTest< - 'static, - TicKVSystem< - 'static, - FlashUser<'static, lowrisc::flash_ctrl::FlashCtrl<'static>>, - capsules_extra::sip_hash::SipHasher24, - 2048, - >, - TicKVKeyType, - >, - KVSystemTest::new(tickv, SubSliceMut::new(value), ret) - ); - - sip_hasher.set_client(tickv); - tickv.set_client(test); - - // Kick start the tests by generating a key - tickv - .generate_key(SubSliceMut::new(key_input), key) - .unwrap(); - } - run_kernel_op(100000); - - debug!(" [ok]"); - run_kernel_op(100); -}