From cac11f55ee7e5e812d087a3c11ffd14641f54db6 Mon Sep 17 00:00:00 2001 From: ergo720 <45463469+ergo720@users.noreply.github.com> Date: Tue, 20 Aug 2024 01:59:59 +0200 Subject: [PATCH] Implemented smbus cycles + connected eeprom to smbus --- src/files.cpp | 2 +- src/hw/eeprom.cpp | 86 ++++++++++++++++++-- src/hw/eeprom.hpp | 17 +++- src/hw/machine.hpp | 10 ++- src/hw/smbus.cpp | 193 ++++++++++++++++++++++++++++++++++++++++----- src/hw/smbus.hpp | 29 ++++++- src/io.cpp | 30 +------ src/logger.hpp | 2 + 8 files changed, 311 insertions(+), 58 deletions(-) diff --git a/src/files.cpp b/src/files.cpp index ff496d4..852f0c6 100755 --- a/src/files.cpp +++ b/src/files.cpp @@ -160,12 +160,12 @@ open_file(std::filesystem::path path) std::optional open_file(std::filesystem::path path, std::uintmax_t *size) { + *size = 0; if (auto opt = open_file(path)) { try { *size = std::filesystem::file_size(path); } catch (const std::exception &e) { - *size = 0; logger_en(info, "Failed to determine the file size of path %s, the error was %s", path.string().c_str(), e.what()); } return opt; diff --git a/src/hw/eeprom.cpp b/src/hw/eeprom.cpp index 8327545..4d53d07 100644 --- a/src/hw/eeprom.cpp +++ b/src/hw/eeprom.cpp @@ -3,11 +3,16 @@ // SPDX-FileCopyrightText: 2023 ergo720 #include "eeprom.hpp" +#include "../files.hpp" #include +#include +#include + +#define MODULE_NAME eeprom // This is bunnie's eeprom, except that it stores the encrypted settings unencrypted, because nboxkrnl cannot decrypt them yet -constexpr uint8_t default_eeprom[] = { +static constexpr uint8_t g_default_eeprom[] = { 0xe3, 0x1c, 0x5c, 0x23, 0x6a, 0x58, 0x68, 0x37, 0xb7, 0x12, 0x26, 0x6c, 0x99, 0x11, 0x30, 0xd1, 0xe2, 0x3e, 0x4d, 0x56, 0x00, 0x00, 0x00, 0x00, @@ -41,17 +46,82 @@ constexpr uint8_t default_eeprom[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; +static_assert(sizeof(g_default_eeprom) == 256); + + +std::optional +eeprom::read_byte(uint8_t command) +{ + return m_eeprom[command]; +} +std::optional +eeprom::write_byte(uint8_t command, uint8_t data) +{ + m_eeprom[command] = data; + return 0; +} + +std::optional +eeprom::read_word(uint8_t command) +{ + return m_eeprom[command] | (((uint16_t)m_eeprom[command + 1]) << 8); +} + +std::optional +eeprom::write_word(uint8_t command, uint16_t data) +{ + m_eeprom[command] = data & 0xFF; + m_eeprom[command + 1] = data >> 8; + return 0; +} + +void +eeprom::deinit() +{ + m_fs.seekg(0); + m_fs.write((const char *)m_eeprom, 256); + if (!m_fs.good()) { + logger_en(error, "Failed to flush eeprom file to disk"); + } +} bool -gen_eeprom(std::fstream fs) +eeprom::init(std::filesystem::path eeprom_dir) { - fs.seekg(0, fs.beg); - fs.write((const char *)default_eeprom, sizeof(default_eeprom)); - if (fs.rdstate() != std::ios_base::goodbit) { - fs.clear(); + uintmax_t size; + eeprom_dir = eeprom_dir.remove_filename(); + eeprom_dir /= "eeprom.bin"; + eeprom_dir.make_preferred(); + if (auto opt = open_file(eeprom_dir, &size); !opt) { + if (auto opt = create_file(eeprom_dir); !opt) { + logger_en(error, "Failed to create eeprom file"); + return false; + } + else { + m_fs = std::move(*opt); + m_fs.seekg(0); + m_fs.write((const char *)g_default_eeprom, 256); + if (m_fs.good()) { + std::memcpy(m_eeprom, g_default_eeprom, 256); + return true; + } + logger_en(error, "Failed to update eeprom file"); + return false; + } + } + else { + if (size != 256) { + logger_en(error, "Unexpected eeprom file size (it was %" PRIuMAX ")", size); + return false; + } + m_fs = std::move(*opt); + m_fs.seekg(0); + m_fs.read((char *)m_eeprom, 256); + if (m_fs.good()) { + return true; + } + logger_en(error, "Failed to copy eeprom file to memory"); return false; } - - return true; } diff --git a/src/hw/eeprom.hpp b/src/hw/eeprom.hpp index ea19018..4627c6a 100644 --- a/src/hw/eeprom.hpp +++ b/src/hw/eeprom.hpp @@ -2,7 +2,22 @@ // SPDX-FileCopyrightText: 2023 ergo720 +#include "smbus.hpp" #include +#include -bool gen_eeprom(std::fstream fs); +class eeprom : public smbus_device { +public: + eeprom(log_module module_name) : smbus_device(module_name) {} + bool init(std::filesystem::path eeprom_dir); + void deinit() override; + std::optional read_byte(uint8_t command) override; + std::optional write_byte(uint8_t command, uint8_t data) override; + std::optional read_word(uint8_t command) override; + std::optional write_word(uint8_t command, uint16_t data) override; + +private: + std::fstream m_fs; + uint8_t m_eeprom[256]; +}; diff --git a/src/hw/machine.hpp b/src/hw/machine.hpp index 4f27fd0..84b261a 100644 --- a/src/hw/machine.hpp +++ b/src/hw/machine.hpp @@ -11,6 +11,7 @@ #include "cmos.hpp" #include "pci.hpp" #include "smbus.hpp" +#include "eeprom.hpp" #include "video/vga.hpp" #include "video/gpu/nv2a.hpp" @@ -21,7 +22,7 @@ concept is_cpu_t = std::is_same_v; class machine { public: machine() : m_cpu(this), m_pit(this), m_pic{ {this, 0, "MASTER PIC"}, {this, 1, "SLAVE PIC"} }, m_pci(this), m_cmos(this), m_nv2a(this), - m_vga(this), m_smbus(this) {} + m_vga(this), m_smbus(this), m_eeprom(log_module::eeprom) {} bool init(const init_info_t &init_info) { if (!m_cpu.init(init_info)) { @@ -51,6 +52,9 @@ class machine { if (!m_smbus.init()) { return false; } + if (!m_eeprom.init(init_info.m_nxbx_path)) { + return false; + } return true; } void deinit() @@ -91,6 +95,9 @@ class machine { else if constexpr (std::is_same_v) { return m_smbus; } + else if constexpr (std::is_same_v) { + return m_eeprom; + } else if constexpr (std::is_same_v) { return m_nv2a; } @@ -171,4 +178,5 @@ class machine { nv2a m_nv2a; vga m_vga; smbus m_smbus; + eeprom m_eeprom; }; diff --git a/src/hw/smbus.cpp b/src/hw/smbus.cpp index bc976c2..ffb471b 100644 --- a/src/hw/smbus.cpp +++ b/src/hw/smbus.cpp @@ -3,6 +3,7 @@ // SPDX-FileCopyrightText: 2024 ergo720 #include "machine.hpp" +#include #define MODULE_NAME smbus @@ -10,13 +11,11 @@ #define SMBUS_GS_addr 0xC000 #define SMBUS_GE_addr 0xC002 #define SMBUS_HA_addr 0xC004 -#define SMBUS_HD_addr 0xC006 +#define SMBUS_HD0_addr 0xC006 +#define SMBUS_HD1_addr 0xC007 #define SMBUS_HC_addr 0xC008 -#define SMBUS_GS_idx 0 -#define SMBUS_GE_idx 1 -#define SMBUS_HA_idx 2 -#define SMBUS_HD_idx 3 -#define SMBUS_HC_idx 4 +#define SMBUS_HB_addr 0xC009 +#define SMBUS_REG_off(x) ((x) - SMBUS_GS_addr) #define GS_ABRT_STS (1 << 0) // write one to clear #define GS_COL_STS (1 << 1) // write one to clear @@ -30,6 +29,9 @@ #define GE_HOST_STC (1 << 3) // write - only #define GE_HCYC_EN (1 << 4) #define GE_ABORT (1 << 5) // write - only +#define GE_RW_BYTE 2 +#define GE_RW_WORD 3 +#define GE_RW_BLOCK 5 template @@ -37,10 +39,15 @@ uint8_t smbus::read8(uint32_t addr) { uint8_t value; if (addr == SMBUS_GE_addr) { - value = m_regs[SMBUS_GE_idx] & ~(GE_HOST_STC | GE_ABORT); + value = m_regs[SMBUS_REG_off(addr)] & ~(GE_HOST_STC | GE_ABORT); + } + else if (addr == SMBUS_HB_addr) { + value = m_block_data[m_block_off]; + m_block_off++; + m_block_off &= 0x1F; } else { - value = m_regs[(addr - SMBUS_GS_addr) >> 1]; + value = m_regs[SMBUS_REG_off(addr)]; } if constexpr (log) { @@ -50,6 +57,19 @@ uint8_t smbus::read8(uint32_t addr) return value; } +template +uint16_t smbus::read16(uint32_t addr) +{ + uint16_t value = read8(addr); + value |= ((uint16_t)read8(addr + 1) << 8); + + if constexpr (log) { + log_io_read(); + } + + return value; +} + template void smbus::write8(uint32_t addr, const uint8_t data) { @@ -57,39 +77,45 @@ void smbus::write8(uint32_t addr, const uint8_t data) log_io_write(); } + uint8_t reg_off = SMBUS_REG_off(addr); switch (addr) { case SMBUS_GS_addr: - if ((m_regs[SMBUS_GE_idx] & GE_HCYC_EN) && ((data & GS_CLEAR) & (m_regs[SMBUS_GS_idx] & GS_CLEAR))) { + if ((m_regs[SMBUS_REG_off(SMBUS_GE_addr)] & GE_HCYC_EN) && ((data & GS_CLEAR) & (m_regs[reg_off] & GS_CLEAR))) { m_machine->lower_irq(SMBUS_IRQ_NUM); } for (uint8_t i = 0; i < 8; ++i) { if ((data & GS_CLEAR) & (1 << i)) { - m_regs[SMBUS_GS_idx] &= ~(1 << i); + m_regs[reg_off] &= ~(1 << i); } } break; case SMBUS_GE_addr: - m_regs[SMBUS_GE_idx] |= (data & (GE_CYCTYPE | GE_HCYC_EN)); + m_regs[reg_off] |= (data & (GE_CYCTYPE | GE_HCYC_EN)); if (data & GE_ABORT) { - m_regs[SMBUS_GS_idx] |= GS_ABRT_STS; + m_regs[SMBUS_REG_off(SMBUS_GS_addr)] |= GS_ABRT_STS; m_machine->raise_irq(SMBUS_IRQ_NUM); break; } else if (data & GE_HOST_STC) { start_cycle(); - if (m_regs[SMBUS_GS_idx] & GS_CLEAR) { - m_machine->raise_irq(SMBUS_IRQ_NUM); - } + m_machine->raise_irq(SMBUS_IRQ_NUM); } break; case SMBUS_HA_addr: - case SMBUS_HD_addr: + case SMBUS_HD0_addr: + case SMBUS_HD1_addr: case SMBUS_HC_addr: - m_regs[(addr - SMBUS_GS_addr) >> 1] = data; + m_regs[reg_off] = data; + break; + + case SMBUS_HB_addr: + m_block_data[m_block_off] = data; + m_block_off++; + m_block_off &= 0x1F; break; default: @@ -97,11 +123,135 @@ void smbus::write8(uint32_t addr, const uint8_t data) } } +template +void smbus::write16(uint32_t addr, const uint16_t data) +{ + if constexpr (log) { + log_io_write(); + } + + write8(addr, data & 0xFF); + write8(addr + 1, data >> 8 & 0xFF); +} + void smbus::start_cycle() { - m_regs[SMBUS_GS_idx] |= GS_PRERR_STS; - // TODO + uint8_t hw_addr = m_regs[SMBUS_REG_off(SMBUS_HA_addr)] >> 1; // changes sw to hw address + uint8_t is_read = m_regs[SMBUS_REG_off(SMBUS_HA_addr)] & 1; + uint8_t command = m_regs[SMBUS_REG_off(SMBUS_HC_addr)]; + uint8_t data0 = m_regs[SMBUS_REG_off(SMBUS_HD0_addr)]; + uint8_t data1 = m_regs[SMBUS_REG_off(SMBUS_HD1_addr)]; + + const auto &check_success = [this, is_read](std::optional ret) + { + if constexpr (cycle_type == 0) { + m_regs[SMBUS_REG_off(SMBUS_GS_addr)] |= (ret == std::nullopt ? GS_PRERR_STS : GS_HCYC_STS); + } + else if constexpr ((cycle_type == 1) || (cycle_type == 2)) { + if (ret == std::nullopt) { + m_regs[SMBUS_REG_off(SMBUS_GS_addr)] |= GS_PRERR_STS; + } + else { + if (is_read) { + m_regs[SMBUS_REG_off(SMBUS_HD0_addr)] = *ret; + } + m_regs[SMBUS_REG_off(SMBUS_GS_addr)] |= GS_HCYC_STS; + } + } + else if constexpr ((cycle_type == 3) || cycle_type == 4) { + if (ret == std::nullopt) { + m_regs[SMBUS_REG_off(SMBUS_GS_addr)] |= GS_PRERR_STS; + } + else { + if (is_read) { + m_regs[SMBUS_REG_off(SMBUS_HD0_addr)] = (*ret) & 0xFF; + m_regs[SMBUS_REG_off(SMBUS_HD1_addr)] = (*ret) >> 8; + } + m_regs[SMBUS_REG_off(SMBUS_GS_addr)] |= GS_HCYC_STS; + } + } + else { + nxbx_fatal("Out of range cycle type"); + } + }; + + if (const auto it = m_devs.find(hw_addr); it != m_devs.end()) { + std::optional ret; + + switch (m_regs[SMBUS_REG_off(SMBUS_GE_addr)] & GE_CYCTYPE) + { + case 0: + ret = it->second->quick_command(is_read); + check_success.template operator()<0>(ret); + return; + + case 1: + if (is_read) { + ret = it->second->receive_byte(); + } + else { + ret = it->second->send_byte(command); + } + check_success.template operator()<1>(ret); + return; + + case 2: + if (is_read) { + ret = it->second->read_byte(command); + } + else { + ret = it->second->write_byte(command, data0); + } + check_success.template operator()<2>(ret); + return; + + case 3: + if (is_read) { + ret = it->second->read_word(command); + } + else { + ret = it->second->write_word(command, data0 | ((uint16_t)data1 << 8)); + } + check_success.template operator()<3>(ret); + return; + + case 4: + ret = it->second->process_call(command, data0 | ((uint16_t)data1 << 8)); + check_success.template operator()<4>(ret); + return; + + case 5: + if (is_read) { + uint8_t bytes_to_transfer = data0 > 32 ? 32 : data0; + uint8_t start_off = m_block_off; + for (uint8_t i = 0; i < bytes_to_transfer; ++i) { + if (const auto opt = it->second->read_byte(command + i); opt) { + m_block_data[(start_off + i) & 0x1F] = *opt; + continue; + } + std::memset(m_block_data, 0, 32); + m_regs[SMBUS_REG_off(SMBUS_GS_addr)] |= GS_PRERR_STS; + return; + } + } + else { + uint8_t bytes_to_transfer = data0 > 32 ? 32 : data0; + uint8_t start_off = (m_block_off - bytes_to_transfer) & 0x1F; + for (uint8_t i = 0; i < bytes_to_transfer; ++i) { + if (!it->second->write_byte(command + i, m_block_data[(start_off + i) & 0x1F])) { + std::memset(m_block_data, 0, 32); + m_regs[SMBUS_REG_off(SMBUS_GS_addr)] |= GS_PRERR_STS; + return; + } + } + } + m_regs[SMBUS_REG_off(SMBUS_GS_addr)] |= GS_HCYC_STS; + return; + } + } + + m_regs[SMBUS_REG_off(SMBUS_GS_addr)] |= GS_PRERR_STS; } bool @@ -111,7 +261,9 @@ smbus::update_io(bool is_update) if (!LC86_SUCCESS(mem_init_region_io(m_machine->get(), 0xC000, 16, true, { .fnr8 = log ? cpu_read> : cpu_read>, + .fnr16 = log ? cpu_read> : cpu_read>, .fnw8 = log ? cpu_write> : cpu_write>, + .fnw16 = log ? cpu_write> : cpu_write>, }, this, is_update, is_update))) { logger_en(error, "Failed to update smbus io ports"); @@ -125,6 +277,8 @@ void smbus::reset() { std::fill(&m_regs[0], &m_regs[4], 0); + std::fill(&m_block_data[0], &m_block_data[4], 0); + m_block_off = 0; } bool @@ -134,6 +288,7 @@ smbus::init() return false; } + m_devs[0x54] = &m_machine->get(); // eeprom reset(); return true; } diff --git a/src/hw/smbus.hpp b/src/hw/smbus.hpp index 6c4579f..b38870e 100644 --- a/src/hw/smbus.hpp +++ b/src/hw/smbus.hpp @@ -5,10 +5,30 @@ #pragma once #include +#include +#include +#include "../logger.hpp" class machine; +class smbus_device { +public: + smbus_device(log_module module_name) : m_log_module(module_name) {} + virtual void deinit() = 0; + virtual std::optional quick_command(bool command) { logger(m_log_module, "Unhandled quick command"); return std::nullopt; } + virtual std::optional receive_byte() { logger(m_log_module, "Unhandled receive command"); return std::nullopt; } + virtual std::optional send_byte(uint8_t data) { logger(m_log_module, "Unhandled send command"); return std::nullopt; } + virtual std::optional read_byte(uint8_t command) { logger(m_log_module, "Unhandled read byte command"); return std::nullopt; } + virtual std::optional write_byte(uint8_t command, uint8_t data) { logger(m_log_module, "Unhandled write byte command"); return std::nullopt; } + virtual std::optional read_word(uint8_t command) { logger(m_log_module, "Unhandled read word command"); return std::nullopt; } + virtual std::optional write_word(uint8_t command, uint16_t data) { logger(m_log_module, "Unhandled write word command"); return std::nullopt; } + virtual std::optional process_call(uint8_t command, uint16_t data) { logger(m_log_module, "Unhandled process call command"); return std::nullopt; } + +protected: + log_module m_log_module; +}; + class smbus { public: smbus(machine *machine) : m_machine(machine) {} @@ -18,12 +38,19 @@ class smbus { template uint8_t read8(uint32_t addr); template + uint16_t read16(uint32_t addr); + template void write8(uint32_t addr, const uint8_t data); + template + void write16(uint32_t addr, const uint16_t data); private: bool update_io(bool is_update); void start_cycle(); machine *const m_machine; - uint8_t m_regs[5]; + uint8_t m_regs[16]; + uint8_t m_block_data[32]; + unsigned m_block_off; + std::unordered_map m_devs; }; diff --git a/src/io.cpp b/src/io.cpp index f017d74..2bb175f 100755 --- a/src/io.cpp +++ b/src/io.cpp @@ -22,7 +22,7 @@ // Device number #define DEV_CDROM 0 -#define DEV_EEPROM 1 +#define DEV_UNUSED 1 #define DEV_PARTITION0 2 #define DEV_PARTITION1 3 #define DEV_PARTITION2 4 @@ -35,7 +35,7 @@ // Special internal handles used by the kernel #define CDROM_HANDLE DEV_CDROM -#define EEPROM_HANDLE DEV_EEPROM +#define UNUSED_HANDLE DEV_UNUSED #define PARTITION0_HANDLE DEV_PARTITION0 #define PARTITION1_HANDLE DEV_PARTITION1 #define PARTITION2_HANDLE DEV_PARTITION2 @@ -183,7 +183,6 @@ namespace io { static std::filesystem::path hdd_path; static std::filesystem::path dvd_path; - static std::filesystem::path eeprom_path; static bool @@ -206,10 +205,6 @@ namespace io { } } - if (!lambda((eeprom_path / "eeprom.bin").make_preferred(), EEPROM_HANDLE)) { - return false; - } - for (unsigned i = 0; i < XBOX_NUM_OF_PARTITIONS; ++i) { std::filesystem::path curr_partition_dir = hdd_path / ("Partition" + std::to_string(i) + ".bin"); curr_partition_dir.make_preferred(); @@ -603,9 +598,7 @@ namespace io { init(const init_info_t &init_info, cpu_t *cpu) { lc86cpu = cpu; - std::filesystem::path curr_dir = init_info.m_nxbx_path; - curr_dir = curr_dir.remove_filename(); - std::filesystem::path hdd_dir = curr_dir; + std::filesystem::path hdd_dir = std::filesystem::path(init_info.m_nxbx_path).remove_filename(); hdd_dir /= "Harddisk/"; hdd_dir.make_preferred(); if (!::create_directory(hdd_dir)) { @@ -624,27 +617,11 @@ namespace io { return false; } } - std::filesystem::path eeprom_dir = curr_dir; - eeprom_dir /= "eeprom.bin"; - eeprom_dir.make_preferred(); - if (!file_exists(eeprom_dir)) { - if (auto opt = create_file(eeprom_dir); !opt) { - logger_en(error, "Failed to create eeprom file"); - return false; - } - else { - if (!gen_eeprom(std::move(opt.value()))) { - logger_en(error, "Failed to update eeprom file"); - return false; - } - } - } if (init_info.m_input_type == input_t::xiso) { xbe_name = "default.xbe"; hdd_path = hdd_dir; dvd_path = std::filesystem::path(init_info.m_input_path).make_preferred().remove_filename(); - eeprom_path = eeprom_dir.remove_filename(); xbe_path = "\\Device\\CdRom0\\" + xbe_name; dvd_input_type = input_t::xiso; } @@ -653,7 +630,6 @@ namespace io { xbe_name = util::traits_cast>(local_xbe_path.filename().string()); hdd_path = hdd_dir; dvd_path = local_xbe_path.remove_filename(); - eeprom_path = eeprom_dir.remove_filename(); xbe_path = "\\Device\\CdRom0\\" + xbe_name; dvd_input_type = input_t::xbe; if (dvd_path.string().starts_with(hdd_path.string())) { diff --git a/src/logger.hpp b/src/logger.hpp index a5ee172..591c438 100644 --- a/src/logger.hpp +++ b/src/logger.hpp @@ -54,6 +54,7 @@ enum class log_module : int32_t { pvideo, user, smbus, + eeprom, max, }; @@ -80,6 +81,7 @@ inline constexpr std::array module_to_str = { "NV2A.PVIDEO -> ", "NV2A.USER -> ", "SMBUS -> ", + "EEPROM -> ", }; static_assert(module_to_str.size() == (uint32_t)(log_module::max));