| // 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 |