1 // Copyright 2020 The Pigweed Authors
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
7 // https://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
15 #define PW_LOG_MODULE_NAME "KVS"
16 #define PW_LOG_LEVEL PW_KVS_LOG_LEVEL
18 #include "pw_kvs/flash_memory.h"
24 #include "pw_assert/assert.h"
25 #include "pw_kvs_private/config.h"
26 #include "pw_log/log.h"
27 #include "pw_status/status_with_size.h"
28 #include "pw_status/try.h"
34 StatusWithSize FlashPartition::Output::DoWrite(std::span<const byte> data) {
35 PW_TRY_WITH_SIZE(flash_.Write(address_, data));
36 address_ += data.size();
37 return StatusWithSize(data.size());
40 StatusWithSize FlashPartition::Input::DoRead(std::span<byte> data) {
41 StatusWithSize result = flash_.Read(address_, data);
42 address_ += result.size();
46 FlashPartition::FlashPartition(
48 uint32_t start_sector_index,
49 uint32_t sector_count,
50 uint32_t alignment_bytes, // Defaults to flash alignment
51 PartitionPermission permission)
54 start_sector_index_(start_sector_index),
55 sector_count_(sector_count),
58 ? flash_.alignment_bytes()
59 : std::max(alignment_bytes, uint32_t(flash_.alignment_bytes()))),
60 permission_(permission) {
61 uint32_t misalignment = (alignment_bytes_ % flash_.alignment_bytes());
62 PW_DCHECK_UINT_EQ(misalignment,
64 "Flash partition alignmentmust be a multiple of the flash "
68 Status FlashPartition::Erase(Address address, size_t num_sectors) {
69 if (permission_ == PartitionPermission::kReadOnly) {
70 return Status::PermissionDenied();
73 PW_TRY(CheckBounds(address, num_sectors * sector_size_bytes()));
74 const size_t address_sector_offset = address % sector_size_bytes();
75 PW_CHECK_UINT_EQ(address_sector_offset, 0u);
77 return flash_.Erase(PartitionToFlashAddress(address), num_sectors);
80 StatusWithSize FlashPartition::Read(Address address, std::span<byte> output) {
81 PW_TRY_WITH_SIZE(CheckBounds(address, output.size()));
82 return flash_.Read(PartitionToFlashAddress(address), output);
85 StatusWithSize FlashPartition::Write(Address address,
86 std::span<const byte> data) {
87 if (permission_ == PartitionPermission::kReadOnly) {
88 return StatusWithSize::PermissionDenied();
90 PW_TRY_WITH_SIZE(CheckBounds(address, data.size()));
91 const size_t address_alignment_offset = address % alignment_bytes();
92 PW_CHECK_UINT_EQ(address_alignment_offset, 0u);
93 const size_t size_alignment_offset = data.size() % alignment_bytes();
94 PW_CHECK_UINT_EQ(size_alignment_offset, 0u);
95 return flash_.Write(PartitionToFlashAddress(address), data);
98 Status FlashPartition::IsRegionErased(Address source_flash_address,
101 // Relying on Read() to check address and len arguments.
102 if (is_erased == nullptr) {
103 return Status::InvalidArgument();
106 // TODO(pwbug/214): Currently using a single flash alignment to do both the
107 // read and write. The allowable flash read length may be less than what write
108 // needs (possibly by a bunch), resulting in buffer and erased_pattern_buffer
109 // being bigger than they need to be.
110 const size_t alignment = alignment_bytes();
111 if (alignment > kMaxFlashAlignment || kMaxFlashAlignment % alignment ||
112 length % alignment) {
113 return Status::InvalidArgument();
116 byte buffer[kMaxFlashAlignment];
117 const byte erased_byte = flash_.erased_memory_content();
120 while (length > 0u) {
121 // Check earlier that length is aligned, no need to round up
122 size_t read_size = std::min(sizeof(buffer), length);
123 PW_TRY(Read(source_flash_address + offset, read_size, buffer).status());
125 for (byte b : std::span(buffer, read_size)) {
126 if (b != erased_byte) {
127 // Detected memory chunk is not entirely erased
139 bool FlashPartition::AppearsErased(std::span<const byte> data) const {
140 byte erased_content = flash_.erased_memory_content();
141 for (byte b : data) {
142 if (b != erased_content) {
149 Status FlashPartition::CheckBounds(Address address, size_t length) const {
150 if (address + length > size_bytes()) {
152 "Attempted out-of-bound flash memory access (address: %u length: %u)",
155 return Status::OutOfRange();
160 } // namespace pw::kvs