• 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 
15 #define PW_LOG_MODULE_NAME "PW_FLASH"
16 #define PW_LOG_LEVEL PW_KVS_LOG_LEVEL
17 
18 #include "pw_kvs/flash_memory.h"
19 
20 #include <algorithm>
21 #include <cinttypes>
22 #include <cstring>
23 
24 #include "pw_assert/check.h"
25 #include "pw_kvs_private/config.h"
26 #include "pw_log/log.h"
27 #include "pw_status/status_with_size.h"
28 #include "pw_status/try.h"
29 
30 namespace pw::kvs {
31 
32 using std::byte;
33 
34 #if PW_CXX_STANDARD_IS_SUPPORTED(17)
35 
DoWrite(ConstByteSpan data)36 Status FlashPartition::Writer::DoWrite(ConstByteSpan data) {
37   if (partition_.size_bytes() <= position_) {
38     return Status::OutOfRange();
39   }
40   if (data.size_bytes() > (partition_.size_bytes() - position_)) {
41     return Status::ResourceExhausted();
42   }
43   if (data.size_bytes() == 0) {
44     return OkStatus();
45   }
46 
47   const StatusWithSize sws = partition_.Write(position_, data);
48   if (sws.ok()) {
49     position_ += data.size_bytes();
50   }
51   return sws.status();
52 }
53 
DoRead(ByteSpan data)54 StatusWithSize FlashPartition::Reader::DoRead(ByteSpan data) {
55   if (position_ >= partition_.size_bytes()) {
56     return StatusWithSize::OutOfRange();
57   }
58 
59   size_t bytes_to_read =
60       std::min(data.size_bytes(), partition_.size_bytes() - position_);
61 
62   const StatusWithSize sws =
63       partition_.Read(position_, data.first(bytes_to_read));
64   if (sws.ok()) {
65     position_ += bytes_to_read;
66   }
67   return sws;
68 }
69 
70 #endif  // PW_CXX_STANDARD_IS_SUPPORTED(17)
71 
DoWrite(span<const byte> data)72 StatusWithSize FlashPartition::Output::DoWrite(span<const byte> data) {
73   PW_TRY_WITH_SIZE(flash_.Write(address_, data));
74   address_ += data.size();
75   return StatusWithSize(data.size());
76 }
77 
DoRead(span<byte> data)78 StatusWithSize FlashPartition::Input::DoRead(span<byte> data) {
79   StatusWithSize result = flash_.Read(address_, data);
80   address_ += result.size();
81   return result;
82 }
83 
FlashPartition(FlashMemory * flash,uint32_t flash_start_sector_index,uint32_t flash_sector_count,uint32_t alignment_bytes,PartitionPermission permission)84 FlashPartition::FlashPartition(
85     FlashMemory* flash,
86     uint32_t flash_start_sector_index,
87     uint32_t flash_sector_count,
88     uint32_t alignment_bytes,  // Defaults to flash alignment
89     PartitionPermission permission)
90 
91     : flash_(*flash),
92       flash_sector_count_(flash_sector_count),
93       flash_start_sector_index_(flash_start_sector_index),
94       alignment_bytes_(
95           alignment_bytes == 0
96               ? flash_.alignment_bytes()
97               : std::max(alignment_bytes, uint32_t(flash_.alignment_bytes()))),
98       permission_(permission) {
99   uint32_t misalignment = (alignment_bytes_ % flash_.alignment_bytes());
100   PW_DCHECK_UINT_EQ(misalignment,
101                     0,
102                     "Flash partition alignmentmust be a multiple of the flash "
103                     "memory alignment");
104 }
105 
Erase(Address address,size_t num_sectors)106 Status FlashPartition::Erase(Address address, size_t num_sectors) {
107   if (permission_ == PartitionPermission::kReadOnly) {
108     return Status::PermissionDenied();
109   }
110 
111   PW_TRY(CheckBounds(address, num_sectors * sector_size_bytes()));
112   const size_t address_sector_offset = address % sector_size_bytes();
113   PW_CHECK_UINT_EQ(address_sector_offset, 0u);
114 
115   return flash_.Erase(PartitionToFlashAddress(address), num_sectors);
116 }
117 
Read(Address address,span<byte> output)118 StatusWithSize FlashPartition::Read(Address address, span<byte> output) {
119   PW_TRY_WITH_SIZE(CheckBounds(address, output.size()));
120   return flash_.Read(PartitionToFlashAddress(address), output);
121 }
122 
Write(Address address,span<const byte> data)123 StatusWithSize FlashPartition::Write(Address address, span<const byte> data) {
124   if (permission_ == PartitionPermission::kReadOnly) {
125     return StatusWithSize::PermissionDenied();
126   }
127   PW_TRY_WITH_SIZE(CheckBounds(address, data.size()));
128   const size_t address_alignment_offset = address % alignment_bytes();
129   PW_CHECK_UINT_EQ(address_alignment_offset, 0u);
130   const size_t size_alignment_offset = data.size() % alignment_bytes();
131   PW_CHECK_UINT_EQ(size_alignment_offset, 0u);
132   return flash_.Write(PartitionToFlashAddress(address), data);
133 }
134 
IsRegionErased(Address source_flash_address,size_t length,bool * is_erased)135 Status FlashPartition::IsRegionErased(Address source_flash_address,
136                                       size_t length,
137                                       bool* is_erased) {
138   // Relying on Read() to check address and len arguments.
139   if (is_erased == nullptr) {
140     return Status::InvalidArgument();
141   }
142 
143   // TODO(pwbug/214): Currently using a single flash alignment to do both the
144   // read and write. The allowable flash read length may be less than what write
145   // needs (possibly by a bunch), resulting in read_buffer and
146   // erased_pattern_buffer being bigger than they need to be.
147   const size_t alignment = alignment_bytes();
148   if (alignment > kMaxFlashAlignment || kMaxFlashAlignment % alignment ||
149       length % alignment) {
150     return Status::InvalidArgument();
151   }
152 
153   byte read_buffer[kMaxFlashAlignment];
154   const byte erased_byte = flash_.erased_memory_content();
155   size_t offset = 0;
156   *is_erased = false;
157   while (length > 0u) {
158     // Check earlier that length is aligned, no need to round up
159     size_t read_size = std::min(sizeof(read_buffer), length);
160     PW_TRY(
161         Read(source_flash_address + offset, read_size, read_buffer).status());
162 
163     for (byte b : span(read_buffer, read_size)) {
164       if (b != erased_byte) {
165         // Detected memory chunk is not entirely erased
166         return OkStatus();
167       }
168     }
169 
170     offset += read_size;
171     length -= read_size;
172   }
173   *is_erased = true;
174   return OkStatus();
175 }
176 
AppearsErased(span<const byte> data) const177 bool FlashPartition::AppearsErased(span<const byte> data) const {
178   byte erased_content = flash_.erased_memory_content();
179   for (byte b : data) {
180     if (b != erased_content) {
181       return false;
182     }
183   }
184   return true;
185 }
186 
CheckBounds(Address address,size_t length) const187 Status FlashPartition::CheckBounds(Address address, size_t length) const {
188   if (address + length > size_bytes()) {
189     PW_LOG_ERROR(
190         "FlashPartition - Attempted access (address: %u length: %u), exceeds "
191         "partition size %u bytes",
192         unsigned(address),
193         unsigned(length),
194         unsigned(size_bytes()));
195     return Status::OutOfRange();
196   }
197   return OkStatus();
198 }
199 
200 }  // namespace pw::kvs
201