1 // Copyright 2020 The Pigweed Authors
2 //
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
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
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
13 // the License.
14
15 #define PW_LOG_MODULE_NAME "PW_FLASH"
16 #define PW_LOG_LEVEL PW_KVS_LOG_LEVEL
17
18 #include "pw_kvs/flash_memory.h"
19
20 #include <algorithm>
21 #include <cinttypes>
22 #include <cstring>
23
24 #include "pw_assert/check.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"
29
30 namespace pw::kvs {
31
32 using std::byte;
33
34 #if PW_CXX_STANDARD_IS_SUPPORTED(17)
35
DoWrite(ConstByteSpan data)36 Status FlashPartition::Writer::DoWrite(ConstByteSpan data) {
37 if (partition_.size_bytes() <= position_) {
38 return Status::OutOfRange();
39 }
40 if (data.size_bytes() > (partition_.size_bytes() - position_)) {
41 return Status::ResourceExhausted();
42 }
43 if (data.size_bytes() == 0) {
44 return OkStatus();
45 }
46
47 const StatusWithSize sws = partition_.Write(position_, data);
48 if (sws.ok()) {
49 position_ += data.size_bytes();
50 }
51 return sws.status();
52 }
53
DoRead(ByteSpan data)54 StatusWithSize FlashPartition::Reader::DoRead(ByteSpan data) {
55 if (position_ >= read_limit_) {
56 return StatusWithSize::OutOfRange();
57 }
58
59 size_t bytes_to_read = std::min(data.size_bytes(), read_limit_ - position_);
60
61 const StatusWithSize sws =
62 partition_.Read(position_, data.first(bytes_to_read));
63 if (sws.ok()) {
64 position_ += bytes_to_read;
65 }
66 return sws;
67 }
68
69 #endif // PW_CXX_STANDARD_IS_SUPPORTED(17)
70
DoWrite(span<const byte> data)71 StatusWithSize FlashPartition::Output::DoWrite(span<const byte> data) {
72 PW_TRY_WITH_SIZE(flash_.Write(address_, data));
73 address_ += data.size();
74 return StatusWithSize(data.size());
75 }
76
DoRead(span<byte> data)77 StatusWithSize FlashPartition::Input::DoRead(span<byte> data) {
78 StatusWithSize result = flash_.Read(address_, data);
79 address_ += result.size();
80 return result;
81 }
82
FlashPartition(FlashMemory * flash,uint32_t flash_start_sector_index,uint32_t flash_sector_count,uint32_t alignment_bytes,PartitionPermission permission)83 FlashPartition::FlashPartition(
84 FlashMemory* flash,
85 uint32_t flash_start_sector_index,
86 uint32_t flash_sector_count,
87 uint32_t alignment_bytes, // Defaults to flash alignment
88 PartitionPermission permission)
89
90 : flash_(*flash),
91 flash_sector_count_(flash_sector_count),
92 flash_start_sector_index_(flash_start_sector_index),
93 alignment_bytes_(
94 alignment_bytes == 0
95 ? flash_.alignment_bytes()
96 : std::max(alignment_bytes, uint32_t(flash_.alignment_bytes()))),
97 permission_(permission) {
98 uint32_t misalignment = (alignment_bytes_ % flash_.alignment_bytes());
99 PW_DCHECK_UINT_EQ(misalignment,
100 0,
101 "Flash partition alignmentmust be a multiple of the flash "
102 "memory alignment");
103 }
104
Erase(Address address,size_t num_sectors)105 Status FlashPartition::Erase(Address address, size_t num_sectors) {
106 if (permission_ == PartitionPermission::kReadOnly) {
107 return Status::PermissionDenied();
108 }
109
110 PW_TRY(CheckBounds(address, num_sectors * sector_size_bytes()));
111 const size_t address_sector_offset = address % sector_size_bytes();
112 PW_CHECK_UINT_EQ(address_sector_offset, 0u);
113
114 return flash_.Erase(PartitionToFlashAddress(address), num_sectors);
115 }
116
Read(Address address,span<byte> output)117 StatusWithSize FlashPartition::Read(Address address, span<byte> output) {
118 PW_TRY_WITH_SIZE(CheckBounds(address, output.size()));
119 return flash_.Read(PartitionToFlashAddress(address), output);
120 }
121
Write(Address address,span<const byte> data)122 StatusWithSize FlashPartition::Write(Address address, span<const byte> data) {
123 if (permission_ == PartitionPermission::kReadOnly) {
124 return StatusWithSize::PermissionDenied();
125 }
126 PW_TRY_WITH_SIZE(CheckBounds(address, data.size()));
127 const size_t address_alignment_offset = address % alignment_bytes();
128 PW_CHECK_UINT_EQ(address_alignment_offset, 0u);
129 const size_t size_alignment_offset = data.size() % alignment_bytes();
130 PW_CHECK_UINT_EQ(size_alignment_offset, 0u);
131 return flash_.Write(PartitionToFlashAddress(address), data);
132 }
133
IsRegionErased(Address source_flash_address,size_t length,bool * is_erased)134 Status FlashPartition::IsRegionErased(Address source_flash_address,
135 size_t length,
136 bool* is_erased) {
137 // Relying on Read() to check address and len arguments.
138 if (is_erased == nullptr) {
139 return Status::InvalidArgument();
140 }
141
142 byte read_buffer[kMaxFlashAlignment];
143 const byte erased_byte = flash_.erased_memory_content();
144 size_t offset = 0;
145 *is_erased = false;
146 while (length > 0u) {
147 // Check earlier that length is aligned, no need to round up
148 size_t read_size = std::min(sizeof(read_buffer), length);
149 PW_TRY(
150 Read(source_flash_address + offset, read_size, read_buffer).status());
151
152 for (byte b : span(read_buffer, read_size)) {
153 if (b != erased_byte) {
154 // Detected memory chunk is not entirely erased
155 return OkStatus();
156 }
157 }
158
159 offset += read_size;
160 length -= read_size;
161 }
162 *is_erased = true;
163 return OkStatus();
164 }
165
EndOfWrittenData()166 StatusWithSize FlashPartition::EndOfWrittenData() {
167 size_t length = size_bytes();
168
169 byte read_buffer[kMaxFlashAlignment];
170 const byte erased_byte = flash_.erased_memory_content();
171
172 while (length > 0) {
173 // Check earlier that length is aligned, no need to round up
174 size_t read_size = std::min(sizeof(read_buffer), length);
175
176 length -= read_size;
177
178 PW_TRY_WITH_SIZE(Read(length, read_size, read_buffer));
179
180 for (size_t offset = read_size; offset > 0; offset--) {
181 if (read_buffer[offset - 1] != erased_byte) {
182 // Detected memory chunk is not entirely erased
183 return StatusWithSize(OkStatus(), length + offset);
184 }
185 }
186 }
187 return StatusWithSize(OkStatus(), 0);
188 }
189
AppearsErased(span<const byte> data) const190 bool FlashPartition::AppearsErased(span<const byte> data) const {
191 byte erased_content = flash_.erased_memory_content();
192 for (byte b : data) {
193 if (b != erased_content) {
194 return false;
195 }
196 }
197 return true;
198 }
199
CheckBounds(Address address,size_t length) const200 Status FlashPartition::CheckBounds(Address address, size_t length) const {
201 if (address + length > size_bytes()) {
202 PW_LOG_ERROR(
203 "FlashPartition - Attempted access (address: %u length: %u), exceeds "
204 "partition size %u bytes",
205 unsigned(address),
206 unsigned(length),
207 unsigned(size_bytes()));
208 return Status::OutOfRange();
209 }
210 return OkStatus();
211 }
212
213 } // namespace pw::kvs
214