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 <span>
20 
21 #include "pw_assert/light.h"
22 #include "pw_kvs/alignment.h"
23 #include "pw_status/status.h"
24 #include "pw_status/status_with_size.h"
25 
26 namespace pw {
27 namespace kvs {
28 
29 enum class PartitionPermission : bool {
30   kReadOnly,
31   kReadAndWrite,
32 };
33 
34 class FlashMemory {
35  public:
36   // The flash address is in the range of: 0 to FlashSize.
37   typedef uint32_t Address;
38 
39   // TODO(pwbug/246): This can be constexpr when tokenized asserts are fixed.
40   FlashMemory(size_t sector_size,
41               size_t sector_count,
42               size_t alignment,
43               uint32_t start_address = 0,
44               uint32_t sector_start = 0,
45               std::byte erased_memory_content = std::byte(0xFF))
sector_size_(sector_size)46       : sector_size_(sector_size),
47         flash_sector_count_(sector_count),
48         alignment_(alignment),
49         start_address_(start_address),
50         start_sector_(sector_start),
51         erased_memory_content_(erased_memory_content) {
52     PW_ASSERT(alignment_ != 0u);
53   }
54 
55   virtual ~FlashMemory() = default;
56 
57   virtual Status Enable() = 0;
58 
59   virtual Status Disable() = 0;
60 
61   virtual bool IsEnabled() const = 0;
62 
SelfTest()63   virtual Status SelfTest() { return Status::Unimplemented(); }
64 
65   // Erase num_sectors starting at a given address. Blocking call.
66   // Address should be on a sector boundary. Returns:
67   //
68   // OK - success
69   // DEADLINE_EXCEEDED - timeout
70   // INVALID_ARGUMENT - address is not sector-aligned
71   // OUT_OF_RANGE - erases past the end of the memory
72   virtual Status Erase(Address flash_address, size_t num_sectors) = 0;
73 
74   // Reads bytes from flash into buffer. Blocking call. Returns:
75   //
76   // OK - success
77   // DEADLINE_EXCEEDED - timeout
78   // OUT_OF_RANGE - write does not fit in the flash memory
79   virtual StatusWithSize Read(Address address, std::span<std::byte> output) = 0;
80 
Read(Address address,void * buffer,size_t len)81   StatusWithSize Read(Address address, void* buffer, size_t len) {
82     return Read(address,
83                 std::span<std::byte>(static_cast<std::byte*>(buffer), len));
84   }
85 
86   // Writes bytes to flash. Blocking call. Returns:
87   //
88   // OK - success
89   // DEADLINE_EXCEEDED - timeout
90   // INVALID_ARGUMENT - address or data size are not aligned
91   // OUT_OF_RANGE - write does not fit in the memory
92   virtual StatusWithSize Write(Address destination_flash_address,
93                                std::span<const std::byte> data) = 0;
94 
Write(Address destination_flash_address,const void * data,size_t len)95   StatusWithSize Write(Address destination_flash_address,
96                        const void* data,
97                        size_t len) {
98     return Write(
99         destination_flash_address,
100         std::span<const std::byte>(static_cast<const std::byte*>(data), len));
101   }
102 
103   // Convert an Address to an MCU pointer, this can be used for memory
104   // mapped reads. Return NULL if the memory is not memory mapped.
FlashAddressToMcuAddress(Address)105   virtual std::byte* FlashAddressToMcuAddress(Address) const { return nullptr; }
106 
107   // start_sector() is useful for FlashMemory instances where the
108   // sector start is not 0. (ex.: cases where there are portions of flash
109   // that should be handled independently).
start_sector()110   constexpr uint32_t start_sector() const { return start_sector_; }
111 
sector_size_bytes()112   constexpr size_t sector_size_bytes() const { return sector_size_; }
113 
sector_count()114   constexpr size_t sector_count() const { return flash_sector_count_; }
115 
alignment_bytes()116   constexpr size_t alignment_bytes() const { return alignment_; }
117 
size_bytes()118   constexpr size_t size_bytes() const {
119     return sector_size_ * flash_sector_count_;
120   }
121 
122   // Address of the start of flash (the address of sector 0)
start_address()123   constexpr uint32_t start_address() const { return start_address_; }
124 
erased_memory_content()125   constexpr std::byte erased_memory_content() const {
126     return erased_memory_content_;
127   }
128 
129  private:
130   const uint32_t sector_size_;
131   const uint32_t flash_sector_count_;
132   const uint32_t alignment_;
133   const uint32_t start_address_;
134   const uint32_t start_sector_;
135   const std::byte erased_memory_content_;
136 };
137 
138 class FlashPartition {
139  public:
140   // The flash address is in the range of: 0 to PartitionSize.
141   using Address = uint32_t;
142 
143   // Implement Output for the Write method.
144   class Output final : public pw::Output {
145    public:
Output(FlashPartition & flash,FlashPartition::Address address)146     constexpr Output(FlashPartition& flash, FlashPartition::Address address)
147         : flash_(flash), address_(address) {}
148 
149    private:
150     StatusWithSize DoWrite(std::span<const std::byte> data) override;
151 
152     FlashPartition& flash_;
153     FlashPartition::Address address_;
154   };
155 
156   // Implement Input for the Read method.
157   class Input final : public pw::Input {
158    public:
Input(FlashPartition & flash,FlashPartition::Address address)159     constexpr Input(FlashPartition& flash, FlashPartition::Address address)
160         : flash_(flash), address_(address) {}
161 
162    private:
163     StatusWithSize DoRead(std::span<std::byte> data) override;
164 
165     FlashPartition& flash_;
166     FlashPartition::Address address_;
167   };
168 
169   FlashPartition(
170       FlashMemory* flash,
171       uint32_t start_sector_index,
172       uint32_t sector_count,
173       uint32_t alignment_bytes = 0,  // Defaults to flash alignment
174       PartitionPermission permission = PartitionPermission::kReadAndWrite);
175 
176   // Creates a FlashPartition that uses the entire flash with its alignment.
FlashPartition(FlashMemory * flash)177   FlashPartition(FlashMemory* flash)
178       : FlashPartition(
179             flash, 0, flash->sector_count(), flash->alignment_bytes()) {}
180 
181   FlashPartition(FlashPartition&&) = default;
182   FlashPartition(const FlashPartition&) = delete;
183   FlashPartition& operator=(const FlashPartition&) = delete;
184 
185   virtual ~FlashPartition() = default;
186 
187   // Performs any required partition or flash-level initialization.
Init()188   virtual Status Init() { return OkStatus(); }
189 
190   // Erase num_sectors starting at a given address. Blocking call.
191   // Address must be on a sector boundary. Returns:
192   //
193   // OK - success.
194   // TIMEOUT - on timeout.
195   // INVALID_ARGUMENT - address or sector count is invalid.
196   // PERMISSION_DENIED - partition is read only.
197   // UNKNOWN - HAL error
198   virtual Status Erase(Address address, size_t num_sectors);
199 
Erase()200   Status Erase() { return Erase(0, this->sector_count()); }
201 
202   // Reads bytes from flash into buffer. Blocking call. Returns:
203   //
204   // OK - success.
205   // TIMEOUT - on timeout.
206   // INVALID_ARGUMENT - address or length is invalid.
207   // UNKNOWN - HAL error
208   virtual StatusWithSize Read(Address address, std::span<std::byte> output);
209 
Read(Address address,size_t length,void * output)210   StatusWithSize Read(Address address, size_t length, void* output) {
211     return Read(address,
212                 std::span<std::byte>(static_cast<std::byte*>(output), length));
213   }
214 
215   // Writes bytes to flash. Address and data.size_bytes() must both be a
216   // multiple of alignment_bytes(). Blocking call. Returns:
217   //
218   // OK - success.
219   // TIMEOUT - on timeout.
220   // INVALID_ARGUMENT - address or length is invalid.
221   // PERMISSION_DENIED - partition is read only.
222   // UNKNOWN - HAL error
223   virtual StatusWithSize Write(Address address,
224                                std::span<const std::byte> data);
225 
226   // Check to see if chunk of flash partition is erased. Address and len need to
227   // be aligned with FlashMemory. Returns:
228   //
229   // OK - success.
230   // TIMEOUT - on timeout.
231   // INVALID_ARGUMENT - address or length is invalid.
232   // UNKNOWN - HAL error
233   // TODO: Result<bool>
234   virtual Status IsRegionErased(Address source_flash_address,
235                                 size_t length,
236                                 bool* is_erased);
237 
238   // Check if the entire partition is erased.
239   // Returns: same as IsRegionErased().
IsErased(bool * is_erased)240   Status IsErased(bool* is_erased) {
241     return IsRegionErased(0, this->size_bytes(), is_erased);
242   }
243 
244   // Checks to see if the data appears to be erased. No reads or writes occur;
245   // the FlashPartition simply compares the data to
246   // flash_.erased_memory_content().
247   bool AppearsErased(std::span<const std::byte> data) const;
248 
249   // Overridden by derived classes. The reported sector size is space available
250   // to users of FlashPartition. It accounts for space reserved in the sector
251   // for FlashPartition to store metadata.
sector_size_bytes()252   virtual size_t sector_size_bytes() const {
253     return flash_.sector_size_bytes();
254   }
255 
size_bytes()256   size_t size_bytes() const { return sector_count() * sector_size_bytes(); }
257 
258   // Alignment required for write address and write size.
alignment_bytes()259   size_t alignment_bytes() const { return alignment_bytes_; }
260 
sector_count()261   size_t sector_count() const { return sector_count_; }
262 
263   // Convert a FlashMemory::Address to an MCU pointer, this can be used for
264   // memory mapped reads. Return NULL if the memory is not memory mapped.
PartitionAddressToMcuAddress(Address address)265   std::byte* PartitionAddressToMcuAddress(Address address) const {
266     return flash_.FlashAddressToMcuAddress(PartitionToFlashAddress(address));
267   }
268 
269   // Converts an address from the partition address space to the flash address
270   // space. If the partition reserves additional space in the sector, the flash
271   // address space may not be contiguous, and this conversion accounts for that.
PartitionToFlashAddress(Address address)272   virtual FlashMemory::Address PartitionToFlashAddress(Address address) const {
273     return flash_.start_address() +
274            (start_sector_index_ - flash_.start_sector()) * sector_size_bytes() +
275            address;
276   }
277 
writable()278   bool writable() const {
279     return permission_ == PartitionPermission::kReadAndWrite;
280   }
281 
erased_memory_content()282   constexpr std::byte erased_memory_content() const {
283     return flash_.erased_memory_content();
284   }
285 
start_sector_index()286   uint32_t start_sector_index() const { return start_sector_index_; }
287 
288  protected:
289   Status CheckBounds(Address address, size_t len) const;
290 
flash()291   FlashMemory& flash() const { return flash_; }
292 
293  private:
294   FlashMemory& flash_;
295   const uint32_t start_sector_index_;
296   const uint32_t sector_count_;
297   const uint32_t alignment_bytes_;
298   const PartitionPermission permission_;
299 };
300 
301 }  // namespace kvs
302 }  // namespace pw
303