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