pw_kvs: Initial commit of new KVS design
Builds, but lots more work needed.
Fun fact: this code was written collaboratively in a Google Doc.
Change-Id: I8a89c5d0fdc71ec28cf432350e65d17e24a6f25c
diff --git a/pw_kvs/public/pw_kvs/flash_memory.h b/pw_kvs/public/pw_kvs/flash_memory.h
index b69eeb2..5495141 100644
--- a/pw_kvs/public/pw_kvs/flash_memory.h
+++ b/pw_kvs/public/pw_kvs/flash_memory.h
@@ -13,32 +13,50 @@
// the License.
#pragma once
-#include <algorithm>
-#include <cinttypes>
-#include <cstring>
+#include <cstddef>
+#include <cstdint>
+#include <initializer_list>
-#include "pw_kvs/assert.h"
-#include "pw_kvs/partition_table_entry.h"
-#include "pw_log/log.h"
+#include "pw_span/span.h"
#include "pw_status/status.h"
+#include "pw_status/status_with_size.h"
+
+namespace pw {
+
+// TODO: These are general-purpose utility functions that should be moved
+// elsewhere.
+constexpr size_t AlignDown(size_t value, size_t alignment) {
+ return (value / alignment) * alignment;
+}
+
+constexpr size_t AlignUp(size_t value, size_t alignment) {
+ return (value + alignment - 1) / alignment * alignment;
+}
+
+} // namespace pw
namespace pw::kvs {
+enum class PartitionPermission : bool {
+ kReadOnly,
+ kReadAndWrite,
+};
+
class FlashMemory {
public:
// The flash address is in the range of: 0 to FlashSize.
typedef uint32_t Address;
- constexpr FlashMemory(uint32_t sector_size,
- uint32_t sector_count,
- uint8_t alignment,
+ constexpr FlashMemory(size_t sector_size,
+ size_t sector_count,
+ size_t alignment,
uint32_t start_address = 0,
uint32_t sector_start = 0,
- uint8_t erased_memory_content = 0xFF)
+ std::byte erased_memory_content = std::byte{0xFF})
: sector_size_(sector_size),
flash_sector_count_(sector_count),
alignment_(alignment),
start_address_(start_address),
- sector_start_(sector_start),
+ start_sector_(sector_start),
erased_memory_content_(erased_memory_content) {}
virtual ~FlashMemory() = default;
@@ -54,43 +72,40 @@
// TIMEOUT, on timeout.
// INVALID_ARGUMENT, if address or sector count is invalid.
// UNKNOWN, on HAL error
- virtual Status Erase(Address flash_address, uint32_t num_sectors) = 0;
+ virtual Status Erase(Address flash_address, size_t num_sectors) = 0;
// Reads bytes from flash into buffer. Blocking call.
// Returns: OK, on success.
// TIMEOUT, on timeout.
// INVALID_ARGUMENT, if address or length is invalid.
// UNKNOWN, on HAL error
- virtual Status Read(uint8_t* destination_ram_address,
- Address source_flash_address,
- uint32_t len) = 0;
+ virtual StatusWithSize Read(Address address, span<std::byte> output) = 0;
// Writes bytes to flash. Blocking call.
// Returns: OK, on success.
// TIMEOUT, on timeout.
// INVALID_ARGUMENT, if address or length is invalid.
// UNKNOWN, on HAL error
- virtual Status Write(Address destination_flash_address,
- const uint8_t* source_ram_address,
- uint32_t len) = 0;
+ virtual StatusWithSize Write(Address destination_flash_address,
+ span<const std::byte> data) = 0;
// Convert an Address to an MCU pointer, this can be used for memory
// mapped reads. Return NULL if the memory is not memory mapped.
- virtual uint8_t* FlashAddressToMcuAddress(Address) const { return nullptr; }
+ virtual std::byte* FlashAddressToMcuAddress(Address) const { return nullptr; }
- // GetStartSector() is useful for FlashMemory instances where the
+ // start_sector() is useful for FlashMemory instances where the
// sector start is not 0. (ex.: cases where there are portions of flash
// that should be handled independently).
- constexpr uint32_t GetStartSector() const { return sector_start_; }
- constexpr uint32_t GetSectorSizeBytes() const { return sector_size_; }
- constexpr uint32_t GetSectorCount() const { return flash_sector_count_; }
- constexpr uint8_t GetAlignmentBytes() const { return alignment_; }
- constexpr uint32_t GetSizeBytes() const {
+ constexpr uint32_t start_sector() const { return start_sector_; }
+ constexpr size_t sector_size_bytes() const { return sector_size_; }
+ constexpr size_t sector_count() const { return flash_sector_count_; }
+ constexpr size_t alignment_bytes() const { return alignment_; }
+ constexpr size_t size_bytes() const {
return sector_size_ * flash_sector_count_;
}
// Address of the start of flash (the address of sector 0)
- constexpr uint32_t GetStartAddress() const { return start_address_; }
- constexpr uint8_t GetErasedMemoryContent() const {
+ constexpr uint32_t start_address() const { return start_address_; }
+ constexpr std::byte erased_memory_content() const {
return erased_memory_content_;
}
@@ -99,14 +114,14 @@
const uint32_t flash_sector_count_;
const uint8_t alignment_;
const uint32_t start_address_;
- const uint32_t sector_start_;
- const uint8_t erased_memory_content_;
+ const uint32_t start_sector_;
+ const std::byte erased_memory_content_;
};
class FlashPartition {
public:
// The flash address is in the range of: 0 to PartitionSize.
- typedef uint32_t Address;
+ using Address = uint32_t;
constexpr FlashPartition(
FlashMemory* flash,
@@ -118,12 +133,17 @@
sector_count_(sector_count),
permission_(permission) {}
- constexpr FlashPartition(FlashMemory* flash, PartitionTableEntry entry)
+#if 0
+ constexpr FlashPartition(
+ FlashMemory* flash,
+ uint32_t start_sector_index,
+ uint32_t end_sector_index,
+ PartitionPermission permission = PartitionPermission::kReadAndWrite)
: flash_(*flash),
- start_sector_index_(entry.partition_start_sector_index),
- sector_count_(entry.partition_end_sector_index -
- entry.partition_start_sector_index + 1),
- permission_(entry.partition_permission) {}
+ start_sector_index_(start_sector_index),
+ sector_count_(end_sector_index - start_sector_index + 1),
+ permission_(permission) {}
+#endif
virtual ~FlashPartition() = default;
@@ -134,32 +154,17 @@
// INVALID_ARGUMENT, if address or sector count is invalid.
// PERMISSION_DENIED, if partition is read only.
// UNKNOWN, on HAL error
- virtual Status Erase(Address address, uint32_t num_sectors) {
- if (permission_ == PartitionPermission::kReadOnly) {
- return Status::PERMISSION_DENIED;
- }
- if (Status status =
- CheckBounds(address, num_sectors * GetSectorSizeBytes());
- !status.ok()) {
- return status;
- }
- return flash_.Erase(PartitionToFlashAddress(address), num_sectors);
- }
+ virtual Status Erase(Address address, size_t num_sectors);
// Reads bytes from flash into buffer. Blocking call.
// Returns: OK, on success.
// TIMEOUT, on timeout.
// INVALID_ARGUMENT, if address or length is invalid.
// UNKNOWN, on HAL error
- virtual Status Read(uint8_t* destination_ram_address,
- Address source_flash_address,
- uint32_t len) {
- if (Status status = CheckBounds(source_flash_address, len); !status.ok()) {
- return status;
- }
- return flash_.Read(destination_ram_address,
- PartitionToFlashAddress(source_flash_address),
- len);
+ virtual StatusWithSize Read(Address address, span<std::byte> output);
+
+ StatusWithSize Read(Address address, size_t length, void* output) {
+ return Read(address, span(static_cast<std::byte*>(output), length));
}
// Writes bytes to flash. Blocking call.
@@ -168,20 +173,10 @@
// INVALID_ARGUMENT, if address or length is invalid.
// PERMISSION_DENIED, if partition is read only.
// UNKNOWN, on HAL error
- virtual Status Write(Address destination_flash_address,
- const uint8_t* source_ram_address,
- uint32_t len) {
- if (permission_ == PartitionPermission::kReadOnly) {
- return Status::PERMISSION_DENIED;
- }
- if (Status status = CheckBounds(destination_flash_address, len);
- !status.ok()) {
- return status;
- }
- return flash_.Write(PartitionToFlashAddress(destination_flash_address),
- source_ram_address,
- len);
- }
+ virtual StatusWithSize Write(Address address, span<const std::byte> data);
+
+ StatusWithSize Write(Address start_address,
+ std::initializer_list<span<const std::byte>> data);
// Check to see if chunk of flash memory is erased. Address and len need to
// be aligned with FlashMemory.
@@ -189,87 +184,40 @@
// TIMEOUT, on timeout.
// INVALID_ARGUMENT, if address or length is invalid.
// UNKNOWN, on HAL error
- virtual Status IsChunkErased(Address source_flash_address,
- uint32_t len,
- bool* is_erased) {
- // Max alignment is artifical to keep the stack usage low for this
- // function. Using 16 because it's the alignment of encrypted flash.
- const uint8_t kMaxAlignment = 16;
- // Relying on Read() to check address and len arguments.
- if (!is_erased) {
- return Status::INVALID_ARGUMENT;
- }
- uint8_t alignment = GetAlignmentBytes();
- if (alignment > kMaxAlignment || kMaxAlignment % alignment ||
- len % alignment) {
- return Status::INVALID_ARGUMENT;
- }
+ // TODO: StatusWithBool
+ virtual Status IsRegionErased(Address source_flash_address,
+ size_t len,
+ bool* is_erased);
- uint8_t buffer[kMaxAlignment];
- uint8_t erased_pattern_buffer[kMaxAlignment];
- size_t offset = 0;
- std::memset(erased_pattern_buffer,
- flash_.GetErasedMemoryContent(),
- sizeof(erased_pattern_buffer));
- *is_erased = false;
- while (len > 0) {
- // Check earlier that len is aligned, no need to round up
- uint16_t read_size = std::min(static_cast<uint32_t>(sizeof(buffer)), len);
- if (Status status =
- Read(buffer, source_flash_address + offset, read_size);
- !status.ok()) {
- return status;
- }
- if (std::memcmp(buffer, erased_pattern_buffer, read_size)) {
- // Detected memory chunk is not entirely erased
- return Status::OK;
- }
- offset += read_size;
- len -= read_size;
- }
- *is_erased = true;
- return Status::OK;
+ constexpr uint32_t sector_size_bytes() const {
+ return flash_.sector_size_bytes();
}
- constexpr uint32_t GetSectorSizeBytes() const {
- return flash_.GetSectorSizeBytes();
+ // Overridden by base classes which store metadata at the start of a sector.
+ virtual uint32_t sector_available_size_bytes() const {
+ return sector_size_bytes();
}
- uint32_t GetSizeBytes() const {
- return GetSectorCount() * GetSectorSizeBytes();
- }
+ size_t size_bytes() const { return sector_count() * sector_size_bytes(); }
- virtual uint8_t GetAlignmentBytes() const {
- return flash_.GetAlignmentBytes();
- }
+ virtual size_t alignment_bytes() const { return flash_.alignment_bytes(); }
- virtual uint32_t GetSectorCount() const { return sector_count_; }
+ virtual size_t sector_count() const { return sector_count_; }
// Convert a FlashMemory::Address to an MCU pointer, this can be used for
// memory mapped reads. Return NULL if the memory is not memory mapped.
- uint8_t* PartitionAddressToMcuAddress(Address address) const {
+ std::byte* PartitionAddressToMcuAddress(Address address) const {
return flash_.FlashAddressToMcuAddress(PartitionToFlashAddress(address));
}
FlashMemory::Address PartitionToFlashAddress(Address address) const {
- return flash_.GetStartAddress() +
- (start_sector_index_ - flash_.GetStartSector()) *
- GetSectorSizeBytes() +
+ return flash_.start_address() +
+ (start_sector_index_ - flash_.start_sector()) * sector_size_bytes() +
address;
}
protected:
- Status CheckBounds(Address address, size_t len) const {
- if (address + len > GetSizeBytes()) {
- PW_LOG_ERROR(
- "Attempted out-of-bound flash memory access (address: %" PRIu32
- " length: %zu)",
- address,
- len);
- return Status::INVALID_ARGUMENT;
- }
- return Status::OK;
- }
+ Status CheckBounds(Address address, size_t len) const;
private:
FlashMemory& flash_;
@@ -278,74 +226,4 @@
const PartitionPermission permission_;
};
-// FlashSubPartition defines a new partition which maps itself as a smaller
-// piece of another partition. This can used when a partition has special
-// behaviours (for example encrypted flash).
-// For example, this will be the first sector of test_partition:
-// FlashSubPartition test_partition_sector1(&test_partition, 0, 1);
-class FlashSubPartition : public FlashPartition {
- public:
- constexpr FlashSubPartition(FlashPartition* parent_partition,
- uint32_t start_sector_index,
- uint32_t sector_count)
- : FlashPartition(*parent_partition),
- partition_(parent_partition),
- start_sector_index_(start_sector_index),
- sector_count_(sector_count) {}
-
- Status Erase(Address address, uint32_t num_sectors) override {
- if (Status status =
- CheckBounds(address, num_sectors * GetSectorSizeBytes());
- !status.ok()) {
- return status;
- }
- return partition_->Erase(ParentAddress(address), num_sectors);
- }
-
- Status Read(uint8_t* destination_ram_address,
- Address source_flash_address,
- uint32_t len) override {
- if (Status status = CheckBounds(source_flash_address, len); !status.ok()) {
- return status;
- }
- return partition_->Read(
- destination_ram_address, ParentAddress(source_flash_address), len);
- }
-
- Status Write(Address destination_flash_address,
- const uint8_t* source_ram_address,
- uint32_t len) override {
- if (Status status = CheckBounds(destination_flash_address, len);
- !status.ok()) {
- return status;
- }
- return partition_->Write(
- ParentAddress(destination_flash_address), source_ram_address, len);
- }
-
- Status IsChunkErased(Address source_flash_address,
- uint32_t len,
- bool* is_erased) override {
- if (Status status = CheckBounds(source_flash_address, len); !status.ok()) {
- return status;
- }
- return partition_->IsChunkErased(
- ParentAddress(source_flash_address), len, is_erased);
- }
-
- uint8_t GetAlignmentBytes() const override {
- return partition_->GetAlignmentBytes();
- }
-
- uint32_t GetSectorCount() const override { return sector_count_; }
-
- private:
- Address ParentAddress(Address address) const {
- return address + start_sector_index_ * partition_->GetSectorSizeBytes();
- }
- FlashPartition* partition_;
- const uint32_t start_sector_index_;
- const uint32_t sector_count_;
-};
-
} // namespace pw::kvs