xref: /aosp_15_r20/external/pigweed/pw_kvs/public/pw_kvs/flash_memory.h (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 #pragma once
15 
16 #include <cstddef>
17 #include <cstdint>
18 #include <initializer_list>
19 #include <limits>
20 
21 #include "pw_assert/assert.h"
22 #include "pw_kvs/alignment.h"
23 #include "pw_polyfill/standard.h"
24 #include "pw_span/span.h"
25 #include "pw_status/status.h"
26 #include "pw_status/status_with_size.h"
27 #include "pw_stream/seek.h"
28 #include "pw_stream/stream.h"
29 
30 namespace pw {
31 namespace kvs {
32 
33 enum class PartitionPermission : bool {
34   kReadOnly,
35   kReadAndWrite,
36 };
37 
38 class FlashMemory {
39  public:
40   // The flash address is in the range of: 0 to FlashSize.
41   using Address = uint32_t;
42 
43   // TODO: b/235149326 - This can be constexpr when tokenized asserts are fixed.
44   FlashMemory(size_t sector_size,
45               size_t sector_count,
46               size_t alignment,
47               uint32_t start_address = 0,
48               uint32_t sector_start = 0,
49               std::byte erased_memory_content = std::byte(0xFF))
sector_size_(sector_size)50       : sector_size_(sector_size),
51         flash_sector_count_(sector_count),
52         alignment_(alignment),
53         start_address_(start_address),
54         start_sector_(sector_start),
55         erased_memory_content_(erased_memory_content) {
56     PW_ASSERT(alignment_ != 0u);
57   }
58 
59   virtual ~FlashMemory() = default;
60 
61   virtual Status Enable() = 0;
62 
63   virtual Status Disable() = 0;
64 
65   virtual bool IsEnabled() const = 0;
66 
SelfTest()67   virtual Status SelfTest() { return Status::Unimplemented(); }
68 
69   // Erase num_sectors starting at a given address. Blocking call.
70   // Address should be on a sector boundary. Returns:
71   //
72   // OK - success
73   // DEADLINE_EXCEEDED - timeout
74   // INVALID_ARGUMENT - address is not sector-aligned
75   // OUT_OF_RANGE - erases past the end of the memory
76   virtual Status Erase(Address flash_address, size_t num_sectors) = 0;
77 
78   // Reads bytes from flash into buffer. Blocking call. Returns:
79   //
80   // OK - success
81   // DEADLINE_EXCEEDED - timeout
82   // OUT_OF_RANGE - write does not fit in the flash memory
83   virtual StatusWithSize Read(Address address, span<std::byte> output) = 0;
84 
Read(Address address,void * buffer,size_t len)85   StatusWithSize Read(Address address, void* buffer, size_t len) {
86     return Read(address, span<std::byte>(static_cast<std::byte*>(buffer), len));
87   }
88 
89   // Writes bytes to flash. Blocking call. Returns:
90   //
91   // OK - success
92   // DEADLINE_EXCEEDED - timeout
93   // INVALID_ARGUMENT - address or data size are not aligned
94   // OUT_OF_RANGE - write does not fit in the memory
95   virtual StatusWithSize Write(Address destination_flash_address,
96                                span<const std::byte> data) = 0;
97 
Write(Address destination_flash_address,const void * data,size_t len)98   StatusWithSize Write(Address destination_flash_address,
99                        const void* data,
100                        size_t len) {
101     return Write(
102         destination_flash_address,
103         span<const std::byte>(static_cast<const std::byte*>(data), len));
104   }
105 
106   // Convert an Address to an MCU pointer, this can be used for memory
107   // mapped reads. Return NULL if the memory is not memory mapped.
FlashAddressToMcuAddress(Address)108   virtual std::byte* FlashAddressToMcuAddress(Address) const { return nullptr; }
109 
110   // start_sector() is useful for FlashMemory instances where the
111   // sector start is not 0. (ex.: cases where there are portions of flash
112   // that should be handled independently).
start_sector()113   constexpr uint32_t start_sector() const { return start_sector_; }
114 
sector_size_bytes()115   constexpr size_t sector_size_bytes() const { return sector_size_; }
116 
sector_count()117   constexpr size_t sector_count() const { return flash_sector_count_; }
118 
alignment_bytes()119   constexpr size_t alignment_bytes() const { return alignment_; }
120 
size_bytes()121   constexpr size_t size_bytes() const {
122     return sector_size_ * flash_sector_count_;
123   }
124 
125   // Address of the start of flash (the address of sector 0)
start_address()126   constexpr uint32_t start_address() const { return start_address_; }
127 
erased_memory_content()128   constexpr std::byte erased_memory_content() const {
129     return erased_memory_content_;
130   }
131 
132  private:
133   const uint32_t sector_size_;
134   const uint32_t flash_sector_count_;
135   const uint32_t alignment_;
136   const uint32_t start_address_;
137   const uint32_t start_sector_;
138   const std::byte erased_memory_content_;
139 };
140 
141 class FlashPartition {
142  public:
143   // The flash address is in the range of: 0 to PartitionSize.
144   using Address = uint32_t;
145 
146   class Writer final : public stream::NonSeekableWriter {
147    public:
Writer(kvs::FlashPartition & partition)148     constexpr Writer(kvs::FlashPartition& partition)
149         : partition_(partition), position_(0) {}
150 
151    private:
152     Status DoWrite(ConstByteSpan data) override;
153 
DoTell()154     size_t DoTell() override { return position_; }
155 
ConservativeLimit(LimitType type)156     size_t ConservativeLimit(LimitType type) const override {
157       return type == LimitType::kWrite ? partition_.size_bytes() - position_
158                                        : 0;
159     }
160 
161     FlashPartition& partition_;
162     size_t position_;
163   };
164 
165   class Reader final : public stream::SeekableReader {
166    public:
167     /// @brief Stream seekable reader for FlashPartitions.
168     ///
169     /// @param partition The partiion to read.
170     /// @param read_limit_bytes Optional limit to read less than the full
171     /// FlashPartition. Reader will use the lesser of read_limit_bytes and
172     /// partition size. Situations needing a subset that starts somewhere other
173     /// than 0 can seek to the desired start point.
174     Reader(kvs::FlashPartition& partition,
175            size_t read_limit_bytes = std::numeric_limits<size_t>::max())
partition_(partition)176         : partition_(partition),
177           read_limit_(std::min(read_limit_bytes, partition_.size_bytes())),
178           position_(0) {}
179 
180     Reader(const Reader&) = delete;
181     Reader& operator=(const Reader&) = delete;
182 
SetReadLimit(size_t read_limit_bytes)183     void SetReadLimit(size_t read_limit_bytes) {
184       read_limit_ = std::min(read_limit_bytes, partition_.size_bytes());
185       position_ = std::min(position_, read_limit_);
186     }
187 
188    private:
189     StatusWithSize DoRead(ByteSpan data) override;
190 
DoTell()191     size_t DoTell() override { return position_; }
192 
DoSeek(ptrdiff_t offset,Whence origin)193     Status DoSeek(ptrdiff_t offset, Whence origin) override {
194       return CalculateSeek(offset, origin, read_limit_, position_);
195     }
196 
ConservativeLimit(LimitType type)197     size_t ConservativeLimit(LimitType type) const override {
198       return type == LimitType::kRead ? read_limit_ - position_ : 0;
199     }
200 
201     FlashPartition& partition_;
202     size_t read_limit_;
203     size_t position_;
204   };
205 
206   // Implement Output for the Write method.
207   class Output final : public pw::Output {
208    public:
Output(FlashPartition & flash,FlashPartition::Address address)209     constexpr Output(FlashPartition& flash, FlashPartition::Address address)
210         : flash_(flash), address_(address) {}
211 
212    private:
213     StatusWithSize DoWrite(span<const std::byte> data) override;
214 
215     FlashPartition& flash_;
216     FlashPartition::Address address_;
217   };
218 
219   // Implement Input for the Read method.
220   class Input final : public pw::Input {
221    public:
Input(FlashPartition & flash,FlashPartition::Address address)222     constexpr Input(FlashPartition& flash, FlashPartition::Address address)
223         : flash_(flash), address_(address) {}
224 
225    private:
226     StatusWithSize DoRead(span<std::byte> data) override;
227 
228     FlashPartition& flash_;
229     FlashPartition::Address address_;
230   };
231 
232   FlashPartition(
233       FlashMemory* flash,
234       uint32_t flash_start_sector_index,
235       uint32_t flash_sector_count,
236       uint32_t alignment_bytes = 0,  // Defaults to flash alignment
237       PartitionPermission permission = PartitionPermission::kReadAndWrite);
238 
239   // Creates a FlashPartition that uses the entire flash with its alignment.
FlashPartition(FlashMemory * flash)240   FlashPartition(FlashMemory* flash)
241       : FlashPartition(
242             flash, 0, flash->sector_count(), flash->alignment_bytes()) {}
243 
244   FlashPartition(FlashPartition&&) = default;
245   FlashPartition(const FlashPartition&) = delete;
246   FlashPartition& operator=(const FlashPartition&) = delete;
247 
248   virtual ~FlashPartition() = default;
249 
250   // Performs any required partition or flash-level initialization.
Init()251   virtual Status Init() { return OkStatus(); }
252 
253   // Erase num_sectors starting at a given address. Blocking call.
254   // Address must be on a sector boundary. Returns:
255   //
256   // OK - success.
257   // TIMEOUT - on timeout.
258   // INVALID_ARGUMENT - address or sector count is invalid.
259   // PERMISSION_DENIED - partition is read only.
260   // UNKNOWN - HAL error
261   virtual Status Erase(Address address, size_t num_sectors);
262 
Erase()263   Status Erase() { return Erase(0, this->sector_count()); }
264 
265   // Reads bytes from flash into buffer. Blocking call. Returns:
266   //
267   // OK - success.
268   // TIMEOUT - on timeout.
269   // INVALID_ARGUMENT - address or length is invalid.
270   // UNKNOWN - HAL error
271   virtual StatusWithSize Read(Address address, span<std::byte> output);
272 
Read(Address address,size_t length,void * output)273   StatusWithSize Read(Address address, size_t length, void* output) {
274     return Read(address,
275                 span<std::byte>(static_cast<std::byte*>(output), length));
276   }
277 
278   // Writes bytes to flash. Address and data.size_bytes() must both be a
279   // multiple of alignment_bytes(). Blocking call. Returns:
280   //
281   // OK - success.
282   // TIMEOUT - on timeout.
283   // INVALID_ARGUMENT - address or length is invalid.
284   // PERMISSION_DENIED - partition is read only.
285   // UNKNOWN - HAL error
286   virtual StatusWithSize Write(Address address, span<const std::byte> data);
287 
288   // Check to see if chunk of flash partition is erased. Address and len need to
289   // be aligned with FlashMemory. Returns:
290   //
291   // OK - success.
292   // TIMEOUT - on timeout.
293   // INVALID_ARGUMENT - address or length is invalid.
294   // UNKNOWN - HAL error
295   // TODO(hepler): Result<bool>
296   virtual Status IsRegionErased(Address source_flash_address,
297                                 size_t length,
298                                 bool* is_erased);
299 
300   // Check if the entire partition is erased.
301   // Returns: same as IsRegionErased().
IsErased(bool * is_erased)302   Status IsErased(bool* is_erased) {
303     return IsRegionErased(0, this->size_bytes(), is_erased);
304   }
305 
306   // Returns the address of the first byte of erased flash that has no more
307   // written bytes to the end of the partition. If no erased bytes at end of
308   // partition, then size_bytes of partition is returned.
309   //
310   // OK - success with number of bytes to end of written data.
311   // Other - error code from flash read operation.
312   StatusWithSize EndOfWrittenData();
313 
314   // Checks to see if the data appears to be erased. No reads or writes occur;
315   // the FlashPartition simply compares the data to
316   // flash_.erased_memory_content().
317   bool AppearsErased(span<const std::byte> data) const;
318 
319   // Optionally overridden by derived classes. The reported sector size is space
320   // available to users of FlashPartition. This reported size can be smaller or
321   // larger than the sector size of the backing FlashMemory.
322   //
323   // Possible reasons for size to be different from the backing FlashMemory
324   // could be due to space reserved in the sector for FlashPartition to store
325   // metadata or due to logical FlashPartition sectors that combine several
326   // FlashMemory sectors.
sector_size_bytes()327   virtual size_t sector_size_bytes() const {
328     return flash_.sector_size_bytes();
329   }
330 
331   // Optionally overridden by derived classes. The reported sector count is
332   // sectors available to users of FlashPartition. This reported count can be
333   // same or smaller than the given flash_sector_count of the backing
334   // FlashMemory for the same region of flash.
335   //
336   // Possible reasons for count to be different from the backing FlashMemory
337   // could be due to space reserved in the FlashPartition to store metadata or
338   // due to logical FlashPartition sectors that combine several FlashMemory
339   // sectors.
sector_count()340   virtual size_t sector_count() const { return flash_sector_count_; }
341 
size_bytes()342   size_t size_bytes() const { return sector_count() * sector_size_bytes(); }
343 
344   // Alignment required for write address and write size.
alignment_bytes()345   size_t alignment_bytes() const { return alignment_bytes_; }
346 
347   // Convert a FlashMemory::Address to an MCU pointer, this can be used for
348   // memory mapped reads. Return NULL if the memory is not memory mapped.
PartitionAddressToMcuAddress(Address address)349   std::byte* PartitionAddressToMcuAddress(Address address) const {
350     return flash_.FlashAddressToMcuAddress(PartitionToFlashAddress(address));
351   }
352 
353   // Converts an address from the partition address space to the flash address
354   // space. If the partition reserves additional space in the sector, the flash
355   // address space may not be contiguous, and this conversion accounts for that.
PartitionToFlashAddress(Address address)356   virtual FlashMemory::Address PartitionToFlashAddress(Address address) const {
357     return flash_.start_address() +
358            (flash_start_sector_index_ - flash_.start_sector()) *
359                flash_.sector_size_bytes() +
360            address;
361   }
362 
writable()363   bool writable() const {
364     return permission_ == PartitionPermission::kReadAndWrite;
365   }
366 
erased_memory_content()367   constexpr std::byte erased_memory_content() const {
368     return flash_.erased_memory_content();
369   }
370 
start_sector_index()371   uint32_t start_sector_index() const { return flash_start_sector_index_; }
372 
373  protected:
374   Status CheckBounds(Address address, size_t len) const;
375 
flash()376   FlashMemory& flash() const { return flash_; }
377 
378   FlashMemory& flash_;
379   const uint32_t flash_sector_count_;
380 
381  private:
382   const uint32_t flash_start_sector_index_;
383   const uint32_t alignment_bytes_;
384   const PartitionPermission permission_;
385 };
386 
387 }  // namespace kvs
388 }  // namespace pw
389