#include #include #include #include #include #include #include #include #include #include "hardware-test/hardwaretests.h" #include "common/be_val.h" #pragma comment(lib, "ws2_32.lib") #pragma comment(lib, "ovsocket.lib") using namespace ovs; using namespace std::placeholders; namespace fs = std::experimental::filesystem; // Deal with it. static std::vector gTestSet; #pragma pack(push, 1) struct HWRegisterState { be_val xer; be_val cr; be_val ctr; be_val _; be_val r3; be_val r4; be_val r5; be_val r6; be_val fpscr; be_val f1; be_val f2; be_val f3; be_val f4; }; struct PacketHeader { enum Commands { Version = 1, ExecuteGeneralTest = 10, ExecutePairedTest = 20, TestsFinished = 50, }; be_val size; be_val command; }; struct VersionPacket : PacketHeader { VersionPacket(uint32_t value) { size = sizeof(VersionPacket); command = PacketHeader::Version; version = value; } be_val version; }; struct ExecuteGeneralTestPacket : PacketHeader { ExecuteGeneralTestPacket() { size = sizeof(ExecuteGeneralTestPacket); command = PacketHeader::ExecuteGeneralTest; memset(&state, 0, sizeof(HWRegisterState)); } be_val instr; HWRegisterState state; }; struct TestFinishedPacket : PacketHeader { TestFinishedPacket() { size = sizeof(VersionPacket); command = PacketHeader::TestsFinished; } }; #pragma pack(pop) class TestServer { static const uint32_t Version = 1; public: TestServer(Socket *socket) : mSocket(socket) { mSocket->addErrorListener(std::bind(&TestServer::onSocketError, this, _1, _2)); mSocket->addDisconnectListener(std::bind(&TestServer::onSocketDisconnect, this, _1)); mSocket->addReceiveListener(std::bind(&TestServer::onSocketReceive, this, _1, _2, _3)); // Send version VersionPacket version { Version }; mSocket->send(reinterpret_cast(&version), sizeof(VersionPacket)); // Read first packet mSocket->recvFill(sizeof(PacketHeader)); // Initialise test iterators mTestFile = gTestSet.begin(); mTestData = mTestFile->tests.begin(); } private: void saveTestFile() { // Save test result std::ofstream out("tests/cpu/wiiu/" + mTestFile->name, std::ofstream::out | std::ofstream::binary); cereal::BinaryOutputArchive ar(out); ar(*mTestFile); std::cout << "Wrote file tests/cpu/wiiu/" << mTestFile->name << std::endl; } void sendNextTest() { if (mTestData == mTestFile->tests.end()) { // Save current test file saveTestFile(); // To the next test! ++mTestFile; if (mTestFile == gTestSet.end()) { std::cout << "Tests finished." << std::endl; // Notify client tests have finished TestFinishedPacket pak; mSocket->send(reinterpret_cast(&pak), sizeof(TestFinishedPacket)); } else { // Start next test file mTestData = mTestFile->tests.begin(); } } // Copy test input ExecuteGeneralTestPacket packet; packet.instr = mTestData->instr.value; packet.state.xer = mTestData->input.xer.value; packet.state.cr = mTestData->input.cr.value; packet.state.ctr = mTestData->input.ctr; packet.state.r3 = mTestData->input.gpr[0]; packet.state.r4 = mTestData->input.gpr[1]; packet.state.r5 = mTestData->input.gpr[2]; packet.state.r6 = mTestData->input.gpr[3]; packet.state.fpscr = mTestData->input.fpscr.value; packet.state.f1 = mTestData->input.fr[0]; packet.state.f2 = mTestData->input.fr[1]; packet.state.f3 = mTestData->input.fr[2]; packet.state.f4 = mTestData->input.fr[3]; mSocket->send(reinterpret_cast(&packet), sizeof(ExecuteGeneralTestPacket)); } void handleTestResult(ExecuteGeneralTestPacket *result) { // Sanity check assert(mTestData->instr.value == result->instr.value()); // Copy the output mTestData->output.xer.value = result->state.xer; mTestData->output.cr.value = result->state.cr; mTestData->output.ctr = result->state.ctr; mTestData->output.gpr[0] = result->state.r3; mTestData->output.gpr[1] = result->state.r4; mTestData->output.gpr[2] = result->state.r5; mTestData->output.gpr[3] = result->state.r6; mTestData->output.fpscr.value = static_cast(result->state.fpscr); mTestData->output.fr[0] = result->state.f1; mTestData->output.fr[1] = result->state.f2; mTestData->output.fr[2] = result->state.f3; mTestData->output.fr[3] = result->state.f4; // Start next test mTestData++; sendNextTest(); } void onReceivePacket(PacketHeader *packet) { VersionPacket *version; ExecuteGeneralTestPacket *result; switch (packet->command) { case PacketHeader::Version: // Receive version, begin tests version = reinterpret_cast(packet); std::cout << "Server Version: " << Version << ", Client Version: " << version->version << std::endl; std::cout << "Running tests..." << std::endl; sendNextTest(); break; case PacketHeader::ExecuteGeneralTest: // Receive test result result = reinterpret_cast(packet); handleTestResult(result); break; } } void onSocketError(Socket *socket, int code) { assert(mSocket == socket); std::cout << "Socket error: " << code << std::endl; } void onSocketDisconnect(Socket *socket) { assert(mSocket == socket); std::cout << "Socket Disconnected" << std::endl; } void onSocketReceive(Socket *socket, const char *buffer, size_t size) { PacketHeader *header; assert(mSocket == socket); if (mCurrentPacket.size() == 0) { assert(size == sizeof(PacketHeader)); // Copy packet to buffer mCurrentPacket.resize(size); header = reinterpret_cast(mCurrentPacket.data()); std::memcpy(header, buffer, size); // Read rest of packet auto read = header->size - size; socket->recvFill(read); } else { // Check we have read rest of packet header = reinterpret_cast(mCurrentPacket.data()); auto totalSize = size + sizeof(PacketHeader); assert(totalSize == header->size); // Read rest of packet mCurrentPacket.resize(totalSize); header = reinterpret_cast(mCurrentPacket.data()); std::memcpy(mCurrentPacket.data() + sizeof(PacketHeader), buffer, size); onReceivePacket(header); // Read next packet mCurrentPacket.clear(); socket->recvFill(sizeof(PacketHeader)); } } private: Socket *mSocket; std::vector mCurrentPacket; std::vector::iterator mTestFile; std::vector::iterator mTestData; }; static std::vector> gTestServers; static void loadTests() { fs::create_directories("tests/cpu/wiiu"); // Read all tests for (auto &entry : fs::directory_iterator("tests/cpu/input")) { std::ifstream file { entry.path().string(), std::ifstream::in | std::ifstream::binary }; gTestSet.emplace_back(); auto &test = gTestSet.back(); test.name = entry.path().filename().string(); // Parse cereal data cereal::BinaryInputArchive input(file); input(test); } } int main(int argc, char **argv) { WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData); loadTests(); NetworkThread thread; auto socket = new Socket(); auto ip = "0.0.0.0"; auto port = "8008"; // On socket error socket->addErrorListener([](Socket *socket, int code) { std::cout << "Listen Socket Error: " << code << std::endl; }); socket->addDisconnectListener([](Socket *socket) { std::cout << "Listen Socket Disconnected" << std::endl; }); // On socket connected, accept pls socket->addAcceptListener([&thread](Socket *socket) { auto newSock = socket->accept(); if (!newSock) { std::cout << "Failed to accept new connection" << std::endl; return; } else { std::cout << "New Connection Accepted" << std::endl; } gTestServers.emplace_back(new TestServer(socket)); thread.addSocket(newSock); }); // Start server if (!socket->listen(ip, port)) { std::cout << "Error starting connect!" << std::endl; return 0; } // Run network thread in main thread std::cout << "Listening on " << ip << ":" << port << std::endl; thread.addSocket(socket); thread.start(); WSACleanup(); return 0; }