blob: c1a4ae737fd87b264b0321163b766baef30f5941 [file] [log] [blame]
// Copyright 2023 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/developer/debug/debug_agent/aligned_chunk.h"
#include <algorithm>
namespace debug_agent {
namespace {
// Provides an way to access the bytes of a 64-bit word in order the word is written to memory
// (taking into account the current endianness).
union WordBytes {
uint64_t word;
uint8_t bytes[sizeof(uint64_t)];
};
} // namespace
bool WriteAligned64Chunks(const void* buf, size_t len,
fit::function<bool(uint64_t addr, uint64_t* value)> read,
fit::function<bool(uint64_t addr, uint64_t value)> write,
uint64_t dest_addr) {
constexpr size_t kWordSize = sizeof(uint64_t);
if (len == 0)
return true;
// The last byte written.
uint64_t last_addr = dest_addr + len - 1;
// The 64-bit word address of the first and last byte written.
uint64_t begin_word_addr = dest_addr / kWordSize * kWordSize;
uint64_t last_word_addr = last_addr / kWordSize * kWordSize;
// The byte offsets within the word of the first and last byte written. This are offsets in
// memory (independent of word endianness).
uint64_t begin_offset = dest_addr % kWordSize;
uint64_t last_offset = last_addr % kWordSize;
// Source as bytes for easier math.
const uint8_t* buf_bytes = reinterpret_cast<const uint8_t*>(buf);
if (begin_word_addr == last_word_addr) {
// Span beings and ends in the same word.
WordBytes data;
if (!read(begin_word_addr, &data.word))
return false;
std::copy(buf_bytes, buf_bytes + len, data.bytes + begin_offset);
return write(begin_word_addr, data.word);
}
// First word.
uint64_t begin_full_addr; // Address of first full word to write.
const uint8_t* cur_source = buf_bytes;
if (begin_offset == 0) {
// First word is full.
begin_full_addr = begin_word_addr;
} else {
// Partial first word.
WordBytes data;
if (!read(begin_word_addr, &data.word))
return false;
std::copy(cur_source, cur_source + kWordSize - begin_offset, data.bytes + begin_offset);
if (!write(begin_word_addr, data.word))
return false;
// Set up for the full-word copying loop.
cur_source += kWordSize - begin_offset;
begin_full_addr = begin_word_addr + kWordSize;
}
uint64_t last_full_addr; // Address of last full word to write.
if (last_offset == kWordSize - 1) {
// Last word is full.
last_full_addr = last_word_addr;
} else {
// Do partial last word.
WordBytes data;
if (!read(last_word_addr, &data.word))
return false;
std::copy(buf_bytes + len - last_offset - 1, buf_bytes + len, data.bytes);
if (!write(last_word_addr, data.word))
return false;
last_full_addr = last_word_addr - kWordSize;
}
// Do all the full words in between.
for (uint64_t cur_dest = begin_full_addr; cur_dest <= last_full_addr;
cur_dest += kWordSize, cur_source += kWordSize) {
// Read the current source value into a single word (it's not necessarily aligned).
uint64_t src_word;
memcpy(&src_word, cur_source, kWordSize);
if (!write(cur_dest, src_word))
return false;
}
return true;
}
} // namespace debug_agent