blob: 217ad16eff2bab23821b3eb127b9c7cee6679c9b [file] [log] [blame]
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -08001// 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
David Rogerseb05d882021-10-12 15:24:48 -070015#define PW_LOG_MODULE_NAME "PW_FLASH"
Wyatt Heplerae222dc2020-10-14 10:46:27 -070016#define PW_LOG_LEVEL PW_KVS_LOG_LEVEL
Armando Montanez28ecccb2020-05-04 15:35:54 -070017
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080018#include "pw_kvs/flash_memory.h"
19
20#include <algorithm>
21#include <cinttypes>
22#include <cstring>
23
Wyatt Heplerf298de42021-03-19 15:06:36 -070024#include "pw_assert/check.h"
David Rogersca592962020-07-01 09:21:54 -070025#include "pw_kvs_private/config.h"
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080026#include "pw_log/log.h"
Wyatt Hepler1927c282020-02-11 16:45:02 -080027#include "pw_status/status_with_size.h"
David Rogersb6b14b82020-09-11 16:27:27 -070028#include "pw_status/try.h"
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080029
30namespace pw::kvs {
31
32using std::byte;
33
Wyatt Hepler79525f52022-03-02 11:16:28 -080034#if PW_CXX_STANDARD_IS_SUPPORTED(17)
35
David Rogersb9acbb02022-02-11 14:19:55 -080036Status 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
54StatusWithSize 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
Wyatt Hepler79525f52022-03-02 11:16:28 -080070#endif // PW_CXX_STANDARD_IS_SUPPORTED(17)
71
Wyatt Heplere2cbadf2020-06-22 11:21:45 -070072StatusWithSize FlashPartition::Output::DoWrite(std::span<const byte> data) {
David Rogersc4dc8642020-09-14 10:52:36 -070073 PW_TRY_WITH_SIZE(flash_.Write(address_, data));
Wyatt Hepler1927c282020-02-11 16:45:02 -080074 address_ += data.size();
75 return StatusWithSize(data.size());
76}
77
Wyatt Heplere2cbadf2020-06-22 11:21:45 -070078StatusWithSize FlashPartition::Input::DoRead(std::span<byte> data) {
Wyatt Hepleree6fd762020-03-09 08:32:19 -070079 StatusWithSize result = flash_.Read(address_, data);
80 address_ += result.size();
81 return result;
82}
83
Paul Mathieu9a9ed132020-10-12 19:15:34 -070084FlashPartition::FlashPartition(
85 FlashMemory* flash,
86 uint32_t start_sector_index,
87 uint32_t sector_count,
88 uint32_t alignment_bytes, // Defaults to flash alignment
89 PartitionPermission permission)
90
91 : flash_(*flash),
92 start_sector_index_(start_sector_index),
93 sector_count_(sector_count),
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
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800106Status FlashPartition::Erase(Address address, size_t num_sectors) {
107 if (permission_ == PartitionPermission::kReadOnly) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700108 return Status::PermissionDenied();
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800109 }
110
David Rogersc4dc8642020-09-14 10:52:36 -0700111 PW_TRY(CheckBounds(address, num_sectors * sector_size_bytes()));
David Rogers907570b2020-08-06 12:44:27 -0700112 const size_t address_sector_offset = address % sector_size_bytes();
113 PW_CHECK_UINT_EQ(address_sector_offset, 0u);
114
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800115 return flash_.Erase(PartitionToFlashAddress(address), num_sectors);
116}
117
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700118StatusWithSize FlashPartition::Read(Address address, std::span<byte> output) {
David Rogersc4dc8642020-09-14 10:52:36 -0700119 PW_TRY_WITH_SIZE(CheckBounds(address, output.size()));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800120 return flash_.Read(PartitionToFlashAddress(address), output);
121}
122
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700123StatusWithSize FlashPartition::Write(Address address,
124 std::span<const byte> data) {
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800125 if (permission_ == PartitionPermission::kReadOnly) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700126 return StatusWithSize::PermissionDenied();
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800127 }
David Rogersc4dc8642020-09-14 10:52:36 -0700128 PW_TRY_WITH_SIZE(CheckBounds(address, data.size()));
David Rogers907570b2020-08-06 12:44:27 -0700129 const size_t address_alignment_offset = address % alignment_bytes();
130 PW_CHECK_UINT_EQ(address_alignment_offset, 0u);
131 const size_t size_alignment_offset = data.size() % alignment_bytes();
132 PW_CHECK_UINT_EQ(size_alignment_offset, 0u);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800133 return flash_.Write(PartitionToFlashAddress(address), data);
134}
135
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800136Status FlashPartition::IsRegionErased(Address source_flash_address,
137 size_t length,
138 bool* is_erased) {
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800139 // Relying on Read() to check address and len arguments.
140 if (is_erased == nullptr) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700141 return Status::InvalidArgument();
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800142 }
David Rogersca592962020-07-01 09:21:54 -0700143
144 // TODO(pwbug/214): Currently using a single flash alignment to do both the
145 // read and write. The allowable flash read length may be less than what write
David Rogers3f12bff2021-04-13 21:59:26 -0700146 // needs (possibly by a bunch), resulting in read_buffer and
147 // erased_pattern_buffer being bigger than they need to be.
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800148 const size_t alignment = alignment_bytes();
David Rogersca592962020-07-01 09:21:54 -0700149 if (alignment > kMaxFlashAlignment || kMaxFlashAlignment % alignment ||
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800150 length % alignment) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700151 return Status::InvalidArgument();
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800152 }
153
David Rogers3f12bff2021-04-13 21:59:26 -0700154 byte read_buffer[kMaxFlashAlignment];
David Rogersa5661ef2020-07-09 15:15:12 -0700155 const byte erased_byte = flash_.erased_memory_content();
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800156 size_t offset = 0;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800157 *is_erased = false;
158 while (length > 0u) {
159 // Check earlier that length is aligned, no need to round up
David Rogers3f12bff2021-04-13 21:59:26 -0700160 size_t read_size = std::min(sizeof(read_buffer), length);
161 PW_TRY(
162 Read(source_flash_address + offset, read_size, read_buffer).status());
David Rogersa5661ef2020-07-09 15:15:12 -0700163
David Rogers3f12bff2021-04-13 21:59:26 -0700164 for (byte b : std::span(read_buffer, read_size)) {
David Rogersa5661ef2020-07-09 15:15:12 -0700165 if (b != erased_byte) {
166 // Detected memory chunk is not entirely erased
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800167 return OkStatus();
David Rogersa5661ef2020-07-09 15:15:12 -0700168 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800169 }
David Rogersa5661ef2020-07-09 15:15:12 -0700170
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800171 offset += read_size;
172 length -= read_size;
173 }
174 *is_erased = true;
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800175 return OkStatus();
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800176}
177
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700178bool FlashPartition::AppearsErased(std::span<const byte> data) const {
David Rogers6a6dae62020-07-10 01:13:38 -0700179 byte erased_content = flash_.erased_memory_content();
Wyatt Heplere541e072020-02-14 09:10:53 -0800180 for (byte b : data) {
David Rogers6a6dae62020-07-10 01:13:38 -0700181 if (b != erased_content) {
Wyatt Heplere541e072020-02-14 09:10:53 -0800182 return false;
183 }
184 }
185 return true;
186}
187
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800188Status FlashPartition::CheckBounds(Address address, size_t length) const {
189 if (address + length > size_bytes()) {
David Rogers9fc78b82020-06-12 13:56:12 -0700190 PW_LOG_ERROR(
David Rogerseb05d882021-10-12 15:24:48 -0700191 "FlashPartition - Attempted access (address: %u length: %u), exceeds "
192 "partition size %u bytes",
David Rogers9fc78b82020-06-12 13:56:12 -0700193 unsigned(address),
David Rogerseb05d882021-10-12 15:24:48 -0700194 unsigned(length),
195 unsigned(size_bytes()));
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700196 return Status::OutOfRange();
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800197 }
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800198 return OkStatus();
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800199}
200
201} // namespace pw::kvs