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