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 "KVS"
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/assert.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 
DoWrite(std::span<const byte> data)34 StatusWithSize FlashPartition::Output::DoWrite(std::span<const byte> data) {
35   PW_TRY_WITH_SIZE(flash_.Write(address_, data));
36   address_ += data.size();
37   return StatusWithSize(data.size());
38 }
39 
DoRead(std::span<byte> data)40 StatusWithSize FlashPartition::Input::DoRead(std::span<byte> data) {
41   StatusWithSize result = flash_.Read(address_, data);
42   address_ += result.size();
43   return result;
44 }
45 
FlashPartition(FlashMemory * flash,uint32_t start_sector_index,uint32_t sector_count,uint32_t alignment_bytes,PartitionPermission permission)46 FlashPartition::FlashPartition(
47     FlashMemory* flash,
48     uint32_t start_sector_index,
49     uint32_t sector_count,
50     uint32_t alignment_bytes,  // Defaults to flash alignment
51     PartitionPermission permission)
52 
53     : flash_(*flash),
54       start_sector_index_(start_sector_index),
55       sector_count_(sector_count),
56       alignment_bytes_(
57           alignment_bytes == 0
58               ? flash_.alignment_bytes()
59               : std::max(alignment_bytes, uint32_t(flash_.alignment_bytes()))),
60       permission_(permission) {
61   uint32_t misalignment = (alignment_bytes_ % flash_.alignment_bytes());
62   PW_DCHECK_UINT_EQ(misalignment,
63                     0,
64                     "Flash partition alignmentmust be a multiple of the flash "
65                     "memory alignment");
66 }
67 
Erase(Address address,size_t num_sectors)68 Status FlashPartition::Erase(Address address, size_t num_sectors) {
69   if (permission_ == PartitionPermission::kReadOnly) {
70     return Status::PermissionDenied();
71   }
72 
73   PW_TRY(CheckBounds(address, num_sectors * sector_size_bytes()));
74   const size_t address_sector_offset = address % sector_size_bytes();
75   PW_CHECK_UINT_EQ(address_sector_offset, 0u);
76 
77   return flash_.Erase(PartitionToFlashAddress(address), num_sectors);
78 }
79 
Read(Address address,std::span<byte> output)80 StatusWithSize FlashPartition::Read(Address address, std::span<byte> output) {
81   PW_TRY_WITH_SIZE(CheckBounds(address, output.size()));
82   return flash_.Read(PartitionToFlashAddress(address), output);
83 }
84 
Write(Address address,std::span<const byte> data)85 StatusWithSize FlashPartition::Write(Address address,
86                                      std::span<const byte> data) {
87   if (permission_ == PartitionPermission::kReadOnly) {
88     return StatusWithSize::PermissionDenied();
89   }
90   PW_TRY_WITH_SIZE(CheckBounds(address, data.size()));
91   const size_t address_alignment_offset = address % alignment_bytes();
92   PW_CHECK_UINT_EQ(address_alignment_offset, 0u);
93   const size_t size_alignment_offset = data.size() % alignment_bytes();
94   PW_CHECK_UINT_EQ(size_alignment_offset, 0u);
95   return flash_.Write(PartitionToFlashAddress(address), data);
96 }
97 
IsRegionErased(Address source_flash_address,size_t length,bool * is_erased)98 Status FlashPartition::IsRegionErased(Address source_flash_address,
99                                       size_t length,
100                                       bool* is_erased) {
101   // Relying on Read() to check address and len arguments.
102   if (is_erased == nullptr) {
103     return Status::InvalidArgument();
104   }
105 
106   // TODO(pwbug/214): Currently using a single flash alignment to do both the
107   // read and write. The allowable flash read length may be less than what write
108   // needs (possibly by a bunch), resulting in read_buffer and
109   // erased_pattern_buffer being bigger than they need to be.
110   const size_t alignment = alignment_bytes();
111   if (alignment > kMaxFlashAlignment || kMaxFlashAlignment % alignment ||
112       length % alignment) {
113     return Status::InvalidArgument();
114   }
115 
116   byte read_buffer[kMaxFlashAlignment];
117   const byte erased_byte = flash_.erased_memory_content();
118   size_t offset = 0;
119   *is_erased = false;
120   while (length > 0u) {
121     // Check earlier that length is aligned, no need to round up
122     size_t read_size = std::min(sizeof(read_buffer), length);
123     PW_TRY(
124         Read(source_flash_address + offset, read_size, read_buffer).status());
125 
126     for (byte b : std::span(read_buffer, read_size)) {
127       if (b != erased_byte) {
128         // Detected memory chunk is not entirely erased
129         return OkStatus();
130       }
131     }
132 
133     offset += read_size;
134     length -= read_size;
135   }
136   *is_erased = true;
137   return OkStatus();
138 }
139 
AppearsErased(std::span<const byte> data) const140 bool FlashPartition::AppearsErased(std::span<const byte> data) const {
141   byte erased_content = flash_.erased_memory_content();
142   for (byte b : data) {
143     if (b != erased_content) {
144       return false;
145     }
146   }
147   return true;
148 }
149 
CheckBounds(Address address,size_t length) const150 Status FlashPartition::CheckBounds(Address address, size_t length) const {
151   if (address + length > size_bytes()) {
152     PW_LOG_ERROR(
153         "Attempted out-of-bound flash memory access (address: %u length: %u)",
154         unsigned(address),
155         unsigned(length));
156     return Status::OutOfRange();
157   }
158   return OkStatus();
159 }
160 
161 }  // namespace pw::kvs
162