This commit converts `gem5::loader::Symbol` to a full class with private members, enforcing encapsulation. Until now client code has been able to (and does) access members directly. This change will enable class invariants to be enforced via accessor methods. Change-Id: Ia0b5b080d4f656637a211808e13dce1ddca74541 Reviewed-by: Andreas Sandberg <andreas.sandberg@arm.com>
649 lines
22 KiB
C++
649 lines
22 KiB
C++
/*
|
|
* Copyright (c) 2016, 2022-2023 Arm Limited
|
|
* All rights reserved
|
|
*
|
|
* The license below extends only to copyright in the software and shall
|
|
* not be construed as granting a license to any other intellectual
|
|
* property including but not limited to intellectual property relating
|
|
* to a hardware implementation of the functionality of the software
|
|
* licensed hereunder. You may use the software subject to the license
|
|
* terms below provided that you ensure that this notice is replicated
|
|
* unmodified and in its entirety in all distributions of the software,
|
|
* modified or unmodified, in source code or in binary form.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met: redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer;
|
|
* redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution;
|
|
* neither the name of the copyright holders nor the names of its
|
|
* contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "kern/linux/helpers.hh"
|
|
|
|
#include <type_traits>
|
|
|
|
#include "base/compiler.hh"
|
|
#include "base/loader/object_file.hh"
|
|
#include "cpu/thread_context.hh"
|
|
#include "mem/port_proxy.hh"
|
|
#include "mem/translating_port_proxy.hh"
|
|
#include "sim/byteswap.hh"
|
|
#include "sim/system.hh"
|
|
|
|
namespace gem5 {
|
|
|
|
namespace linux {
|
|
|
|
namespace {
|
|
|
|
namespace pre5_10 {
|
|
|
|
/** Dmesg entry for Linux versions pre-v5.10 */
|
|
struct GEM5_PACKED DmesgEntry
|
|
{
|
|
uint64_t ts_nsec;
|
|
uint16_t len;
|
|
uint16_t text_len;
|
|
uint16_t dict_len;
|
|
uint8_t facility;
|
|
uint8_t flags;
|
|
};
|
|
|
|
/** Dump a Linux Demsg entry, pre-v5.10. */
|
|
static int
|
|
dumpDmesgEntry(const uint8_t *base, const uint8_t *end,
|
|
const ByteOrder bo,
|
|
std::ostream &os)
|
|
{
|
|
const size_t max_length = end - base;
|
|
DmesgEntry de;
|
|
|
|
if (max_length < sizeof(de)) {
|
|
warn("Malformed dmesg entry\n");
|
|
return -1;
|
|
}
|
|
|
|
memcpy(&de, base, sizeof(de));
|
|
de.ts_nsec = gtoh(de.ts_nsec, bo);
|
|
de.len = gtoh(de.len, bo);
|
|
de.text_len = gtoh(de.text_len, bo);
|
|
|
|
if (de.len < sizeof(de) ||
|
|
max_length < de.len ||
|
|
max_length < sizeof(DmesgEntry) + de.text_len) {
|
|
|
|
warn("Malformed dmesg entry:\n");
|
|
warn("\tMax length: %i\n", max_length);
|
|
warn("\tde.len: %i\n", de.len);
|
|
warn("\tde.text_len: %i\n", de.text_len);
|
|
return -1;
|
|
}
|
|
|
|
ccprintf(os, "[%.6f] ", de.ts_nsec * 10e-9);
|
|
os.write((char *)base + sizeof(de), de.text_len);
|
|
os << std::endl;
|
|
|
|
return de.len;
|
|
}
|
|
|
|
/** Dump the kernel Dmesg ringbuffer for Linux versions pre-v5.10 */
|
|
void
|
|
dumpDmesg(ThreadContext *tc, std::ostream &os)
|
|
{
|
|
System *system = tc->getSystemPtr();
|
|
const ByteOrder bo = system->getGuestByteOrder();
|
|
const auto &symtab = system->workload->symtab(tc);
|
|
TranslatingPortProxy proxy(tc);
|
|
|
|
auto lb = symtab.find("__log_buf");
|
|
auto lb_len = symtab.find("log_buf_len");
|
|
auto first = symtab.find("log_first_idx");
|
|
auto next = symtab.find("log_next_idx");
|
|
|
|
auto end_it = symtab.end();
|
|
|
|
if (lb == end_it || lb_len == end_it ||
|
|
first == end_it || next == end_it) {
|
|
warn("Failed to find kernel dmesg symbols.\n");
|
|
return;
|
|
}
|
|
|
|
uint32_t log_buf_len = proxy.read<uint32_t>(lb_len->address(), bo);
|
|
uint32_t log_first_idx = proxy.read<uint32_t>(first->address(), bo);
|
|
uint32_t log_next_idx = proxy.read<uint32_t>(next->address(), bo);
|
|
|
|
if (log_first_idx >= log_buf_len || log_next_idx >= log_buf_len) {
|
|
warn("dmesg pointers/length corrupted\n");
|
|
return;
|
|
}
|
|
|
|
// Normalize and read the dmesg ring buffer
|
|
std::vector<uint8_t> log_buf(log_buf_len);
|
|
int length;
|
|
if (log_first_idx < log_next_idx) {
|
|
length = log_next_idx - log_first_idx;
|
|
if (length < 0 || length > log_buf.size()) {
|
|
warn("Unexpected dmesg buffer length\n");
|
|
return;
|
|
}
|
|
proxy.readBlob(lb->address() + log_first_idx, log_buf.data(), length);
|
|
} else {
|
|
const int length_2 = log_buf_len - log_first_idx;
|
|
if (length_2 < 0 || length_2 + log_next_idx > log_buf.size()) {
|
|
warn("Unexpected dmesg buffer length\n");
|
|
return;
|
|
}
|
|
length = log_buf_len;
|
|
proxy.readBlob(
|
|
lb->address() + log_first_idx, log_buf.data(), length_2);
|
|
proxy.readBlob(
|
|
lb->address(), log_buf.data() + length_2, log_next_idx);
|
|
}
|
|
|
|
// Print dmesg buffer content
|
|
const uint8_t *cur = log_buf.data(), *end = log_buf.data() + length;
|
|
while (cur < end) {
|
|
int ret = dumpDmesgEntry(cur, end, bo, os);
|
|
if (ret < 0)
|
|
return;
|
|
cur += ret;
|
|
}
|
|
}
|
|
|
|
} // namespace pre5_10
|
|
|
|
namespace post5_10 {
|
|
|
|
/** Metadata record for the Linux dmesg ringbuffer, post-v5.10.
|
|
*
|
|
* Struct data members are compatible with the equivalent Linux data
|
|
* structure. Should be templated on atomic_var_t=int32_t for 32-bit
|
|
* Linux and atomic_var_t=int64_t for 64-bit Linux.
|
|
*
|
|
* Also includes helper methods for reading the data structure into
|
|
* the gem5 world.
|
|
*
|
|
*/
|
|
template<typename atomic_var_t>
|
|
struct GEM5_PACKED DmesgMetadataRecord
|
|
{
|
|
using guest_ptr_t = typename std::make_unsigned_t<atomic_var_t>;
|
|
|
|
// Struct data members
|
|
atomic_var_t state;
|
|
struct
|
|
{
|
|
guest_ptr_t curr_offset;
|
|
guest_ptr_t next_offset;
|
|
} data_buffer;
|
|
|
|
/** Read a DmesgMetadataRecord from guest memory. */
|
|
static DmesgMetadataRecord
|
|
read(const TranslatingPortProxy & proxy,
|
|
Addr address,
|
|
guest_ptr_t data_offset_mask,
|
|
const ByteOrder & bo)
|
|
{
|
|
DmesgMetadataRecord metadata;
|
|
proxy.readBlob(address, &metadata, sizeof(metadata));
|
|
|
|
// Convert members to host byte order
|
|
metadata.state = gtoh(metadata.state, bo);
|
|
metadata.data_buffer.curr_offset =
|
|
gtoh(metadata.data_buffer.curr_offset, bo);
|
|
metadata.data_buffer.next_offset =
|
|
gtoh(metadata.data_buffer.next_offset, bo);
|
|
|
|
// Mask the offsets
|
|
metadata.data_buffer.curr_offset =
|
|
metadata.data_buffer.curr_offset & data_offset_mask;
|
|
metadata.data_buffer.next_offset =
|
|
metadata.data_buffer.next_offset & data_offset_mask;
|
|
|
|
return metadata;
|
|
}
|
|
};
|
|
|
|
/** Info record for the Linux dmesg ringbuffer, post-v5.10.
|
|
*
|
|
* Struct data members are compatible with the equivalent Linux data
|
|
* structure. Should be templated on atomic_var_t=int32_t for 32-bit
|
|
* Linux and atomic_var_t=int64_t for 64-bit Linux.
|
|
*
|
|
* Also includes helper methods for reading the data structure into
|
|
* the gem5 world.
|
|
*
|
|
*/
|
|
struct GEM5_PACKED DmesgInfoRecord
|
|
{
|
|
// Struct data members
|
|
uint64_t unused1;
|
|
uint64_t ts_nsec;
|
|
uint16_t message_size;
|
|
uint8_t unused2;
|
|
uint8_t unused3;
|
|
uint32_t unused4;
|
|
struct
|
|
{
|
|
char unused5_1[16];
|
|
char unused5_2[48];
|
|
} unused5;
|
|
|
|
/** Read a DmesgInfoRecord from guest memory. */
|
|
static DmesgInfoRecord
|
|
read(const TranslatingPortProxy & proxy,
|
|
Addr address,
|
|
const ByteOrder & bo)
|
|
{
|
|
DmesgInfoRecord info;
|
|
proxy.readBlob(address, &info, sizeof(info));
|
|
|
|
// Convert members to host byte order
|
|
info.ts_nsec = gtoh(info.ts_nsec, bo);
|
|
info.message_size = gtoh(info.message_size, bo);
|
|
|
|
return info;
|
|
}
|
|
};
|
|
|
|
/** Top-level ringbuffer record for the Linux dmesg ringbuffer, post-v5.10.
|
|
*
|
|
* Struct data members are compatible with the equivalent Linux data
|
|
* structure. Should be templated on AtomicVarType=int32_t for 32-bit
|
|
* Linux and AtomicVarType=int64_t for 64-bit Linux.
|
|
*
|
|
* Also includes helper methods for reading the data structure into
|
|
* the gem5 world, and reading/generating appropriate masks.
|
|
*
|
|
*/
|
|
template<typename AtomicVarType>
|
|
struct GEM5_PACKED DmesgRingbuffer
|
|
{
|
|
static_assert(
|
|
std::disjunction<
|
|
std::is_same<AtomicVarType, int32_t>,
|
|
std::is_same<AtomicVarType, int64_t>
|
|
>::value,
|
|
"AtomicVarType must be int32_t or int64_t");
|
|
|
|
using atomic_var_t = AtomicVarType;
|
|
using guest_ptr_t = typename std::make_unsigned_t<atomic_var_t>;
|
|
using metadata_record_t = DmesgMetadataRecord<atomic_var_t>;
|
|
|
|
// Struct data members
|
|
struct
|
|
{
|
|
unsigned int mask_bits;
|
|
guest_ptr_t metadata_ring_ptr;
|
|
guest_ptr_t info_ring_ptr;
|
|
atomic_var_t unused1;
|
|
atomic_var_t unused2;
|
|
} metadata;
|
|
struct
|
|
{
|
|
unsigned int mask_bits;
|
|
guest_ptr_t data_ring_ptr;
|
|
atomic_var_t head_offset;
|
|
atomic_var_t tail_offset;
|
|
} data;
|
|
atomic_var_t fail;
|
|
|
|
/** Read a DmesgRingbuffer from guest memory. */
|
|
static DmesgRingbuffer
|
|
read(const TranslatingPortProxy & proxy,
|
|
const Addr address,
|
|
const ByteOrder & bo)
|
|
{
|
|
DmesgRingbuffer rb;
|
|
proxy.readBlob(address, &rb, sizeof(rb));
|
|
|
|
// Convert members to host byte order
|
|
rb.metadata.mask_bits =
|
|
gtoh(rb.metadata.mask_bits, bo);
|
|
rb.metadata.metadata_ring_ptr =
|
|
gtoh(rb.metadata.metadata_ring_ptr, bo);
|
|
rb.metadata.info_ring_ptr =
|
|
gtoh(rb.metadata.info_ring_ptr, bo);
|
|
|
|
rb.data.mask_bits = gtoh(rb.data.mask_bits, bo);
|
|
rb.data.data_ring_ptr = gtoh(rb.data.data_ring_ptr, bo);
|
|
rb.data.head_offset = gtoh(rb.data.head_offset, bo);
|
|
rb.data.tail_offset = gtoh(rb.data.tail_offset, bo);
|
|
|
|
// Mask offsets to the correct number of bits
|
|
rb.data.head_offset =
|
|
rb.mask_data_offset(rb.data.head_offset);
|
|
rb.data.tail_offset =
|
|
rb.mask_data_offset(rb.data.tail_offset);
|
|
|
|
return rb;
|
|
}
|
|
|
|
/** Make a mask for the bottom mask_bits of an `atomic_var_t`, then
|
|
* cast it to the required `as_type`.
|
|
*/
|
|
template<typename as_type>
|
|
static as_type
|
|
make_offset_mask_as(const unsigned int mask_bits)
|
|
{
|
|
using unsigned_atomic_var_t =
|
|
typename std::make_unsigned<atomic_var_t>::type;
|
|
const atomic_var_t offset_mask =
|
|
static_cast<atomic_var_t>(
|
|
(static_cast<unsigned_atomic_var_t>(1) << mask_bits) - 1);
|
|
return static_cast<as_type>(offset_mask);
|
|
}
|
|
|
|
/** Make a mask for an offset into the metadata or info ringbuffers. */
|
|
template<typename metadata_offset_t>
|
|
metadata_offset_t
|
|
make_metadata_offset_mask() const
|
|
{
|
|
return make_offset_mask_as<metadata_offset_t>(metadata.mask_bits);
|
|
}
|
|
|
|
/** Make a mask for an offset into the data ringbuffer. */
|
|
template<typename data_offset_t>
|
|
data_offset_t
|
|
make_data_offset_mask() const
|
|
{
|
|
return make_offset_mask_as<data_offset_t>(data.mask_bits);
|
|
}
|
|
|
|
/** Apply the correct masking to an offset into the metadata or info
|
|
ringbuffers. */
|
|
template<typename metadata_offset_t>
|
|
metadata_offset_t
|
|
mask_metadata_offset(const metadata_offset_t metadata_offset) const
|
|
{
|
|
const atomic_var_t MASK =
|
|
make_metadata_offset_mask<metadata_offset_t>();
|
|
return metadata_offset & MASK;
|
|
}
|
|
|
|
/** Apply the correct masking to an offset into the data ringbuffer. */
|
|
template<typename data_offset_t>
|
|
data_offset_t
|
|
mask_data_offset(const data_offset_t data_offset) const
|
|
{
|
|
const atomic_var_t MASK = make_data_offset_mask<data_offset_t>();
|
|
return data_offset & MASK;
|
|
}
|
|
};
|
|
|
|
// Aliases for the two types of Ringbuffer that could be used.
|
|
using Linux64_Ringbuffer = DmesgRingbuffer<int64_t>;
|
|
using Linux32_Ringbuffer = DmesgRingbuffer<int32_t>;
|
|
|
|
/** Print the record at the specified offset into the data ringbuffer,
|
|
* and return the offset of the next entry in the data ringbuffer,
|
|
* post-v5.10.
|
|
*
|
|
* The `first_metadata_offset` argument is used to check for
|
|
* wraparound. If the final data record of the ringbuffer is not large
|
|
* enough to hold the message, the record will be left empty and
|
|
* repeated at the beginning of the data ringbuffer. In this case the
|
|
* metadata offset at the beginning of the last record will match the
|
|
* metadata offset of the first record of the ringbuffer, and the last
|
|
* record of the ring buffer should be skipped.
|
|
*
|
|
*/
|
|
template <typename ringbuffer_t,
|
|
typename atomic_var_t=typename ringbuffer_t::atomic_var_t,
|
|
typename guest_ptr_t=typename ringbuffer_t::guest_ptr_t>
|
|
atomic_var_t
|
|
iterateDataRingbuffer(std::ostream & os,
|
|
const TranslatingPortProxy & proxy,
|
|
const ringbuffer_t & rb,
|
|
const atomic_var_t offset,
|
|
const guest_ptr_t first_metadata_offset,
|
|
const ByteOrder bo)
|
|
{
|
|
using metadata_record_t = typename ringbuffer_t::metadata_record_t;
|
|
|
|
constexpr size_t METADATA_RECORD_SIZE =
|
|
sizeof(typename ringbuffer_t::metadata_record_t);
|
|
constexpr size_t INFO_RECORD_SIZE = sizeof(DmesgInfoRecord);
|
|
|
|
const guest_ptr_t DATA_OFFSET_MASK =
|
|
rb.template make_data_offset_mask<guest_ptr_t>();
|
|
|
|
// Read the offset of the metadata record from the beginning of
|
|
// the data record.
|
|
guest_ptr_t metadata_info_offset = rb.mask_metadata_offset(
|
|
proxy.read<guest_ptr_t>(rb.data.data_ring_ptr + offset, bo));
|
|
|
|
// If the metadata offset of the block is the same as the metadata
|
|
// offset of the first block of the data ringbuffer, then this
|
|
// data block is unsused (padding), and the iteration can wrap
|
|
// around to the beginning of the data ringbuffer (offset == 0).
|
|
if (metadata_info_offset == first_metadata_offset) {
|
|
return static_cast<atomic_var_t>(0);
|
|
}
|
|
|
|
// Read the metadata record from the metadata ringbuffer.
|
|
guest_ptr_t metadata_address =
|
|
rb.metadata.metadata_ring_ptr +
|
|
(metadata_info_offset * METADATA_RECORD_SIZE);
|
|
metadata_record_t metadata =
|
|
metadata_record_t::read(proxy, metadata_address, DATA_OFFSET_MASK, bo);
|
|
|
|
// Read the info record from the info ringbuffer.
|
|
guest_ptr_t info_address =
|
|
rb.metadata.info_ring_ptr +
|
|
(metadata_info_offset * INFO_RECORD_SIZE);
|
|
DmesgInfoRecord info =
|
|
DmesgInfoRecord::read(proxy, info_address, bo);
|
|
|
|
// The metadata record should point back to the same data record
|
|
// in the data ringbuffer.
|
|
if (metadata.data_buffer.curr_offset != offset) {
|
|
warn_once("Dmesg dump: metadata record (at 0x%08x) does not point "
|
|
"back to the correponding data record (at 0x%08x). Dmesg "
|
|
"buffer may be corrupted",
|
|
metadata.data_buffer.next_offset, offset);
|
|
}
|
|
|
|
// Read the message from the data record. This is placed
|
|
// immediately after the `guest_ptr_t` sized metadata offset at
|
|
// the beginning of the record.
|
|
std::vector<uint8_t> message(info.message_size);
|
|
proxy.readBlob(rb.data.data_ring_ptr + offset + sizeof(guest_ptr_t),
|
|
message.data(), info.message_size);
|
|
|
|
// Print the record
|
|
ccprintf(os, "[%.6f] ", info.ts_nsec * 10e-9);
|
|
os.write((char *)message.data(), info.message_size);
|
|
os << "\n";
|
|
|
|
// Return the offset of the next data record in the data
|
|
// ringbuffer.
|
|
return metadata.data_buffer.next_offset;
|
|
}
|
|
|
|
/** Dump the kernel Dmesg ringbuffer for Linux versions post-v5.10.
|
|
|
|
Templated implementation specific to 32-bit or 64-bit Linux.
|
|
*/
|
|
template <typename ringbuffer_t>
|
|
void
|
|
dumpDmesgImpl(ThreadContext *tc, std::ostream &os)
|
|
{
|
|
using atomic_var_t = typename ringbuffer_t::atomic_var_t;
|
|
using guest_ptr_t = typename ringbuffer_t::guest_ptr_t;
|
|
|
|
System *system = tc->getSystemPtr();
|
|
const ByteOrder bo = system->getGuestByteOrder();
|
|
const auto &symtab = system->workload->symtab(tc);
|
|
TranslatingPortProxy proxy(tc);
|
|
|
|
auto symtab_end_it = symtab.end();
|
|
|
|
// Read the dynamic ringbuffer structure from guest memory, if present.
|
|
ringbuffer_t dynamic_rb;
|
|
auto dynamic_rb_symbol = symtab.find("printk_rb_dynamic");
|
|
if (dynamic_rb_symbol != symtab_end_it) {
|
|
dynamic_rb =
|
|
ringbuffer_t::read(proxy, dynamic_rb_symbol->address(), bo);
|
|
} else {
|
|
warn("Failed to find required dmesg symbols.\n");
|
|
return;
|
|
}
|
|
|
|
// Read the static ringbuffer structure from guest memory, if present.
|
|
ringbuffer_t static_rb;
|
|
auto static_rb_symbol = symtab.find("printk_rb_static");
|
|
if (static_rb_symbol != symtab_end_it) {
|
|
static_rb = ringbuffer_t::read(proxy, static_rb_symbol->address(), bo);
|
|
} else {
|
|
warn("Failed to find required dmesg symbols.\n");
|
|
return;
|
|
}
|
|
|
|
// Read the pointer to the active ringbuffer structure from guest
|
|
// memory. This should point to one of the two ringbuffer
|
|
// structures already read from guest memory.
|
|
guest_ptr_t active_ringbuffer_ptr = 0x0;
|
|
auto active_ringbuffer_ptr_symbol = symtab.find("prb");
|
|
if (active_ringbuffer_ptr_symbol != symtab_end_it) {
|
|
active_ringbuffer_ptr =
|
|
proxy.read<guest_ptr_t>(active_ringbuffer_ptr_symbol->address(),
|
|
bo);
|
|
} else {
|
|
warn("Failed to find required dmesg symbols.\n");
|
|
return;
|
|
}
|
|
|
|
if (active_ringbuffer_ptr == 0 ||
|
|
(active_ringbuffer_ptr != dynamic_rb_symbol->address() &&
|
|
active_ringbuffer_ptr != static_rb_symbol->address())) {
|
|
warn("Kernel Dmesg ringbuffer appears to be invalid.\n");
|
|
return;
|
|
}
|
|
|
|
ringbuffer_t & rb =
|
|
(active_ringbuffer_ptr == dynamic_rb_symbol->address())
|
|
? dynamic_rb : static_rb;
|
|
|
|
atomic_var_t head_offset = rb.data.head_offset;
|
|
atomic_var_t tail_offset = rb.data.tail_offset;
|
|
|
|
// Get some marker offsets into the data ringbuffer which will be
|
|
// used as end values to control the iteration.
|
|
const guest_ptr_t first_metadata_offset = rb.mask_metadata_offset(
|
|
proxy.read<guest_ptr_t>(rb.data.data_ring_ptr, bo));
|
|
const guest_ptr_t invalid_metadata_offset =
|
|
rb.template make_metadata_offset_mask<guest_ptr_t>() + 1;
|
|
|
|
// Iterate over the active ringbuffer, printing each message to
|
|
// `os`. Use the maximum number of possible info records plus one
|
|
// (invalid_metadata_offset) as an escape counter to make sure the
|
|
// process doesn't iterate infinitely if the kernel data
|
|
// structures have been corrupted.
|
|
|
|
// When head is behind tail, read to the end of the ringbuffer,
|
|
// then loop back to the begining.
|
|
//
|
|
// iterateDataRingbuffer will return offset at the beginning of
|
|
// the data ringbuffer when it loops back.
|
|
//
|
|
// `first_metadata_offset` is used to detect cases where the final data
|
|
// block is unused.
|
|
guest_ptr_t count = 0;
|
|
while (head_offset < tail_offset && count < invalid_metadata_offset) {
|
|
tail_offset =
|
|
iterateDataRingbuffer<ringbuffer_t>(
|
|
os, proxy, rb, tail_offset, first_metadata_offset, bo);
|
|
++count;
|
|
}
|
|
|
|
// When tail is behind head, read forwards from the tail offset to
|
|
// the head offset.
|
|
count = 0;
|
|
while (tail_offset < head_offset && count < invalid_metadata_offset) {
|
|
tail_offset =
|
|
iterateDataRingbuffer<ringbuffer_t>(
|
|
os, proxy, rb, tail_offset, invalid_metadata_offset, bo);
|
|
++count;
|
|
}
|
|
}
|
|
|
|
/** Dump the kernel Dmesg ringbuffer for Linux versions post-v5.10.
|
|
*
|
|
* Delegates to an architecture specific template funtion instance.
|
|
*
|
|
*/
|
|
void
|
|
dumpDmesg(ThreadContext *tc, std::ostream &os)
|
|
{
|
|
System *system = tc->getSystemPtr();
|
|
const bool os_is_64_bit = loader::archIs64Bit(system->workload->getArch());
|
|
|
|
if (os_is_64_bit) {
|
|
dumpDmesgImpl<Linux64_Ringbuffer>(tc, os);
|
|
} else {
|
|
dumpDmesgImpl<Linux32_Ringbuffer>(tc, os);
|
|
}
|
|
}
|
|
|
|
} // namespace post5_10
|
|
|
|
} // anonymous namespace
|
|
|
|
void
|
|
dumpDmesg(ThreadContext *tc, std::ostream &os)
|
|
{
|
|
System *system = tc->getSystemPtr();
|
|
const auto &symtab = system->workload->symtab(tc);
|
|
|
|
auto end_it = symtab.end();
|
|
|
|
// Search for symbols associated with the Kernel Dmesg ringbuffer,
|
|
// pre-v5.10.
|
|
auto lb = symtab.find("__log_buf");
|
|
auto lb_len = symtab.find("log_buf_len");
|
|
auto first = symtab.find("log_first_idx");
|
|
auto next = symtab.find("log_next_idx");
|
|
|
|
if (lb != end_it && lb_len != end_it &&
|
|
first != end_it && next != end_it) {
|
|
linux::pre5_10::dumpDmesg(tc, os);
|
|
return;
|
|
}
|
|
|
|
// Search for symbols associated with the Kernel Dmesg ringbuffer,
|
|
// post-v5.10.
|
|
auto printk_rb_static = symtab.find("printk_rb_static");
|
|
auto printk_rb_dynamic = symtab.find("printk_rb_dynamic");
|
|
|
|
if (printk_rb_dynamic != end_it || printk_rb_static != end_it) {
|
|
linux::post5_10::dumpDmesg(tc, os);
|
|
return;
|
|
}
|
|
|
|
// Required symbols relating to the Kernel Dmesg buffer were not
|
|
// found for any supported version of Linux.
|
|
warn("Failed to find kernel dmesg symbols.\n");
|
|
}
|
|
|
|
} // namespace linux
|
|
|
|
} // namespace gem5
|