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