POC reading and writing spectre sites

This commit is contained in:
Jakub Hlusička 2026-01-25 18:43:07 +01:00
parent a3a95b179b
commit 0cb6209d4b
5 changed files with 134 additions and 18 deletions

2
firmware/Cargo.lock generated
View file

@ -18,6 +18,7 @@ dependencies = [
"ekv", "ekv",
"embassy-embedded-hal", "embassy-embedded-hal",
"embassy-executor", "embassy-executor",
"embassy-sync 0.6.2",
"embassy-sync 0.7.2", "embassy-sync 0.7.2",
"embassy-time", "embassy-time",
"embedded-cli", "embedded-cli",
@ -1692,6 +1693,7 @@ dependencies = [
"futures-sink", "futures-sink",
"futures-util", "futures-util",
"heapless 0.8.0", "heapless 0.8.0",
"log",
] ]
[[package]] [[package]]

View file

@ -44,6 +44,7 @@ embassy-executor = { version = "0.9", features = ["log"] }
embassy-time = { version = "0.5.0", features = ["log"] } embassy-time = { version = "0.5.0", features = ["log"] }
embassy-embedded-hal = "0.5.0" embassy-embedded-hal = "0.5.0"
embassy-sync = { version = "0.7.2", features = ["log"] } embassy-sync = { version = "0.7.2", features = ["log"] }
embassy-sync-old = { package = "embassy-sync", version = "0.6.2", features = ["log"] }
esp-backtrace = { version = "0.18", default-features = false, features = [ esp-backtrace = { version = "0.18", default-features = false, features = [
"esp32s3", "esp32s3",
"println", "println",
@ -97,7 +98,8 @@ features = [
"crc", "crc",
"max-page-count-2048", "max-page-count-2048",
"max-key-size-256", "max-key-size-256",
"max-value-size-65536", # "max-value-size-65536",
"max-value-size-1024",
# These must adhere to `FlashStorage`'s parameters. # These must adhere to `FlashStorage`'s parameters.
"align-4", "align-4",
"page-size-4096", "page-size-4096",

View file

@ -4,13 +4,16 @@ use core::{
}; };
use alloc::{borrow::Cow, boxed::Box, vec::Vec}; use alloc::{borrow::Cow, boxed::Box, vec::Vec};
use ekv::{Database, flash::PageID}; use ekv::{
Database, ReadTransaction,
flash::{Flash, PageID},
};
use embassy_embedded_hal::{adapter::BlockingAsync, flash::partition::Partition}; use embassy_embedded_hal::{adapter::BlockingAsync, flash::partition::Partition};
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, RawMutex};
use embedded_storage_async::nor_flash::{NorFlash, ReadNorFlash}; use embedded_storage_async::nor_flash::{NorFlash, ReadNorFlash};
use esp_hal::rng::Trng; use esp_hal::rng::Trng;
use esp_storage::FlashStorage; use esp_storage::FlashStorage;
use log::info; use log::{debug, info};
pub type PartitionAcid = pub type PartitionAcid =
Partition<'static, CriticalSectionRawMutex, BlockingAsync<FlashStorage<'static>>>; Partition<'static, CriticalSectionRawMutex, BlockingAsync<FlashStorage<'static>>>;
@ -241,7 +244,7 @@ impl<'a> IntoIterator for DbPathSpectreUsers {
} }
pub struct DbPathSpectreUserSites<'a> { pub struct DbPathSpectreUserSites<'a> {
username: DbPathSegment<'a>, pub username: DbPathSegment<'a>,
} }
impl<'a> IntoIterator for DbPathSpectreUserSites<'a> { impl<'a> IntoIterator for DbPathSpectreUserSites<'a> {
@ -260,8 +263,8 @@ impl<'a> IntoIterator for DbPathSpectreUserSites<'a> {
} }
pub struct DbPathSpectreUserSite<'a> { pub struct DbPathSpectreUserSite<'a> {
user_sites: DbPathSpectreUserSites<'a>, pub user_sites: DbPathSpectreUserSites<'a>,
site: DbPathSegment<'a>, pub site: DbPathSegment<'a>,
} }
impl<'a> IntoIterator for DbPathSpectreUserSite<'a> { impl<'a> IntoIterator for DbPathSpectreUserSite<'a> {
@ -275,3 +278,42 @@ impl<'a> IntoIterator for DbPathSpectreUserSite<'a> {
.chain(core::iter::once(self.site)) .chain(core::iter::once(self.site))
} }
} }
pub trait ReadTransactionExt<F>
where
F: Flash,
{
async fn read_to_vec<'b>(
&self,
key: &[u8],
buffer: &'b mut Vec<u8>,
) -> Result<&'b mut [u8], ekv::ReadError<F::Error>>;
}
impl<'a, F, M> ReadTransactionExt<F> for ReadTransaction<'a, F, M>
where
F: Flash + 'a,
M: embassy_sync_old::blocking_mutex::raw::RawMutex + 'a,
{
async fn read_to_vec<'b>(
&self,
key: &[u8],
buffer: &'b mut Vec<u8>,
) -> Result<&'b mut [u8], ekv::ReadError<F::Error>> {
if buffer.len() == 0 {
buffer.resize(1, 0);
}
loop {
match self.read(key, buffer.as_mut_slice()).await {
Ok(size) => break Ok(&mut buffer[..size]),
Err(ekv::ReadError::BufferTooSmall) => {
let new_size = buffer.len() * 2;
debug!("Resizing read buffer to {new_size} bytes.");
buffer.resize(new_size, 0)
}
Err(error) => break Err(error),
}
}
}
}

