| // Copyright 2024 Google LLC |
| // |
| // 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 |
| // |
| // http://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 <netinet/in.h> |
| #include <sys/socket.h> |
| #include <sys/types.h> |
| |
| #include <cerrno> |
| #include <cstdint> |
| #include <cstring> |
| #include <memory> |
| |
| #include "absl/flags/flag.h" |
| #include "absl/flags/parse.h" |
| #include "absl/flags/usage.h" |
| #include "absl/log/check.h" |
| #include "absl/log/log.h" |
| #include "cheriot/cheriot_test_rig.h" |
| #include "cheriot/test_rig_packets.h" |
| |
| using ::mpact::sim::cheriot::CheriotTestRig; |
| using ::mpact::sim::cheriot::test_rig::InstructionPacket; |
| using ::mpact::sim::cheriot::test_rig::TraceCommand; |
| using ::mpact::sim::cheriot::test_rig::VersionPacket; |
| |
| ABSL_FLAG(int, trace_port, 0, "Trace port number"); |
| |
| int main(int argc, char **argv) { |
| absl::SetProgramUsageMessage(argv[0]); |
| auto arg_vec = absl::ParseCommandLine(argc, argv); |
| |
| // Verify that the port number has been set. |
| if (absl::GetFlag(FLAGS_trace_port) == 0) { |
| LOG(ERROR) << "No trace target port specified\n"; |
| return -1; |
| } |
| |
| // Connect to the sockets. |
| auto trace_socket = socket(AF_INET, SOCK_STREAM, 0); |
| if (trace_socket == -1) { |
| LOG(ERROR) << "Error creating socket\n"; |
| return -1; |
| } |
| // Set socket option SO_REUSEADDR and SO_REUSEPORT. |
| int reuseaddr = 1; |
| int reuseport = 1; |
| if (setsockopt(trace_socket, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, |
| sizeof(reuseaddr)) < 0) { |
| LOG(ERROR) << "Failed to set socket option SO_REUSEADDR\n"; |
| return -1; |
| } |
| if (setsockopt(trace_socket, SOL_SOCKET, SO_REUSEPORT, &reuseport, |
| sizeof(reuseport)) < 0) { |
| LOG(ERROR) << "Failed to set socket option SO_REUSEPORT\n"; |
| return -1; |
| } |
| if (struct timeval t = {0, 0}; |
| setsockopt(trace_socket, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t)) < 0) { |
| LOG(ERROR) << "Failed to set socket option SO_RCVTIMEO\n"; |
| return -1; |
| } |
| // Bind the socket. |
| const sockaddr_in trace_address_in = { |
| AF_INET, htons(absl::GetFlag(FLAGS_trace_port)), {INADDR_ANY}}; |
| int res = |
| bind(trace_socket, reinterpret_cast<const sockaddr *>(&trace_address_in), |
| sizeof(trace_address_in)); |
| if (res != 0) { |
| LOG(ERROR) << "Error connecting to trace_socket (" << res << ")\n"; |
| return -1; |
| } |
| // Accept connection. |
| res = listen(trace_socket, 1); |
| int trace_fd = accept(trace_socket, nullptr, nullptr); |
| |
| // Test rig engine. |
| auto test_rig = std::make_unique<CheriotTestRig>(); |
| |
| CHECK_OK(test_rig->SetVersion(1)); |
| InstructionPacket inst_packet; |
| VersionPacket version_packet; |
| |
| bool error = false; |
| uint32_t trace_version = 0; |
| while (true) { |
| auto res = read(trace_fd, &inst_packet, sizeof(inst_packet)); |
| // Check for error. |
| if (res < 0) { |
| LOG(ERROR) << "Error reading from trace socket (" << errno << ")\n"; |
| error = true; |
| break; |
| } |
| // Zero bytes indicates end of file. |
| if (res == 0) break; |
| if (res != sizeof(inst_packet)) { |
| LOG(ERROR) << "Error reading insufficient bytes from trace socket (" |
| << res << " != " << sizeof(inst_packet) << ")\n"; |
| error = true; |
| break; |
| } |
| switch (inst_packet.rvfi_cmd) { |
| case TraceCommand::kEndOfTrace: { |
| // First check if this is a version negotiation packet. |
| uint8_t halt; |
| if (inst_packet.rvfi_insn == 0x56455253) { |
| auto version = test_rig->GetMaxSupportedVersion(); |
| halt = 1 | version; |
| } else { // End of trace packet. |
| halt = 1; |
| } |
| auto status = test_rig->Reset(halt, trace_fd); |
| if (!status.ok()) { |
| LOG(ERROR) << "Error: " << status.message() << "\n"; |
| error = true; |
| } |
| break; |
| } |
| case TraceCommand::kInstruction: { |
| // Execute the trace packet. |
| auto status = test_rig->Execute(inst_packet, trace_fd); |
| if (!status.ok()) { |
| LOG(ERROR) << "Error executing trace packet (" << status.message() |
| << ")\n"; |
| error = true; |
| } |
| break; |
| } |
| case TraceCommand::kSetVersion: { |
| // Set the trace version to write. |
| trace_version = inst_packet.rvfi_insn; |
| auto status = test_rig->SetVersion(trace_version); |
| if (!status.ok()) { |
| LOG(ERROR) << "Error setting trace version (" << status.message() |
| << ")\n"; |
| error = true; |
| break; |
| } |
| version_packet.version = trace_version; |
| res = write(trace_fd, &version_packet, sizeof(version_packet)); |
| if (res != sizeof(version_packet)) { |
| LOG(ERROR) << "Error writing to trace socket (" << res << ")\n"; |
| error = true; |
| } |
| break; |
| } |
| default: |
| LOG(ERROR) << "Unknown command (ignored): " << (int)inst_packet.rvfi_cmd |
| << "\n"; |
| break; |
| } |
| if (error) break; |
| } |
| |
| // Shutdown the socket. |
| res = shutdown(trace_fd, SHUT_RDWR); |
| res = shutdown(trace_socket, SHUT_RDWR); |
| } |