blob: ceb2d3e94f7c191c85f7b156e1fbc2dc96ad0142 [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
Armando Montanez28ecccb2020-05-04 15:35:54 -070015#define PW_LOG_MODULE_NAME "KVS"
16
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080017#include "pw_kvs/flash_memory.h"
18
19#include <algorithm>
20#include <cinttypes>
21#include <cstring>
22
David Rogersca592962020-07-01 09:21:54 -070023#include "pw_kvs_private/config.h"
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080024#include "pw_log/log.h"
Wyatt Hepler1927c282020-02-11 16:45:02 -080025#include "pw_status/status_with_size.h"
David Rogersb6b14b82020-09-11 16:27:27 -070026#include "pw_status/try.h"
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080027
28namespace pw::kvs {
29
30using std::byte;
31
Wyatt Heplere2cbadf2020-06-22 11:21:45 -070032StatusWithSize FlashPartition::Output::DoWrite(std::span<const byte> data) {
David Rogersc4dc8642020-09-14 10:52:36 -070033 PW_TRY_WITH_SIZE(flash_.Write(address_, data));
Wyatt Hepler1927c282020-02-11 16:45:02 -080034 address_ += data.size();
35 return StatusWithSize(data.size());
36}
37
Wyatt Heplere2cbadf2020-06-22 11:21:45 -070038StatusWithSize FlashPartition::Input::DoRead(std::span<byte> data) {
Wyatt Hepleree6fd762020-03-09 08:32:19 -070039 StatusWithSize result = flash_.Read(address_, data);
40 address_ += result.size();
41 return result;
42}
43
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080044Status FlashPartition::Erase(Address address, size_t num_sectors) {
45 if (permission_ == PartitionPermission::kReadOnly) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -070046 return Status::PermissionDenied();
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080047 }
48
David Rogersc4dc8642020-09-14 10:52:36 -070049 PW_TRY(CheckBounds(address, num_sectors * sector_size_bytes()));
David Rogers907570b2020-08-06 12:44:27 -070050 const size_t address_sector_offset = address % sector_size_bytes();
51 PW_CHECK_UINT_EQ(address_sector_offset, 0u);
52
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080053 return flash_.Erase(PartitionToFlashAddress(address), num_sectors);
54}
55
Wyatt Heplere2cbadf2020-06-22 11:21:45 -070056StatusWithSize FlashPartition::Read(Address address, std::span<byte> output) {
David Rogersc4dc8642020-09-14 10:52:36 -070057 PW_TRY_WITH_SIZE(CheckBounds(address, output.size()));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080058 return flash_.Read(PartitionToFlashAddress(address), output);
59}
60
Wyatt Heplere2cbadf2020-06-22 11:21:45 -070061StatusWithSize FlashPartition::Write(Address address,
62 std::span<const byte> data) {
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080063 if (permission_ == PartitionPermission::kReadOnly) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -070064 return StatusWithSize::PermissionDenied();
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080065 }
David Rogersc4dc8642020-09-14 10:52:36 -070066 PW_TRY_WITH_SIZE(CheckBounds(address, data.size()));
David Rogers907570b2020-08-06 12:44:27 -070067 const size_t address_alignment_offset = address % alignment_bytes();
68 PW_CHECK_UINT_EQ(address_alignment_offset, 0u);
69 const size_t size_alignment_offset = data.size() % alignment_bytes();
70 PW_CHECK_UINT_EQ(size_alignment_offset, 0u);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080071 return flash_.Write(PartitionToFlashAddress(address), data);
72}
73
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080074Status FlashPartition::IsRegionErased(Address source_flash_address,
75 size_t length,
76 bool* is_erased) {
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080077 // Relying on Read() to check address and len arguments.
78 if (is_erased == nullptr) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -070079 return Status::InvalidArgument();
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080080 }
David Rogersca592962020-07-01 09:21:54 -070081
82 // TODO(pwbug/214): Currently using a single flash alignment to do both the
83 // read and write. The allowable flash read length may be less than what write
84 // needs (possibly by a bunch), resulting in buffer and erased_pattern_buffer
85 // being bigger than they need to be.
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080086 const size_t alignment = alignment_bytes();
David Rogersca592962020-07-01 09:21:54 -070087 if (alignment > kMaxFlashAlignment || kMaxFlashAlignment % alignment ||
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080088 length % alignment) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -070089 return Status::InvalidArgument();
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080090 }
91
David Rogersca592962020-07-01 09:21:54 -070092 byte buffer[kMaxFlashAlignment];
David Rogersa5661ef2020-07-09 15:15:12 -070093 const byte erased_byte = flash_.erased_memory_content();
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080094 size_t offset = 0;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080095 *is_erased = false;
96 while (length > 0u) {
97 // Check earlier that length is aligned, no need to round up
98 size_t read_size = std::min(sizeof(buffer), length);
David Rogersc4dc8642020-09-14 10:52:36 -070099 PW_TRY(Read(source_flash_address + offset, read_size, buffer).status());
David Rogersa5661ef2020-07-09 15:15:12 -0700100
101 for (byte b : std::span(buffer, read_size)) {
102 if (b != erased_byte) {
103 // Detected memory chunk is not entirely erased
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700104 return Status::Ok();
David Rogersa5661ef2020-07-09 15:15:12 -0700105 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800106 }
David Rogersa5661ef2020-07-09 15:15:12 -0700107
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800108 offset += read_size;
109 length -= read_size;
110 }
111 *is_erased = true;
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700112 return Status::Ok();
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800113}
114
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700115bool FlashPartition::AppearsErased(std::span<const byte> data) const {
David Rogers6a6dae62020-07-10 01:13:38 -0700116 byte erased_content = flash_.erased_memory_content();
Wyatt Heplere541e072020-02-14 09:10:53 -0800117 for (byte b : data) {
David Rogers6a6dae62020-07-10 01:13:38 -0700118 if (b != erased_content) {
Wyatt Heplere541e072020-02-14 09:10:53 -0800119 return false;
120 }
121 }
122 return true;
123}
124
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800125Status FlashPartition::CheckBounds(Address address, size_t length) const {
126 if (address + length > size_bytes()) {
David Rogers9fc78b82020-06-12 13:56:12 -0700127 PW_LOG_ERROR(
128 "Attempted out-of-bound flash memory access (address: %u length: %u)",
129 unsigned(address),
130 unsigned(length));
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700131 return Status::OutOfRange();
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800132 }
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700133 return Status::Ok();
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800134}
135
136} // namespace pw::kvs