View file

@ -42,6 +42,7 @@ use esp_hal::lcd_cam::LcdCam;
use esp_hal::lcd_cam::lcd::dpi::Dpi; use esp_hal::lcd_cam::lcd::dpi::Dpi;
use esp_hal::mcpwm::{McPwm, PeripheralClockConfig}; use esp_hal::mcpwm::{McPwm, PeripheralClockConfig};
use esp_hal::psram::{FlashFreq, PsramConfig, PsramSize, SpiRamFreq, SpiTimingConfigCoreClock}; use esp_hal::psram::{FlashFreq, PsramConfig, PsramSize, SpiRamFreq, SpiTimingConfigCoreClock};
use esp_hal::ram;
use esp_hal::rng::TrngSource; use esp_hal::rng::TrngSource;
use esp_hal::sha::{Sha, ShaBackend}; use esp_hal::sha::{Sha, ShaBackend};
use esp_hal::system::Stack; use esp_hal::system::Stack;
@ -151,7 +152,9 @@ async fn main(_spawner: Spawner) {
// TODO: Can we combine the regular link section with dram2? // TODO: Can we combine the regular link section with dram2?
// esp_alloc::heap_allocator!(size: 80 * 1024); // esp_alloc::heap_allocator!(size: 80 * 1024);
// esp_alloc::heap_allocator!(#[unsafe(link_section = ".dram2_uninit")] size: 72 * 1024); // esp_alloc::heap_allocator!(#[unsafe(link_section = ".dram2_uninit")] size: 72 * 1024);
esp_alloc::heap_allocator!(#[unsafe(link_section = ".dram2_uninit")] size: 64 * 1024); //esp_alloc::heap_allocator!(#[unsafe(link_section = ".dram2_uninit")] size: 64 * 1024);
esp_alloc::heap_allocator!(#[ram(reclaimed)] size: 72 * 1024);
esp_alloc::heap_allocator!(size: 16 * 1024);
info!("Heap initialized! {:#?}", esp_alloc::HEAP.stats()); info!("Heap initialized! {:#?}", esp_alloc::HEAP.stats());
// Initialize the PSRAM allocator. // Initialize the PSRAM allocator.
@ -164,10 +167,12 @@ async fn main(_spawner: Spawner) {
MemoryCapability::External.into(), MemoryCapability::External.into(),
)); ));
} }
info!(
"PSRAM allocator initialized with capacity of {} MiB!",
psram_size / 1024 / 1024
);
} }
info!("PSRAM allocator initialized!");
// let mut io = Io::new(peripherals.IO_MUX); // let mut io = Io::new(peripherals.IO_MUX);
// io.set_interrupt_handler(interrupt_handler); // io.set_interrupt_handler(interrupt_handler);
@ -376,7 +381,7 @@ async fn main(_spawner: Spawner) {
let window_size = [framebuffer.height, framebuffer.width]; let window_size = [framebuffer.height, framebuffer.width];
let framebuffer_ptr = FramebufferPtr(framebuffer.as_target_pixels() as _); let framebuffer_ptr = FramebufferPtr(framebuffer.as_target_pixels() as _);
static SECOND_CORE_STACK: StaticCell<Stack<{ 8192 * 2 }>> = StaticCell::new(); static SECOND_CORE_STACK: StaticCell<Stack<{ 16 * 1024 }>> = StaticCell::new();
let second_core_stack = SECOND_CORE_STACK.init(Stack::new()); let second_core_stack = SECOND_CORE_STACK.init(Stack::new());
esp_rtos::start_second_core( esp_rtos::start_second_core(
peripherals.CPU_CTRL, peripherals.CPU_CTRL,

View file

@ -34,7 +34,10 @@ use spectre_api_sys::{
use crate::FRAME_DURATION_MIN; use crate::FRAME_DURATION_MIN;
use crate::{ use crate::{
SIGNAL_LCD_SUBMIT, SIGNAL_UI_RENDER, SIGNAL_LCD_SUBMIT, SIGNAL_UI_RENDER,
db::{AcidDatabase, DbKey, DbPathSpectreUsers, PartitionAcid}, db::{
AcidDatabase, DbKey, DbPathSpectreUserSite, DbPathSpectreUserSites, DbPathSpectreUsers,
PartitionAcid, ReadTransactionExt,
},
ui::backend::SlintBackend, ui::backend::SlintBackend,
util::DurationExt, util::DurationExt,
}; };
@ -123,6 +126,21 @@ struct SpectreSiteConfig {
last_used: NaiveDateTime, last_used: NaiveDateTime,
} }
impl Default for SpectreSiteConfig {
fn default() -> Self {
Self {
algorithm: SpectreAlgorithm::Current,
counter: SpectreCounter::Default,
result_type: SpectreResultType::SpectreResultDefaultResult,
password: None,
login_type: SpectreResultType::SpectreResultDefaultLogin,
login_name: None,
uses: 0,
last_used: Default::default(),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
struct SpectreSite { struct SpectreSite {
username: String, username: String,
@ -142,6 +160,30 @@ pub async fn run_renderer_task(backend: SlintBackend, flash_part_acid: Partition
{ {
let mut write = db.write_transaction().await; let mut write = db.write_transaction().await;
write
.write(
&DbKey::new(DbPathSpectreUserSite {
user_sites: DbPathSpectreUserSites {
username: "test".into(),
},
site: "example.org".into(),
}),
&postcard::to_allocvec(&SpectreSiteConfig::default()).unwrap(),
)
.await
.unwrap();
write
.write(
&DbKey::new(DbPathSpectreUserSite {
user_sites: DbPathSpectreUserSites {
username: "test".into(),
},
site: "sub.example.org".into(),
}),
&postcard::to_allocvec(&SpectreSiteConfig::default()).unwrap(),
)
.await
.unwrap();
write write
.write( .write(
&DbKey::new(DbPathSpectreUsers), &DbKey::new(DbPathSpectreUsers),
@ -155,19 +197,42 @@ pub async fn run_renderer_task(backend: SlintBackend, flash_part_acid: Partition
let read_value = { let read_value = {
let read = db.read_transaction().await; let read = db.read_transaction().await;
// TODO: https://github.com/embassy-rs/ekv/issues/20 // TODO: https://github.com/embassy-rs/ekv/issues/20
let mut buffer = Vec::new(); let mut buffer = vec![0; 256];
let length = read let slice = read
.read(&DbKey::new(DbPathSpectreUsers), &mut buffer) .read_to_vec(&DbKey::new(DbPathSpectreUsers), &mut buffer)
.await .await
.unwrap(); .unwrap();
buffer.resize(length, 0); let read_value = postcard::from_bytes::<SpectreUsersConfig>(&slice).unwrap();
read.read(&DbKey::new(DbPathSpectreUsers), &mut buffer)
let key_sites = DbKey::new(DbPathSpectreUserSites {
username: "test".into(),
});
let mut key_buffer = [0_u8; ekv::config::MAX_KEY_SIZE];
let mut cursor = read
.read_range(key_sites.range_of_children())
.await .await
.unwrap(); .unwrap();
postcard::from_bytes::<SpectreUsersConfig>(&buffer).unwrap() while let Some((key_len, value_len)) =
cursor.next(&mut key_buffer, &mut buffer).await.unwrap()
{
let key = DbKey::from_raw(key_buffer[..key_len].into());
let mut key_segments = key.segments();
let value = &buffer[..value_len];
let site_config = postcard::from_bytes::<SpectreSiteConfig>(&value).unwrap();
let site = SpectreSite {
config: site_config,
username: key_segments.nth(2).unwrap().into(),
site_name: key_segments.nth(1).unwrap().into(),
};
info!("site = {:#?}", site);
}
read_value
}; };
info!("read_value = {:#?}", read_value);
assert_eq!(value, read_value, "values do not match"); assert_eq!(value, read_value, "values do not match");
// TODO: // TODO: