blob: 242f13e6ddb183e15a332d7c177b808a313a3c5c [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
23#include "pw_kvs_private/macros.h"
24#include "pw_log/log.h"
Wyatt Hepler1927c282020-02-11 16:45:02 -080025#include "pw_status/status_with_size.h"
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080026
27namespace pw::kvs {
28
29using std::byte;
30
Wyatt Hepleree6fd762020-03-09 08:32:19 -070031StatusWithSize FlashPartition::Output::DoWrite(span<const byte> data) {
Wyatt Hepler50f70772020-02-13 11:25:10 -080032 TRY_WITH_SIZE(flash_.Write(address_, data));
Wyatt Hepler1927c282020-02-11 16:45:02 -080033 address_ += data.size();
34 return StatusWithSize(data.size());
35}
36
Wyatt Hepleree6fd762020-03-09 08:32:19 -070037StatusWithSize FlashPartition::Input::DoRead(span<byte> data) {
38 StatusWithSize result = flash_.Read(address_, data);
39 address_ += result.size();
40 return result;
41}
42
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080043Status FlashPartition::Erase(Address address, size_t num_sectors) {
44 if (permission_ == PartitionPermission::kReadOnly) {
45 return Status::PERMISSION_DENIED;
46 }
47
48 TRY(CheckBounds(address, num_sectors * sector_size_bytes()));
49 return flash_.Erase(PartitionToFlashAddress(address), num_sectors);
50}
51
52StatusWithSize FlashPartition::Read(Address address, span<byte> output) {
Wyatt Hepler50f70772020-02-13 11:25:10 -080053 TRY_WITH_SIZE(CheckBounds(address, output.size()));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080054 return flash_.Read(PartitionToFlashAddress(address), output);
55}
56
57StatusWithSize FlashPartition::Write(Address address, span<const byte> data) {
58 if (permission_ == PartitionPermission::kReadOnly) {
Wyatt Heplerf7078802020-02-25 13:50:05 -080059 return StatusWithSize::PERMISSION_DENIED;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080060 }
Wyatt Hepler50f70772020-02-13 11:25:10 -080061 TRY_WITH_SIZE(CheckBounds(address, data.size()));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080062 return flash_.Write(PartitionToFlashAddress(address), data);
63}
64
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080065Status FlashPartition::IsRegionErased(Address source_flash_address,
66 size_t length,
67 bool* is_erased) {
68 // Max alignment is artificial to keep the stack usage low for this
69 // function. Using 16 because it's the alignment of encrypted flash.
70 constexpr size_t kMaxAlignment = 16;
71
72 // Relying on Read() to check address and len arguments.
73 if (is_erased == nullptr) {
74 return Status::INVALID_ARGUMENT;
75 }
76 const size_t alignment = alignment_bytes();
77 if (alignment > kMaxAlignment || kMaxAlignment % alignment ||
78 length % alignment) {
79 return Status::INVALID_ARGUMENT;
80 }
81
82 byte buffer[kMaxAlignment];
83 byte erased_pattern_buffer[kMaxAlignment];
84
85 size_t offset = 0;
86 std::memset(erased_pattern_buffer,
87 int(flash_.erased_memory_content()),
88 sizeof(erased_pattern_buffer));
89 *is_erased = false;
90 while (length > 0u) {
91 // Check earlier that length is aligned, no need to round up
92 size_t read_size = std::min(sizeof(buffer), length);
93 TRY(Read(source_flash_address + offset, read_size, buffer).status());
94 if (std::memcmp(buffer, erased_pattern_buffer, read_size)) {
95 // Detected memory chunk is not entirely erased
96 return Status::OK;
97 }
98 offset += read_size;
99 length -= read_size;
100 }
101 *is_erased = true;
102 return Status::OK;
103}
104
Wyatt Heplere541e072020-02-14 09:10:53 -0800105bool FlashPartition::AppearsErased(span<const byte> data) const {
106 for (byte b : data) {
107 if (b != flash_.erased_memory_content()) {
108 return false;
109 }
110 }
111 return true;
112}
113
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800114Status FlashPartition::CheckBounds(Address address, size_t length) const {
115 if (address + length > size_bytes()) {
David Rogers9fc78b82020-06-12 13:56:12 -0700116 PW_LOG_ERROR(
117 "Attempted out-of-bound flash memory access (address: %u length: %u)",
118 unsigned(address),
119 unsigned(length));
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800120 return Status::OUT_OF_RANGE;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800121 }
122 return Status::OK;
123}
124
125} // namespace pw::kvs