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