mirror of
https://fuchsia.googlesource.com/third_party/pigweed.googlesource.com/pigweed/pigweed
synced 2024-09-21 14:16:26 +00:00
4da1fcb4b2
Builds, but lots more work needed. Fun fact: this code was written collaboratively in a Google Doc. Change-Id: I8a89c5d0fdc71ec28cf432350e65d17e24a6f25c
150 lines
4.8 KiB
C++
150 lines
4.8 KiB
C++
// Copyright 2020 The Pigweed Authors
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
|
|
// use this file except in compliance with the License. You may obtain a copy of
|
|
// the License at
|
|
//
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
// License for the specific language governing permissions and limitations under
|
|
// the License.
|
|
|
|
#include "pw_kvs/flash_memory.h"
|
|
|
|
#include <algorithm>
|
|
#include <cinttypes>
|
|
#include <cstring>
|
|
|
|
#include "pw_kvs_private/macros.h"
|
|
#include "pw_log/log.h"
|
|
|
|
namespace pw::kvs {
|
|
|
|
using std::byte;
|
|
|
|
Status FlashPartition::Erase(Address address, size_t num_sectors) {
|
|
if (permission_ == PartitionPermission::kReadOnly) {
|
|
return Status::PERMISSION_DENIED;
|
|
}
|
|
|
|
TRY(CheckBounds(address, num_sectors * sector_size_bytes()));
|
|
return flash_.Erase(PartitionToFlashAddress(address), num_sectors);
|
|
}
|
|
|
|
StatusWithSize FlashPartition::Read(Address address, span<byte> output) {
|
|
TRY(CheckBounds(address, output.size()));
|
|
return flash_.Read(PartitionToFlashAddress(address), output);
|
|
}
|
|
|
|
StatusWithSize FlashPartition::Write(Address address, span<const byte> data) {
|
|
if (permission_ == PartitionPermission::kReadOnly) {
|
|
return Status::PERMISSION_DENIED;
|
|
}
|
|
TRY(CheckBounds(address, data.size()));
|
|
return flash_.Write(PartitionToFlashAddress(address), data);
|
|
}
|
|
|
|
StatusWithSize FlashPartition::Write(
|
|
const Address start_address, std::initializer_list<span<const byte>> data) {
|
|
byte buffer[64]; // TODO: Configure this?
|
|
|
|
Address address = start_address;
|
|
auto bytes_written = [&]() { return address - start_address; };
|
|
|
|
const size_t write_size = AlignDown(sizeof(buffer), alignment_bytes());
|
|
size_t bytes_in_buffer = 0;
|
|
|
|
for (span<const byte> chunk : data) {
|
|
while (!chunk.empty()) {
|
|
const size_t to_copy =
|
|
std::min(write_size - bytes_in_buffer, chunk.size());
|
|
|
|
std::memcpy(&buffer[bytes_in_buffer], chunk.data(), to_copy);
|
|
chunk = chunk.subspan(to_copy);
|
|
bytes_in_buffer += to_copy;
|
|
|
|
// If the buffer is full, write it out.
|
|
if (bytes_in_buffer == write_size) {
|
|
Status status = Write(address, span(buffer, write_size));
|
|
if (!status.ok()) {
|
|
return StatusWithSize(status, bytes_written());
|
|
}
|
|
|
|
address += write_size;
|
|
bytes_in_buffer = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If data remains in the buffer, pad it to the alignment size and flush
|
|
// the remaining data.
|
|
if (bytes_in_buffer != 0u) {
|
|
size_t remaining_write_size = AlignUp(bytes_in_buffer, alignment_bytes());
|
|
std::memset(
|
|
&buffer[bytes_in_buffer], 0, remaining_write_size - bytes_in_buffer);
|
|
if (Status status = Write(address, span(buffer, remaining_write_size));
|
|
!status.ok()) {
|
|
return StatusWithSize(status, bytes_written());
|
|
}
|
|
address += remaining_write_size;
|
|
}
|
|
|
|
return StatusWithSize(bytes_written());
|
|
}
|
|
|
|
Status FlashPartition::IsRegionErased(Address source_flash_address,
|
|
size_t length,
|
|
bool* is_erased) {
|
|
// Max alignment is artificial to keep the stack usage low for this
|
|
// function. Using 16 because it's the alignment of encrypted flash.
|
|
constexpr size_t kMaxAlignment = 16;
|
|
|
|
// Relying on Read() to check address and len arguments.
|
|
if (is_erased == nullptr) {
|
|
return Status::INVALID_ARGUMENT;
|
|
}
|
|
const size_t alignment = alignment_bytes();
|
|
if (alignment > kMaxAlignment || kMaxAlignment % alignment ||
|
|
length % alignment) {
|
|
return Status::INVALID_ARGUMENT;
|
|
}
|
|
|
|
byte buffer[kMaxAlignment];
|
|
byte erased_pattern_buffer[kMaxAlignment];
|
|
|
|
size_t offset = 0;
|
|
std::memset(erased_pattern_buffer,
|
|
int(flash_.erased_memory_content()),
|
|
sizeof(erased_pattern_buffer));
|
|
*is_erased = false;
|
|
while (length > 0u) {
|
|
// Check earlier that length is aligned, no need to round up
|
|
size_t read_size = std::min(sizeof(buffer), length);
|
|
TRY(Read(source_flash_address + offset, read_size, buffer).status());
|
|
if (std::memcmp(buffer, erased_pattern_buffer, read_size)) {
|
|
// Detected memory chunk is not entirely erased
|
|
return Status::OK;
|
|
}
|
|
offset += read_size;
|
|
length -= read_size;
|
|
}
|
|
*is_erased = true;
|
|
return Status::OK;
|
|
}
|
|
|
|
Status FlashPartition::CheckBounds(Address address, size_t length) const {
|
|
if (address + length > size_bytes()) {
|
|
PW_LOG_ERROR("Attempted out-of-bound flash memory access (address: %" PRIu32
|
|
" length: %zu)",
|
|
address,
|
|
length);
|
|
return Status::INVALID_ARGUMENT;
|
|
}
|
|
return Status::OK;
|
|
}
|
|
|
|
} // namespace pw::kvs
|