From 05ba0340006e57aaa62d0b962238fa7686df8e06 Mon Sep 17 00:00:00 2001 From: Liav A Date: Fri, 23 Sep 2022 11:50:04 +0300 Subject: [PATCH] Kernel: Introduce the IOWindow class This class is intended to replace all IOAddress usages in the Kernel codebase altogether. The idea is to ensure IO can be done in arch-specific manner that is determined mostly in compile-time, but to still be able to use most of the Kernel code in non-x86 builds. Specific devices that rely on x86-specific IO instructions are already placed in the Arch/x86 directory and are omitted for non-x86 builds. The reason this works so well is the fact that x86 IO space acts in a similar fashion to the traditional memory space being available in most CPU architectures - the x86 IO space is essentially just an array of bytes like the physical memory address space, but requires x86 IO instructions to load and store data. Therefore, many devices allow host software to interact with the hardware registers in both ways, with a noticeable trend even in the modern x86 hardware to move away from the old x86 IO space to exclusively using memory-mapped IO. Therefore, the IOWindow class encapsulates both methods for x86 builds. The idea is to allow PCI devices to be used in either way in x86 builds, so when trying to map an IOWindow on a PCI BAR, the Kernel will try to find the proper method being declared with the PCI BAR flags. For old PCI hardware on non-x86 builds this might turn into a problem as we can't use port mapped IO, so the Kernel will gracefully fail with ENOTSUP error code if that's the case, as there's really nothing we can do within such case. For general IO, the read{8,16,32} and write{8,16,32} methods are available as a convenient API for other places in the Kernel. There are simply no direct 64-bit IO API methods yet, as it's not needed right now and is not considered to be Arch-agnostic too - the x86 IO space doesn't support generating 64 bit cycle on IO bus and instead requires two 2 32-bit accesses. If for whatever reason it appears to be necessary to do IO in such manner, it could probably be added with some neat tricks to do so. It is recommended to use Memory::TypedMapping struct if direct 64 bit IO is actually needed. --- Kernel/Arch/x86/ISABus/IDEController.cpp | 15 +- Kernel/Arch/x86/ISABus/SerialDevice.cpp | 51 ++++ .../Arch/x86/PCI/IDELegacyModeController.cpp | 63 ++-- Kernel/Bus/PCI/API.cpp | 18 ++ Kernel/Bus/PCI/API.h | 1 + Kernel/Bus/PCI/Definitions.h | 7 + Kernel/Bus/USB/UHCI/UHCIController.cpp | 9 +- Kernel/Bus/USB/UHCI/UHCIController.h | 38 +-- Kernel/Bus/USB/USBHub.cpp | 1 + Kernel/Bus/VirtIO/Device.cpp | 77 ++--- Kernel/Bus/VirtIO/Device.h | 44 +-- Kernel/CMakeLists.txt | 2 + Kernel/Devices/Audio/AC97.cpp | 102 ++++--- Kernel/Devices/Audio/AC97.h | 32 +- Kernel/Devices/PCISerialDevice.cpp | 9 +- Kernel/Devices/SerialDevice.cpp | 66 +--- Kernel/Devices/SerialDevice.h | 6 +- Kernel/Firmware/ACPI/Parser.cpp | 7 +- Kernel/Graphics/VMWare/GraphicsAdapter.cpp | 18 +- Kernel/Graphics/VMWare/GraphicsAdapter.h | 6 +- Kernel/IOWindow.cpp | 285 ++++++++++++++++++ Kernel/IOWindow.h | 163 ++++++++++ Kernel/Memory/TypedMapping.h | 6 + Kernel/Net/Intel/E1000ENetworkAdapter.cpp | 21 +- Kernel/Net/Intel/E1000ENetworkAdapter.h | 4 +- Kernel/Net/Intel/E1000NetworkAdapter.cpp | 52 +--- Kernel/Net/Intel/E1000NetworkAdapter.h | 10 +- Kernel/Net/NE2000/NetworkAdapter.cpp | 21 +- Kernel/Net/NE2000/NetworkAdapter.h | 6 +- Kernel/Net/NetworkingManagement.cpp | 1 - Kernel/Net/Realtek/RTL8139NetworkAdapter.cpp | 22 +- Kernel/Net/Realtek/RTL8139NetworkAdapter.h | 6 +- Kernel/Net/Realtek/RTL8168NetworkAdapter.cpp | 25 +- Kernel/Net/Realtek/RTL8168NetworkAdapter.h | 6 +- Kernel/Storage/ATA/GenericIDE/Channel.cpp | 121 ++++---- Kernel/Storage/ATA/GenericIDE/Channel.h | 67 ++-- 36 files changed, 919 insertions(+), 469 deletions(-) create mode 100644 Kernel/Arch/x86/ISABus/SerialDevice.cpp create mode 100644 Kernel/IOWindow.cpp create mode 100644 Kernel/IOWindow.h diff --git a/Kernel/Arch/x86/ISABus/IDEController.cpp b/Kernel/Arch/x86/ISABus/IDEController.cpp index b8b0e17e0d..190c1ddca8 100644 --- a/Kernel/Arch/x86/ISABus/IDEController.cpp +++ b/Kernel/Arch/x86/ISABus/IDEController.cpp @@ -28,10 +28,10 @@ UNMAP_AFTER_INIT ISAIDEController::ISAIDEController() UNMAP_AFTER_INIT void ISAIDEController::initialize_channels() { - auto primary_base_io = IOAddress(0x1F0); - auto primary_control_io = IOAddress(0x3F6); - auto secondary_base_io = IOAddress(0x170); - auto secondary_control_io = IOAddress(0x376); + auto primary_base_io_window = IOWindow::create_for_io_space(IOAddress(0x1F0), 8).release_value_but_fixme_should_propagate_errors(); + auto primary_control_io_window = IOWindow::create_for_io_space(IOAddress(0x3F6), 4).release_value_but_fixme_should_propagate_errors(); + auto secondary_base_io_window = IOWindow::create_for_io_space(IOAddress(0x170), 8).release_value_but_fixme_should_propagate_errors(); + auto secondary_control_io_window = IOWindow::create_for_io_space(IOAddress(0x376), 4).release_value_but_fixme_should_propagate_errors(); auto initialize_and_enumerate = [](IDEChannel& channel) -> void { { @@ -46,11 +46,14 @@ UNMAP_AFTER_INIT void ISAIDEController::initialize_channels() } }; - m_channels.append(IDEChannel::create(*this, { primary_base_io, primary_control_io }, IDEChannel::ChannelType::Primary)); + auto primary_channel_io_window_group = IDEChannel::IOWindowGroup { move(primary_base_io_window), move(primary_control_io_window) }; + auto secondary_channel_io_window_group = IDEChannel::IOWindowGroup { move(secondary_base_io_window), move(secondary_control_io_window) }; + + m_channels.append(IDEChannel::create(*this, move(primary_channel_io_window_group), IDEChannel::ChannelType::Primary)); initialize_and_enumerate(m_channels[0]); m_channels[0].enable_irq(); - m_channels.append(IDEChannel::create(*this, { secondary_base_io, secondary_control_io }, IDEChannel::ChannelType::Secondary)); + m_channels.append(IDEChannel::create(*this, move(secondary_channel_io_window_group), IDEChannel::ChannelType::Secondary)); initialize_and_enumerate(m_channels[1]); m_channels[1].enable_irq(); dbgln("ISA IDE controller detected and initialized"); diff --git a/Kernel/Arch/x86/ISABus/SerialDevice.cpp b/Kernel/Arch/x86/ISABus/SerialDevice.cpp new file mode 100644 index 0000000000..22a50d9a3d --- /dev/null +++ b/Kernel/Arch/x86/ISABus/SerialDevice.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2022, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include + +namespace Kernel { + +#define SERIAL_COM1_ADDR 0x3F8 +#define SERIAL_COM2_ADDR 0x2F8 +#define SERIAL_COM3_ADDR 0x3E8 +#define SERIAL_COM4_ADDR 0x2E8 + +UNMAP_AFTER_INIT NonnullLockRefPtr SerialDevice::must_create(size_t com_number) +{ + // FIXME: This way of blindly doing release_value is really not a good thing, find + // a way to propagate errors back. + LockRefPtr serial_device; + switch (com_number) { + case 0: { + auto io_window = IOWindow::create_for_io_space(IOAddress(SERIAL_COM1_ADDR), 16).release_value_but_fixme_should_propagate_errors(); + serial_device = DeviceManagement::try_create_device(move(io_window), 64).release_value(); + break; + } + case 1: { + auto io_window = IOWindow::create_for_io_space(IOAddress(SERIAL_COM2_ADDR), 16).release_value_but_fixme_should_propagate_errors(); + serial_device = DeviceManagement::try_create_device(move(io_window), 65).release_value(); + break; + } + case 2: { + auto io_window = IOWindow::create_for_io_space(IOAddress(SERIAL_COM3_ADDR), 16).release_value_but_fixme_should_propagate_errors(); + serial_device = DeviceManagement::try_create_device(move(io_window), 66).release_value(); + break; + } + case 3: { + auto io_window = IOWindow::create_for_io_space(IOAddress(SERIAL_COM4_ADDR), 16).release_value_but_fixme_should_propagate_errors(); + serial_device = DeviceManagement::try_create_device(move(io_window), 67).release_value(); + break; + } + default: + break; + } + return serial_device.release_nonnull(); +} + +} diff --git a/Kernel/Arch/x86/PCI/IDELegacyModeController.cpp b/Kernel/Arch/x86/PCI/IDELegacyModeController.cpp index 2ac3dc0c06..5f445e9682 100644 --- a/Kernel/Arch/x86/PCI/IDELegacyModeController.cpp +++ b/Kernel/Arch/x86/PCI/IDELegacyModeController.cpp @@ -80,12 +80,12 @@ static char const* detect_controller_type(u8 programming_value) UNMAP_AFTER_INIT void PCIIDELegacyModeController::initialize(bool force_pio) { - auto bus_master_base = IOAddress(PCI::get_BAR4(pci_address()) & (~1)); - dbgln("IDE controller @ {}: bus master base was set to {}", pci_address(), bus_master_base); dbgln("IDE controller @ {}: interrupt line was set to {}", pci_address(), m_interrupt_line.value()); dbgln("IDE controller @ {}: {}", pci_address(), detect_controller_type(m_prog_if.value())); - dbgln("IDE controller @ {}: primary channel DMA capable? {}", pci_address(), ((bus_master_base.offset(2).in() >> 5) & 0b11)); - dbgln("IDE controller @ {}: secondary channel DMA capable? {}", pci_address(), ((bus_master_base.offset(2 + 8).in() >> 5) & 0b11)); + { + auto bus_master_base = IOAddress(PCI::get_BAR4(pci_address()) & (~1)); + dbgln("IDE controller @ {}: bus master base was set to {}", pci_address(), bus_master_base); + } auto initialize_and_enumerate = [&force_pio](IDEChannel& channel) -> void { { @@ -103,20 +103,40 @@ UNMAP_AFTER_INIT void PCIIDELegacyModeController::initialize(bool force_pio) if (!is_bus_master_capable()) force_pio = true; - auto bar0 = PCI::get_BAR0(pci_address()); - auto bar1 = PCI::get_BAR1(pci_address()); - auto bar2 = PCI::get_BAR2(pci_address()); - auto bar3 = PCI::get_BAR3(pci_address()); + OwnPtr primary_base_io_window; + OwnPtr primary_control_io_window; + if (!is_pci_native_mode_enabled_on_primary_channel()) { + primary_base_io_window = IOWindow::create_for_io_space(IOAddress(0x1F0), 8).release_value_but_fixme_should_propagate_errors(); + primary_control_io_window = IOWindow::create_for_io_space(IOAddress(0x3F6), 4).release_value_but_fixme_should_propagate_errors(); + } else { + auto primary_base_io_window = IOWindow::create_for_pci_device_bar(pci_address(), PCI::HeaderType0BaseRegister::BAR0).release_value_but_fixme_should_propagate_errors(); + auto pci_primary_control_io_window = IOWindow::create_for_pci_device_bar(pci_address(), PCI::HeaderType0BaseRegister::BAR1).release_value_but_fixme_should_propagate_errors(); + // Note: the PCI IDE specification says we should access the IO address with an offset of 2 + // on native PCI IDE controllers. + primary_control_io_window = pci_primary_control_io_window->create_from_io_window_with_offset(2, 4).release_value_but_fixme_should_propagate_errors(); + } - auto primary_base_io = (bar0 == 0x1 || bar0 == 0) ? IOAddress(0x1F0) : IOAddress(bar0 & (~1)); - // Note: the PCI IDE specification says we should access the IO address with an offset of 2 - // on native PCI IDE controllers. - auto primary_control_io = (bar1 == 0x1 || bar1 == 0) ? IOAddress(0x3F6) : IOAddress((bar1 & (~1)) | 2); + VERIFY(primary_base_io_window); + VERIFY(primary_control_io_window); - auto secondary_base_io = (bar2 == 0x1 || bar2 == 0) ? IOAddress(0x170) : IOAddress(bar2 & (~1)); - // Note: the PCI IDE specification says we should access the IO address with an offset of 2 - // on native PCI IDE controllers. - auto secondary_control_io = (bar3 == 0x1 || bar3 == 0) ? IOAddress(0x376) : IOAddress((bar3 & (~1)) | 2); + OwnPtr secondary_base_io_window; + OwnPtr secondary_control_io_window; + + if (!is_pci_native_mode_enabled_on_primary_channel()) { + secondary_base_io_window = IOWindow::create_for_io_space(IOAddress(0x170), 8).release_value_but_fixme_should_propagate_errors(); + secondary_control_io_window = IOWindow::create_for_io_space(IOAddress(0x376), 4).release_value_but_fixme_should_propagate_errors(); + } else { + secondary_base_io_window = IOWindow::create_for_pci_device_bar(pci_address(), PCI::HeaderType0BaseRegister::BAR2).release_value_but_fixme_should_propagate_errors(); + auto pci_secondary_control_io_window = IOWindow::create_for_pci_device_bar(pci_address(), PCI::HeaderType0BaseRegister::BAR3).release_value_but_fixme_should_propagate_errors(); + // Note: the PCI IDE specification says we should access the IO address with an offset of 2 + // on native PCI IDE controllers. + secondary_control_io_window = pci_secondary_control_io_window->create_from_io_window_with_offset(2, 4).release_value_but_fixme_should_propagate_errors(); + } + VERIFY(secondary_base_io_window); + VERIFY(secondary_control_io_window); + + auto primary_bus_master_io = IOWindow::create_for_pci_device_bar(pci_address(), PCI::HeaderType0BaseRegister::BAR4, 16).release_value_but_fixme_should_propagate_errors(); + auto secondary_bus_master_io = primary_bus_master_io->create_from_io_window_with_offset(8).release_value_but_fixme_should_propagate_errors(); // FIXME: On IOAPIC based system, this value might be completely wrong // On QEMU for example, it should be "u8 irq_line = 22;" to actually work. @@ -126,18 +146,21 @@ UNMAP_AFTER_INIT void PCIIDELegacyModeController::initialize(bool force_pio) VERIFY(irq_line != 0); } + auto primary_channel_io_window_group = IDEChannel::IOWindowGroup { primary_base_io_window.release_nonnull(), primary_control_io_window.release_nonnull(), move(primary_bus_master_io) }; + auto secondary_channel_io_window_group = IDEChannel::IOWindowGroup { secondary_base_io_window.release_nonnull(), secondary_control_io_window.release_nonnull(), move(secondary_bus_master_io) }; + if (is_pci_native_mode_enabled_on_primary_channel()) { - m_channels.append(IDEChannel::create(*this, irq_line, { primary_base_io, primary_control_io, bus_master_base }, IDEChannel::ChannelType::Primary)); + m_channels.append(IDEChannel::create(*this, irq_line, move(primary_channel_io_window_group), IDEChannel::ChannelType::Primary)); } else { - m_channels.append(IDEChannel::create(*this, { primary_base_io, primary_control_io, bus_master_base }, IDEChannel::ChannelType::Primary)); + m_channels.append(IDEChannel::create(*this, move(primary_channel_io_window_group), IDEChannel::ChannelType::Primary)); } initialize_and_enumerate(m_channels[0]); m_channels[0].enable_irq(); if (is_pci_native_mode_enabled_on_secondary_channel()) { - m_channels.append(IDEChannel::create(*this, irq_line, { secondary_base_io, secondary_control_io, bus_master_base.offset(8) }, IDEChannel::ChannelType::Secondary)); + m_channels.append(IDEChannel::create(*this, irq_line, move(secondary_channel_io_window_group), IDEChannel::ChannelType::Secondary)); } else { - m_channels.append(IDEChannel::create(*this, { secondary_base_io, secondary_control_io, bus_master_base.offset(8) }, IDEChannel::ChannelType::Secondary)); + m_channels.append(IDEChannel::create(*this, move(secondary_channel_io_window_group), IDEChannel::ChannelType::Secondary)); } initialize_and_enumerate(m_channels[1]); m_channels[1].enable_irq(); diff --git a/Kernel/Bus/PCI/API.cpp b/Kernel/Bus/PCI/API.cpp index bdc308b0b9..1539c5960f 100644 --- a/Kernel/Bus/PCI/API.cpp +++ b/Kernel/Bus/PCI/API.cpp @@ -115,6 +115,24 @@ u32 get_BAR(Address address, HeaderType0BaseRegister pci_bar) } } +BARSpaceType get_BAR_space_type(u32 pci_bar_value) +{ + // Note: For IO space, bit 0 is set to 1. + if (pci_bar_value & (1 << 0)) + return BARSpaceType::IOSpace; + auto memory_space_type = (pci_bar_value >> 1) & 0b11; + switch (memory_space_type) { + case 0: + return BARSpaceType::Memory32BitSpace; + case 1: + return BARSpaceType::Memory16BitSpace; + case 2: + return BARSpaceType::Memory64BitSpace; + default: + VERIFY_NOT_REACHED(); + } +} + void enable_bus_mastering(Address address) { auto value = read16(address, PCI::RegisterOffset::COMMAND); diff --git a/Kernel/Bus/PCI/API.h b/Kernel/Bus/PCI/API.h index 92152cb171..545942deb6 100644 --- a/Kernel/Bus/PCI/API.h +++ b/Kernel/Bus/PCI/API.h @@ -33,6 +33,7 @@ u32 get_BAR4(Address); u32 get_BAR5(Address); u32 get_BAR(Address address, HeaderType0BaseRegister); size_t get_BAR_space_size(Address, HeaderType0BaseRegister); +BARSpaceType get_BAR_space_type(u32 pci_bar_value); void enable_bus_mastering(Address); void disable_bus_mastering(Address); void enable_io_space(Address); diff --git a/Kernel/Bus/PCI/Definitions.h b/Kernel/Bus/PCI/Definitions.h index 63f3eae5da..9a2437ed18 100644 --- a/Kernel/Bus/PCI/Definitions.h +++ b/Kernel/Bus/PCI/Definitions.h @@ -30,6 +30,13 @@ enum class HeaderType0BaseRegister { BAR5, }; +enum class BARSpaceType { + IOSpace, + Memory16BitSpace, + Memory32BitSpace, + Memory64BitSpace, +}; + enum class RegisterOffset { VENDOR_ID = 0x00, // word DEVICE_ID = 0x02, // word diff --git a/Kernel/Bus/USB/UHCI/UHCIController.cpp b/Kernel/Bus/USB/UHCI/UHCIController.cpp index 7fed7817b7..bb425e4743 100644 --- a/Kernel/Bus/USB/UHCI/UHCIController.cpp +++ b/Kernel/Bus/USB/UHCI/UHCIController.cpp @@ -66,7 +66,8 @@ static constexpr u16 UHCI_NUMBER_OF_FRAMES = 1024; ErrorOr> UHCIController::try_to_initialize(PCI::DeviceIdentifier const& pci_device_identifier) { // NOTE: This assumes that address is pointing to a valid UHCI controller. - auto controller = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) UHCIController(pci_device_identifier))); + auto registers_io_window = TRY(IOWindow::create_for_pci_device_bar(pci_device_identifier, PCI::HeaderType0BaseRegister::BAR4)); + auto controller = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) UHCIController(pci_device_identifier, move(registers_io_window)))); TRY(controller->initialize()); return controller; } @@ -74,7 +75,7 @@ ErrorOr> UHCIController::try_to_initialize(PCI ErrorOr UHCIController::initialize() { dmesgln("UHCI: Controller found {} @ {}", PCI::get_hardware_id(pci_address()), pci_address()); - dmesgln("UHCI: I/O base {}", m_io_base); + dmesgln("UHCI: I/O base {}", m_registers_io_window); dmesgln("UHCI: Interrupt line: {}", interrupt_number()); TRY(spawn_port_process()); @@ -83,10 +84,10 @@ ErrorOr UHCIController::initialize() return start(); } -UNMAP_AFTER_INIT UHCIController::UHCIController(PCI::DeviceIdentifier const& pci_device_identifier) +UNMAP_AFTER_INIT UHCIController::UHCIController(PCI::DeviceIdentifier const& pci_device_identifier, NonnullOwnPtr registers_io_window) : PCI::Device(pci_device_identifier.address()) , IRQHandler(pci_device_identifier.interrupt_line().value()) - , m_io_base(PCI::get_BAR4(pci_address()) & ~1) + , m_registers_io_window(move(registers_io_window)) , m_schedule_lock(LockRank::None) { } diff --git a/Kernel/Bus/USB/UHCI/UHCIController.h b/Kernel/Bus/USB/UHCI/UHCIController.h index 6f1eedef51..882e54bffd 100644 --- a/Kernel/Bus/USB/UHCI/UHCIController.h +++ b/Kernel/Bus/USB/UHCI/UHCIController.h @@ -10,12 +10,12 @@ #include #include #include -#include #include #include #include #include #include +#include #include #include #include @@ -54,25 +54,25 @@ public: ErrorOr clear_port_feature(Badge, u8, HubFeatureSelector); private: - explicit UHCIController(PCI::DeviceIdentifier const& pci_device_identifier); + UHCIController(PCI::DeviceIdentifier const& pci_device_identifier, NonnullOwnPtr registers_io_window); - u16 read_usbcmd() { return m_io_base.offset(0).in(); } - u16 read_usbsts() { return m_io_base.offset(0x2).in(); } - u16 read_usbintr() { return m_io_base.offset(0x4).in(); } - u16 read_frnum() { return m_io_base.offset(0x6).in(); } - u32 read_flbaseadd() { return m_io_base.offset(0x8).in(); } - u8 read_sofmod() { return m_io_base.offset(0xc).in(); } - u16 read_portsc1() { return m_io_base.offset(0x10).in(); } - u16 read_portsc2() { return m_io_base.offset(0x12).in(); } + u16 read_usbcmd() { return m_registers_io_window->read16(0); } + u16 read_usbsts() { return m_registers_io_window->read16(0x2); } + u16 read_usbintr() { return m_registers_io_window->read16(0x4); } + u16 read_frnum() { return m_registers_io_window->read16(0x6); } + u32 read_flbaseadd() { return m_registers_io_window->read32(0x8); } + u8 read_sofmod() { return m_registers_io_window->read8(0xc); } + u16 read_portsc1() { return m_registers_io_window->read16(0x10); } + u16 read_portsc2() { return m_registers_io_window->read16(0x12); } - void write_usbcmd(u16 value) { m_io_base.offset(0).out(value); } - void write_usbsts(u16 value) { m_io_base.offset(0x2).out(value); } - void write_usbintr(u16 value) { m_io_base.offset(0x4).out(value); } - void write_frnum(u16 value) { m_io_base.offset(0x6).out(value); } - void write_flbaseadd(u32 value) { m_io_base.offset(0x8).out(value); } - void write_sofmod(u8 value) { m_io_base.offset(0xc).out(value); } - void write_portsc1(u16 value) { m_io_base.offset(0x10).out(value); } - void write_portsc2(u16 value) { m_io_base.offset(0x12).out(value); } + void write_usbcmd(u16 value) { m_registers_io_window->write16(0, value); } + void write_usbsts(u16 value) { m_registers_io_window->write16(0x2, value); } + void write_usbintr(u16 value) { m_registers_io_window->write16(0x4, value); } + void write_frnum(u16 value) { m_registers_io_window->write16(0x6, value); } + void write_flbaseadd(u32 value) { m_registers_io_window->write32(0x8, value); } + void write_sofmod(u8 value) { m_registers_io_window->write8(0xc, value); } + void write_portsc1(u16 value) { m_registers_io_window->write16(0x10, value); } + void write_portsc2(u16 value) { m_registers_io_window->write16(0x12, value); } virtual bool handle_irq(RegisterState const&) override; @@ -93,7 +93,7 @@ private: void reset_port(u8); - IOAddress m_io_base; + NonnullOwnPtr m_registers_io_window; Spinlock m_schedule_lock; diff --git a/Kernel/Bus/USB/USBHub.cpp b/Kernel/Bus/USB/USBHub.cpp index ba3aff0237..bb80647ed2 100644 --- a/Kernel/Bus/USB/USBHub.cpp +++ b/Kernel/Bus/USB/USBHub.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include namespace Kernel::USB { diff --git a/Kernel/Bus/VirtIO/Device.cpp b/Kernel/Bus/VirtIO/Device.cpp index 1b3b3f8f8f..722b6098c1 100644 --- a/Kernel/Bus/VirtIO/Device.cpp +++ b/Kernel/Bus/VirtIO/Device.cpp @@ -90,8 +90,6 @@ UNMAP_AFTER_INIT void Device::initialize() { auto address = pci_address(); enable_bus_mastering(pci_address()); - PCI::enable_interrupt_line(pci_address()); - enable_irq(); auto capabilities = PCI::get_device_identifier(address).capabilities(); for (auto& capability : capabilities) { @@ -128,27 +126,23 @@ UNMAP_AFTER_INIT void Device::initialize() if (m_use_mmio) { for (auto& cfg : m_configs) { - auto& mapping = m_mmio[cfg.bar]; - mapping.size = PCI::get_BAR_space_size(pci_address(), static_cast(cfg.bar)); - if (!mapping.base && mapping.size) { - auto region_size_or_error = Memory::page_round_up(mapping.size); - if (region_size_or_error.is_error()) { - dbgln_if(VIRTIO_DEBUG, "{}: Failed to round up size={} to pages", m_class_name, mapping.size); - continue; - } - auto region_or_error = MM.allocate_kernel_region(PhysicalAddress(page_base_of(PCI::get_BAR(pci_address(), static_cast(cfg.bar)))), region_size_or_error.value(), "VirtIO MMIO"sv, Memory::Region::Access::ReadWrite, Memory::Region::Cacheable::No); - if (region_or_error.is_error()) { - dbgln_if(VIRTIO_DEBUG, "{}: Failed to map bar {} - (size={}) {}", m_class_name, cfg.bar, mapping.size, region_or_error.error()); - } else { - mapping.base = region_or_error.release_value(); - } - } + auto mapping_io_window = IOWindow::create_for_pci_device_bar(pci_address(), static_cast(cfg.bar)).release_value_but_fixme_should_propagate_errors(); + m_register_bases[cfg.bar] = move(mapping_io_window); } m_common_cfg = get_config(ConfigurationType::Common, 0); m_notify_cfg = get_config(ConfigurationType::Notify, 0); m_isr_cfg = get_config(ConfigurationType::ISR, 0); + } else { + auto mapping_io_window = IOWindow::create_for_pci_device_bar(pci_address(), PCI::HeaderType0BaseRegister::BAR0).release_value_but_fixme_should_propagate_errors(); + m_register_bases[0] = move(mapping_io_window); } + // Note: We enable interrupts at least after the m_register_bases[0] ptr is + // assigned with an IOWindow, to ensure that in case of getting an interrupt + // we can access registers from that IO window range. + PCI::enable_interrupt_line(pci_address()); + enable_irq(); + reset_device(); set_status_bit(DEVICE_STATUS_ACKNOWLEDGE); @@ -158,66 +152,67 @@ UNMAP_AFTER_INIT void Device::initialize() UNMAP_AFTER_INIT VirtIO::Device::Device(PCI::DeviceIdentifier const& device_identifier) : PCI::Device(device_identifier.address()) , IRQHandler(device_identifier.interrupt_line().value()) - , m_io_base(IOAddress(PCI::get_BAR0(pci_address()) & ~1)) , m_class_name(VirtIO::determine_device_class(device_identifier)) { dbgln("{}: Found @ {}", m_class_name, pci_address()); } -auto Device::mapping_for_bar(u8 bar) -> MappedMMIO& -{ - VERIFY(m_use_mmio); - return m_mmio[bar]; -} - void Device::notify_queue(u16 queue_index) { dbgln_if(VIRTIO_DEBUG, "{}: notifying about queue change at idx: {}", m_class_name, queue_index); if (!m_notify_cfg) - out(REG_QUEUE_NOTIFY, queue_index); + base_io_window().write16(REG_QUEUE_NOTIFY, queue_index); else config_write16(*m_notify_cfg, get_queue(queue_index).notify_offset() * m_notify_multiplier, queue_index); } +auto Device::mapping_for_bar(u8 bar) -> IOWindow& +{ + VERIFY(m_use_mmio); + VERIFY(m_register_bases[bar]); + return *m_register_bases[bar]; +} + u8 Device::config_read8(Configuration const& config, u32 offset) { - return mapping_for_bar(config.bar).read(config.offset + offset); + return mapping_for_bar(config.bar).read8(config.offset + offset); } u16 Device::config_read16(Configuration const& config, u32 offset) { - return mapping_for_bar(config.bar).read(config.offset + offset); + return mapping_for_bar(config.bar).read16(config.offset + offset); } u32 Device::config_read32(Configuration const& config, u32 offset) { - return mapping_for_bar(config.bar).read(config.offset + offset); + return mapping_for_bar(config.bar).read32(config.offset + offset); } void Device::config_write8(Configuration const& config, u32 offset, u8 value) { - mapping_for_bar(config.bar).write(config.offset + offset, value); + mapping_for_bar(config.bar).write8(config.offset + offset, value); } void Device::config_write16(Configuration const& config, u32 offset, u16 value) { - mapping_for_bar(config.bar).write(config.offset + offset, value); + mapping_for_bar(config.bar).write16(config.offset + offset, value); } void Device::config_write32(Configuration const& config, u32 offset, u32 value) { - mapping_for_bar(config.bar).write(config.offset + offset, value); + mapping_for_bar(config.bar).write32(config.offset + offset, value); } void Device::config_write64(Configuration const& config, u32 offset, u64 value) { - mapping_for_bar(config.bar).write(config.offset + offset, value); + mapping_for_bar(config.bar).write32(config.offset + offset, (u32)(value & 0xFFFFFFFF)); + mapping_for_bar(config.bar).write32(config.offset + offset + 4, (u32)(value >> 32)); } u8 Device::read_status_bits() { if (!m_common_cfg) - return in(REG_DEVICE_STATUS); + return base_io_window().read8(REG_DEVICE_STATUS); return config_read8(*m_common_cfg, COMMON_CFG_DEVICE_STATUS); } @@ -225,7 +220,7 @@ void Device::mask_status_bits(u8 status_mask) { m_status &= status_mask; if (!m_common_cfg) - out(REG_DEVICE_STATUS, m_status); + base_io_window().write8(REG_DEVICE_STATUS, m_status); else config_write8(*m_common_cfg, COMMON_CFG_DEVICE_STATUS, m_status); } @@ -234,7 +229,7 @@ void Device::set_status_bit(u8 status_bit) { m_status |= status_bit; if (!m_common_cfg) - out(REG_DEVICE_STATUS, m_status); + base_io_window().write8(REG_DEVICE_STATUS, m_status); else config_write8(*m_common_cfg, COMMON_CFG_DEVICE_STATUS, m_status); } @@ -242,7 +237,7 @@ void Device::set_status_bit(u8 status_bit) u64 Device::get_device_features() { if (!m_common_cfg) - return in(REG_DEVICE_FEATURES); + return base_io_window().read32(REG_DEVICE_FEATURES); config_write32(*m_common_cfg, COMMON_CFG_DEVICE_FEATURE_SELECT, 0); auto lower_bits = config_read32(*m_common_cfg, COMMON_CFG_DEVICE_FEATURE); config_write32(*m_common_cfg, COMMON_CFG_DEVICE_FEATURE_SELECT, 1); @@ -250,6 +245,12 @@ u64 Device::get_device_features() return upper_bits | lower_bits; } +IOWindow& Device::base_io_window() +{ + VERIFY(m_register_bases[0]); + return *m_register_bases[0]; +} + bool Device::accept_device_features(u64 device_features, u64 accepted_features) { VERIFY(!m_did_accept_features); @@ -277,7 +278,7 @@ bool Device::accept_device_features(u64 device_features, u64 accepted_features) dbgln_if(VIRTIO_DEBUG, "{}: Accepted features: {}", m_class_name, accepted_features); if (!m_common_cfg) { - out(REG_GUEST_FEATURES, accepted_features); + base_io_window().write32(REG_GUEST_FEATURES, accepted_features); } else { config_write32(*m_common_cfg, COMMON_CFG_DRIVER_FEATURE_SELECT, 0); config_write32(*m_common_cfg, COMMON_CFG_DRIVER_FEATURE, accepted_features); @@ -399,7 +400,7 @@ void Device::finish_init() u8 Device::isr_status() { if (!m_isr_cfg) - return in(REG_ISR_STATUS); + return base_io_window().read8(REG_ISR_STATUS); return config_read8(*m_isr_cfg, 0); } diff --git a/Kernel/Bus/VirtIO/Device.h b/Kernel/Bus/VirtIO/Device.h index 3c8fe360bc..e185f7a171 100644 --- a/Kernel/Bus/VirtIO/Device.h +++ b/Kernel/Bus/VirtIO/Device.h @@ -7,10 +7,10 @@ #pragma once #include -#include #include #include #include +#include #include #include @@ -94,30 +94,6 @@ public: protected: virtual StringView class_name() const { return "VirtIO::Device"sv; } explicit Device(PCI::DeviceIdentifier const&); - struct MappedMMIO { - OwnPtr base; - size_t size { 0 }; - - template - T read(u32 offset) const - { - if (!base) - return 0; - VERIFY(size >= sizeof(T)); - VERIFY(offset + sizeof(T) <= size); - return *(volatile T*)(base->vaddr().offset(offset).get()); - } - - template - void write(u32 offset, T value) - { - if (!base) - return; - VERIFY(size >= sizeof(T)); - VERIFY(offset + sizeof(T) <= size); - *(volatile T*)(base->vaddr().offset(offset).get()) = value; - } - }; Configuration const* get_config(ConfigurationType cfg_type, u32 index = 0) const { @@ -156,7 +132,7 @@ protected: void config_write32(Configuration const&, u32, u32); void config_write64(Configuration const&, u32, u64); - auto mapping_for_bar(u8) -> MappedMMIO&; + auto mapping_for_bar(u8) -> IOWindow&; u8 read_status_bits(); void mask_status_bits(u8 status_mask); @@ -203,18 +179,6 @@ protected: virtual void handle_queue_update(u16 queue_index) = 0; private: - template - void out(u16 address, T value) - { - m_io_base.offset(address).out(value); - } - - template - T in(u16 address) - { - return m_io_base.offset(address).in(); - } - bool accept_device_features(u64 device_features, u64 accepted_features); bool setup_queue(u16 queue_index); @@ -232,8 +196,8 @@ private: Configuration const* m_notify_cfg { nullptr }; // Cached due to high usage Configuration const* m_isr_cfg { nullptr }; // Cached due to high usage - IOAddress m_io_base; - MappedMMIO m_mmio[6]; + IOWindow& base_io_window(); + Array, 6> m_register_bases; StringView const m_class_name; diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index 73661b8f9d..777086cf6f 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -84,6 +84,7 @@ set(KERNEL_SOURCES Graphics/VirtIOGPU/Console.cpp Graphics/VirtIOGPU/GPU3DDevice.cpp Graphics/VirtIOGPU/GraphicsAdapter.cpp + IOWindow.cpp SanCov.cpp Storage/ATA/AHCI/Controller.cpp Storage/ATA/AHCI/Port.cpp @@ -336,6 +337,7 @@ if ("${SERENITY_ARCH}" STREQUAL "i686" OR "${SERENITY_ARCH}" STREQUAL "x86_64") Arch/x86/ISABus/HID/VMWareMouseDevice.cpp Arch/x86/ISABus/I8042Controller.cpp Arch/x86/ISABus/IDEController.cpp + Arch/x86/ISABus/SerialDevice.cpp Arch/x86/PCI/Controller/HostBridge.cpp Arch/x86/PCI/IDELegacyModeController.cpp Arch/x86/PCI/Initializer.cpp diff --git a/Kernel/Devices/Audio/AC97.cpp b/Kernel/Devices/Audio/AC97.cpp index 7b7346979a..5ba2a33bba 100644 --- a/Kernel/Devices/Audio/AC97.cpp +++ b/Kernel/Devices/Audio/AC97.cpp @@ -23,18 +23,24 @@ static constexpr u16 pcm_sample_rate_maximum = 48000; UNMAP_AFTER_INIT ErrorOr> AC97::try_create(PCI::DeviceIdentifier const& pci_device_identifier) { - auto ac97 = adopt_nonnull_lock_ref_or_enomem(new (nothrow) AC97(pci_device_identifier)); + auto mixer_io_window = TRY(IOWindow::create_for_pci_device_bar(pci_device_identifier, PCI::HeaderType0BaseRegister::BAR0)); + auto bus_io_window = TRY(IOWindow::create_for_pci_device_bar(pci_device_identifier, PCI::HeaderType0BaseRegister::BAR1)); + + auto pcm_out_channel_io_window = TRY(bus_io_window->create_from_io_window_with_offset(NativeAudioBusChannel::PCMOutChannel)); + auto pcm_out_channel = TRY(AC97Channel::create_with_parent_pci_device(pci_device_identifier.address(), "PCMOut"sv, move(pcm_out_channel_io_window))); + + auto ac97 = adopt_nonnull_lock_ref_or_enomem(new (nothrow) AC97(pci_device_identifier, move(pcm_out_channel), move(mixer_io_window), move(bus_io_window))); if (!ac97.is_error()) TRY(ac97.value()->initialize()); return ac97; } -UNMAP_AFTER_INIT AC97::AC97(PCI::DeviceIdentifier const& pci_device_identifier) +UNMAP_AFTER_INIT AC97::AC97(PCI::DeviceIdentifier const& pci_device_identifier, NonnullOwnPtr pcm_out_channel, NonnullOwnPtr mixer_io_window, NonnullOwnPtr bus_io_window) : PCI::Device(pci_device_identifier.address()) , IRQHandler(pci_device_identifier.interrupt_line().value()) - , m_io_mixer_base(PCI::get_BAR0(pci_address()) & ~1) - , m_io_bus_base(PCI::get_BAR1(pci_address()) & ~1) - , m_pcm_out_channel(channel("PCMOut"sv, NativeAudioBusChannel::PCMOutChannel)) + , m_mixer_io_window(move(mixer_io_window)) + , m_bus_io_window(move(bus_io_window)) + , m_pcm_out_channel(move(pcm_out_channel)) { } @@ -42,8 +48,7 @@ UNMAP_AFTER_INIT AC97::~AC97() = default; bool AC97::handle_irq(RegisterState const&) { - auto pcm_out_status_register = m_pcm_out_channel.reg(AC97Channel::Register::Status); - auto pcm_out_status = pcm_out_status_register.in(); + auto pcm_out_status = m_pcm_out_channel->io_window().read16(AC97Channel::Register::Status); dbgln_if(AC97_DEBUG, "AC97 @ {}: interrupt received - status: {:#05b}", pci_address(), pcm_out_status); bool is_dma_halted = (pcm_out_status & AudioStatusRegisterFlag::DMAControllerHalted) > 0; @@ -60,11 +65,11 @@ bool AC97::handle_irq(RegisterState const&) pcm_out_status = AudioStatusRegisterFlag::LastValidBufferCompletionInterrupt | AudioStatusRegisterFlag::BufferCompletionInterruptStatus | AudioStatusRegisterFlag::FIFOError; - pcm_out_status_register.out(pcm_out_status); + m_pcm_out_channel->io_window().write16(AC97Channel::Register::Status, pcm_out_status); if (is_dma_halted) { VERIFY(current_equals_last_valid); - m_pcm_out_channel.handle_dma_stopped(); + m_pcm_out_channel->handle_dma_stopped(); } if (!m_irq_queue.is_empty()) @@ -75,34 +80,33 @@ bool AC97::handle_irq(RegisterState const&) UNMAP_AFTER_INIT ErrorOr AC97::initialize() { - dbgln_if(AC97_DEBUG, "AC97 @ {}: mixer base: {:#04x}", pci_address(), m_io_mixer_base.get()); - dbgln_if(AC97_DEBUG, "AC97 @ {}: bus base: {:#04x}", pci_address(), m_io_bus_base.get()); + dbgln_if(AC97_DEBUG, "AC97 @ {}: mixer base: {:#04x}", pci_address(), m_mixer_io_window); + dbgln_if(AC97_DEBUG, "AC97 @ {}: bus base: {:#04x}", pci_address(), m_bus_io_window); // Read out AC'97 codec revision and vendor - auto extended_audio_id = m_io_mixer_base.offset(NativeAudioMixerRegister::ExtendedAudioID).in(); + auto extended_audio_id = m_mixer_io_window->read16(NativeAudioMixerRegister::ExtendedAudioID); m_codec_revision = static_cast(((extended_audio_id & ExtendedAudioMask::Revision) >> 10) & 0b11); dbgln_if(AC97_DEBUG, "AC97 @ {}: codec revision {:#02b}", pci_address(), to_underlying(m_codec_revision)); if (m_codec_revision == AC97Revision::Reserved) return ENOTSUP; // Report vendor / device ID - u32 vendor_id = m_io_mixer_base.offset(NativeAudioMixerRegister::VendorID1).in() << 16 | m_io_mixer_base.offset(NativeAudioMixerRegister::VendorID2).in(); + u32 vendor_id = m_mixer_io_window->read16(NativeAudioMixerRegister::VendorID1) << 16 | m_mixer_io_window->read16(NativeAudioMixerRegister::VendorID2); dbgln("AC97 @ {}: Vendor ID: {:#8x}", pci_address(), vendor_id); // Bus cold reset, enable interrupts enable_pin_based_interrupts(); PCI::enable_bus_mastering(pci_address()); - auto control = m_io_bus_base.offset(NativeAudioBusRegister::GlobalControl).in(); + auto control = m_bus_io_window->read32(NativeAudioBusRegister::GlobalControl); control |= GlobalControlFlag::GPIInterruptEnable; control |= GlobalControlFlag::AC97ColdReset; - m_io_bus_base.offset(NativeAudioBusRegister::GlobalControl).out(control); + m_bus_io_window->write32(NativeAudioBusRegister::GlobalControl, control); // Reset mixer - m_io_mixer_base.offset(NativeAudioMixerRegister::Reset).out(1); + m_mixer_io_window->write16(NativeAudioMixerRegister::Reset, 1); // Enable variable and double rate PCM audio if supported - auto extended_audio_status_control_register = m_io_mixer_base.offset(NativeAudioMixerRegister::ExtendedAudioStatusControl); - auto extended_audio_status = extended_audio_status_control_register.in(); + auto extended_audio_status = m_mixer_io_window->read16(NativeAudioMixerRegister::ExtendedAudioStatusControl); if ((extended_audio_id & ExtendedAudioMask::VariableRatePCMAudio) > 0) { extended_audio_status |= ExtendedAudioStatusControlFlag::VariableRateAudio; m_variable_rate_pcm_supported = true; @@ -113,7 +117,7 @@ UNMAP_AFTER_INIT ErrorOr AC97::initialize() extended_audio_status |= ExtendedAudioStatusControlFlag::DoubleRateAudio; m_double_rate_pcm_enabled = true; } - extended_audio_status_control_register.out(extended_audio_status); + m_mixer_io_window->write16(NativeAudioMixerRegister::ExtendedAudioStatusControl, extended_audio_status); TRY(set_pcm_output_sample_rate(m_variable_rate_pcm_supported ? pcm_default_sample_rate : pcm_fixed_sample_rate)); @@ -121,7 +125,7 @@ UNMAP_AFTER_INIT ErrorOr AC97::initialize() set_master_output_volume(0, 0, Muted::No); set_pcm_output_volume(0, 0, Muted::No); - m_pcm_out_channel.reset(); + m_pcm_out_channel->reset(); enable_irq(); return {}; } @@ -131,7 +135,7 @@ void AC97::set_master_output_volume(u8 left_channel, u8 right_channel, Muted mut u16 volume_value = ((right_channel & 63) << 0) | ((left_channel & 63) << 8) | ((mute == Muted::Yes ? 1 : 0) << 15); - m_io_mixer_base.offset(NativeAudioMixerRegister::SetMasterOutputVolume).out(volume_value); + m_mixer_io_window->write16(NativeAudioMixerRegister::SetMasterOutputVolume, volume_value); } ErrorOr AC97::set_pcm_output_sample_rate(u32 sample_rate) @@ -146,15 +150,14 @@ ErrorOr AC97::set_pcm_output_sample_rate(u32 sample_rate) if (shifted_sample_rate < pcm_sample_rate_minimum || shifted_sample_rate > pcm_sample_rate_maximum) return ENOTSUP; - auto pcm_front_dac_rate_register = m_io_mixer_base.offset(NativeAudioMixerRegister::PCMFrontDACRate); - pcm_front_dac_rate_register.out(shifted_sample_rate); - m_sample_rate = static_cast(pcm_front_dac_rate_register.in()) << double_rate_shift; + m_mixer_io_window->write16(NativeAudioMixerRegister::PCMFrontDACRate, shifted_sample_rate); + m_sample_rate = static_cast(m_mixer_io_window->read16(NativeAudioMixerRegister::PCMFrontDACRate)) << double_rate_shift; dbgln("AC97 @ {}: PCM front DAC rate set to {} Hz", pci_address(), m_sample_rate); // Setting the sample rate stops a running DMA engine, so restart it - if (m_pcm_out_channel.dma_running()) - m_pcm_out_channel.start_dma(); + if (m_pcm_out_channel->dma_running()) + m_pcm_out_channel->start_dma(); return {}; } @@ -164,7 +167,7 @@ void AC97::set_pcm_output_volume(u8 left_channel, u8 right_channel, Muted mute) u16 volume_value = ((right_channel & 31) << 0) | ((left_channel & 31) << 8) | ((mute == Muted::Yes ? 1 : 0) << 15); - m_io_mixer_base.offset(NativeAudioMixerRegister::SetPCMOutputVolume).out(volume_value); + m_mixer_io_window->write16(NativeAudioMixerRegister::SetPCMOutputVolume, volume_value); } LockRefPtr AC97::audio_channel(u32 index) const @@ -226,14 +229,14 @@ ErrorOr AC97::write_single_buffer(UserOrKernelBuffer const& data, size_t o // Block until we can write into an unused buffer cli(); do { - auto pcm_out_status = m_pcm_out_channel.reg(AC97Channel::Register::Status).in(); - auto current_index = m_pcm_out_channel.reg(AC97Channel::Register::CurrentIndexValue).in(); - int last_valid_index = m_pcm_out_channel.reg(AC97Channel::Register::LastValidIndex).in(); + auto pcm_out_status = m_pcm_out_channel->io_window().read16(AC97Channel::Register::Status); + auto current_index = m_pcm_out_channel->io_window().read8(AC97Channel::Register::CurrentIndexValue); + int last_valid_index = m_pcm_out_channel->io_window().read8(AC97Channel::Register::LastValidIndex); auto head_distance = last_valid_index - current_index; if (head_distance < 0) head_distance += buffer_descriptor_list_max_entries; - if (m_pcm_out_channel.dma_running()) + if (m_pcm_out_channel->dma_running()) ++head_distance; // Current index has _passed_ last valid index - move our list index up @@ -248,7 +251,7 @@ ErrorOr AC97::write_single_buffer(UserOrKernelBuffer const& data, size_t o dbgln_if(AC97_DEBUG, "AC97 @ {}: waiting on interrupt - status: {:#05b} CI: {} LVI: {}", pci_address(), pcm_out_status, current_index, last_valid_index); m_irq_queue.wait_forever("AC97"sv); - } while (m_pcm_out_channel.dma_running()); + } while (m_pcm_out_channel->dma_running()); sti(); // Copy data from userspace into one of our buffers @@ -262,10 +265,10 @@ ErrorOr AC97::write_single_buffer(UserOrKernelBuffer const& data, size_t o list_entry->control_and_length = number_of_samples | BufferDescriptorListEntryFlags::InterruptOnCompletion; auto buffer_address = static_cast(m_buffer_descriptor_list->physical_page(0)->paddr().get()); - m_pcm_out_channel.set_last_valid_index(buffer_address, m_buffer_descriptor_list_index); + m_pcm_out_channel->set_last_valid_index(buffer_address, m_buffer_descriptor_list_index); - if (!m_pcm_out_channel.dma_running()) - m_pcm_out_channel.start_dma(); + if (!m_pcm_out_channel->dma_running()) + m_pcm_out_channel->start_dma(); m_output_buffer_page_index = (m_output_buffer_page_index + 1) % m_output_buffer_page_count; m_buffer_descriptor_list_index = (m_buffer_descriptor_list_index + 1) % buffer_descriptor_list_max_entries; @@ -273,25 +276,29 @@ ErrorOr AC97::write_single_buffer(UserOrKernelBuffer const& data, size_t o return {}; } +ErrorOr> AC97::AC97Channel::create_with_parent_pci_device(PCI::Address pci_device_address, StringView name, NonnullOwnPtr channel_io_base) +{ + return adopt_nonnull_own_or_enomem(new (nothrow) AC97::AC97Channel(pci_device_address, name, move(channel_io_base))); +} + void AC97::AC97Channel::handle_dma_stopped() { - dbgln_if(AC97_DEBUG, "AC97 @ {}: channel {}: DMA engine has stopped", m_device.pci_address(), name()); + dbgln_if(AC97_DEBUG, "AC97 @ {}: channel {}: DMA engine has stopped", m_device_pci_address, name()); m_dma_running.with([this](auto& dma_running) { // NOTE: QEMU might send spurious interrupts while we're not running, so we don't want to panic here. if (!dma_running) - dbgln("AC97 @ {}: received DMA interrupt while it wasn't running", m_device.pci_address()); + dbgln("AC97 @ {}: received DMA interrupt while it wasn't running", m_device_pci_address); dma_running = false; }); } void AC97::AC97Channel::reset() { - dbgln_if(AC97_DEBUG, "AC97 @ {}: channel {}: resetting", m_device.pci_address(), name()); + dbgln_if(AC97_DEBUG, "AC97 @ {}: channel {}: resetting", m_device_pci_address, name()); - auto control_register = reg(Register::Control); - control_register.out(AudioControlRegisterFlag::ResetRegisters); + m_channel_io_window->write8(Register::Control, AudioControlRegisterFlag::ResetRegisters); - while ((control_register.in() & AudioControlRegisterFlag::ResetRegisters) > 0) + while ((m_channel_io_window->read8(Register::Control) & AudioControlRegisterFlag::ResetRegisters) > 0) microseconds_delay(50); m_dma_running.with([](auto& dma_running) { @@ -301,22 +308,21 @@ void AC97::AC97Channel::reset() void AC97::AC97Channel::set_last_valid_index(u32 buffer_address, u8 last_valid_index) { - dbgln_if(AC97_DEBUG, "AC97 @ {}: channel {}: setting buffer address: {:#x} LVI: {}", m_device.pci_address(), name(), buffer_address, last_valid_index); + dbgln_if(AC97_DEBUG, "AC97 @ {}: channel {}: setting buffer address: {:#x} LVI: {}", m_device_pci_address, name(), buffer_address, last_valid_index); - reg(Register::BufferDescriptorListBaseAddress).out(buffer_address); - reg(Register::LastValidIndex).out(last_valid_index); + m_channel_io_window->write32(Register::BufferDescriptorListBaseAddress, buffer_address); + m_channel_io_window->write8(Register::LastValidIndex, last_valid_index); } void AC97::AC97Channel::start_dma() { - dbgln_if(AC97_DEBUG, "AC97 @ {}: channel {}: starting DMA engine", m_device.pci_address(), name()); + dbgln_if(AC97_DEBUG, "AC97 @ {}: channel {}: starting DMA engine", m_device_pci_address, name()); - auto control_register = reg(Register::Control); - auto control = control_register.in(); + auto control = m_channel_io_window->read8(Register::Control); control |= AudioControlRegisterFlag::RunPauseBusMaster; control |= AudioControlRegisterFlag::FIFOErrorInterruptEnable; control |= AudioControlRegisterFlag::InterruptOnCompletionEnable; - control_register.out(control); + m_channel_io_window->write8(Register::Control, control); m_dma_running.with([](auto& dma_running) { dma_running = true; diff --git a/Kernel/Devices/Audio/AC97.h b/Kernel/Devices/Audio/AC97.h index 31655ecd45..9ad53a8d18 100644 --- a/Kernel/Devices/Audio/AC97.h +++ b/Kernel/Devices/Audio/AC97.h @@ -7,11 +7,11 @@ #pragma once #include -#include #include #include #include #include +#include #include #include @@ -123,12 +123,7 @@ private: Control = 0x0b, }; - AC97Channel(AC97& device, StringView name, IOAddress channel_base) - : m_channel_base(channel_base) - , m_device(device) - , m_name(name) - { - } + static ErrorOr> create_with_parent_pci_device(PCI::Address pci_device_address, StringView name, NonnullOwnPtr channel_io_base); bool dma_running() const { @@ -136,24 +131,31 @@ private: } void handle_dma_stopped(); StringView name() const { return m_name; } - IOAddress reg(Register reg) const { return m_channel_base.offset(reg); } void reset(); void set_last_valid_index(u32 buffer_address, u8 last_valid_index); void start_dma(); + IOWindow& io_window() { return *m_channel_io_window; } + private: - IOAddress m_channel_base; - AC97& m_device; + AC97Channel(PCI::Address pci_device_address, StringView name, NonnullOwnPtr channel_io_base) + : m_channel_io_window(move(channel_io_base)) + , m_device_pci_address(pci_device_address) + , m_name(name) + { + } + + NonnullOwnPtr m_channel_io_window; + PCI::Address m_device_pci_address; SpinlockProtected m_dma_running { LockRank::None, false }; StringView m_name; }; - explicit AC97(PCI::DeviceIdentifier const&); + AC97(PCI::DeviceIdentifier const&, NonnullOwnPtr pcm_out_channel, NonnullOwnPtr mixer_io_window, NonnullOwnPtr bus_io_window); // ^IRQHandler virtual bool handle_irq(RegisterState const&) override; - AC97Channel channel(StringView name, NativeAudioBusChannel channel) { return AC97Channel(*this, name, m_io_bus_base.offset(channel)); } ErrorOr initialize(); void set_master_output_volume(u8, u8, Muted); ErrorOr set_pcm_output_sample_rate(u32); @@ -171,13 +173,13 @@ private: u8 m_buffer_descriptor_list_index { 0 }; AC97Revision m_codec_revision { AC97Revision::Revision21OrEarlier }; bool m_double_rate_pcm_enabled { false }; - IOAddress m_io_mixer_base; - IOAddress m_io_bus_base; + NonnullOwnPtr m_mixer_io_window; + NonnullOwnPtr m_bus_io_window; WaitQueue m_irq_queue; OwnPtr m_output_buffer; u8 m_output_buffer_page_count { 4 }; u8 m_output_buffer_page_index { 0 }; - AC97Channel m_pcm_out_channel; + NonnullOwnPtr m_pcm_out_channel; u32 m_sample_rate { 0 }; bool m_variable_rate_pcm_supported { false }; LockRefPtr m_audio_channel; diff --git a/Kernel/Devices/PCISerialDevice.cpp b/Kernel/Devices/PCISerialDevice.cpp index 32c5905dd6..c649dc37ab 100644 --- a/Kernel/Devices/PCISerialDevice.cpp +++ b/Kernel/Devices/PCISerialDevice.cpp @@ -6,6 +6,7 @@ #include #include +#include #include namespace Kernel { @@ -20,10 +21,12 @@ UNMAP_AFTER_INIT void PCISerialDevice::detect() if (board_definition.device_id != device_identifier.hardware_id()) continue; - auto bar_base = PCI::get_BAR(device_identifier.address(), static_cast(board_definition.pci_bar)) & ~1; - auto port_base = IOAddress(bar_base + board_definition.first_offset); + auto registers_io_window = IOWindow::create_for_pci_device_bar(device_identifier, static_cast(board_definition.pci_bar)).release_value_but_fixme_should_propagate_errors(); + auto first_offset_registers_io_window = registers_io_window->create_from_io_window_with_offset(board_definition.first_offset).release_value_but_fixme_should_propagate_errors(); + for (size_t i = 0; i < board_definition.port_count; i++) { - auto serial_device = new SerialDevice(port_base.offset(board_definition.port_size * i), current_device_minor++); + auto port_registers_io_window = first_offset_registers_io_window->create_from_io_window_with_offset(board_definition.port_size * i).release_value_but_fixme_should_propagate_errors(); + auto serial_device = new SerialDevice(move(port_registers_io_window), current_device_minor++); if (board_definition.baud_rate != SerialDevice::Baud::Baud38400) // non-default baud serial_device->set_baud(board_definition.baud_rate); diff --git a/Kernel/Devices/SerialDevice.cpp b/Kernel/Devices/SerialDevice.cpp index b32b5a79dc..ea5ac24cf3 100644 --- a/Kernel/Devices/SerialDevice.cpp +++ b/Kernel/Devices/SerialDevice.cpp @@ -5,49 +5,16 @@ * SPDX-License-Identifier: BSD-2-Clause */ -#include #include #include +#include #include namespace Kernel { -#define SERIAL_COM1_ADDR 0x3F8 -#define SERIAL_COM2_ADDR 0x2F8 -#define SERIAL_COM3_ADDR 0x3E8 -#define SERIAL_COM4_ADDR 0x2E8 - -UNMAP_AFTER_INIT NonnullLockRefPtr SerialDevice::must_create(size_t com_number) -{ - // FIXME: This way of blindly doing release_value is really not a good thing, find - // a way to propagate errors back. - LockRefPtr serial_device; - switch (com_number) { - case 0: { - serial_device = DeviceManagement::try_create_device(IOAddress(SERIAL_COM1_ADDR), 64).release_value(); - break; - } - case 1: { - serial_device = DeviceManagement::try_create_device(IOAddress(SERIAL_COM2_ADDR), 65).release_value(); - break; - } - case 2: { - serial_device = DeviceManagement::try_create_device(IOAddress(SERIAL_COM3_ADDR), 66).release_value(); - break; - } - case 3: { - serial_device = DeviceManagement::try_create_device(IOAddress(SERIAL_COM4_ADDR), 67).release_value(); - break; - } - default: - break; - } - return serial_device.release_nonnull(); -} - -UNMAP_AFTER_INIT SerialDevice::SerialDevice(IOAddress base_addr, unsigned minor) +UNMAP_AFTER_INIT SerialDevice::SerialDevice(NonnullOwnPtr registers_io_window, unsigned minor) : CharacterDevice(4, minor) - , m_base_addr(base_addr) + , m_registers_io_window(move(registers_io_window)) { initialize(); } @@ -70,7 +37,7 @@ ErrorOr SerialDevice::read(OpenFileDescription&, u64, UserOrKernelBuffer return buffer.write_buffered<128>(size, [&](Bytes bytes) { for (auto& byte : bytes) - byte = m_base_addr.in(); + byte = m_registers_io_window->read8(0); return bytes.size(); }); } @@ -102,9 +69,9 @@ void SerialDevice::put_char(char ch) ; if (ch == '\n' && !m_last_put_char_was_carriage_return) - m_base_addr.out('\r'); + m_registers_io_window->write8(0, '\r'); - m_base_addr.out(ch); + m_registers_io_window->write8(0, ch); m_last_put_char_was_carriage_return = (ch == '\r'); } @@ -122,24 +89,23 @@ UNMAP_AFTER_INIT void SerialDevice::set_interrupts(bool interrupt_enable) { m_interrupt_enable = interrupt_enable; - m_base_addr.offset(1).out(interrupt_enable); + m_registers_io_window->write8(1, interrupt_enable); } void SerialDevice::set_baud(Baud baud) { m_baud = baud; - m_base_addr.offset(3).out(m_base_addr.offset(3).in() | 0x80); // turn on DLAB - m_base_addr.out(((u8)(baud)) & 0xff); // lower half of divisor - m_base_addr.offset(1).out(((u8)(baud)) >> 2); // upper half of divisor - m_base_addr.offset(3).out(m_base_addr.offset(3).in() & 0x7f); // turn off DLAB + m_registers_io_window->write8(3, m_registers_io_window->read8(3) | 0x80); // turn on DLAB + m_registers_io_window->write8(0, ((u8)(baud)) & 0xff); // lower half of divisor + m_registers_io_window->write8(1, ((u8)(baud)) >> 2); // lower half of divisor + m_registers_io_window->write8(3, m_registers_io_window->read8(3) & 0x7f); // turn off DLAB } void SerialDevice::set_fifo_control(u8 fifo_control) { m_fifo_control = fifo_control; - - m_base_addr.offset(2).out(fifo_control); + m_registers_io_window->write8(2, fifo_control); } void SerialDevice::set_line_control(ParitySelect parity_select, StopBits stop_bits, WordLength word_length) @@ -148,26 +114,26 @@ void SerialDevice::set_line_control(ParitySelect parity_select, StopBits stop_bi m_stop_bits = stop_bits; m_word_length = word_length; - m_base_addr.offset(3).out((m_base_addr.offset(3).in() & ~0x3f) | parity_select | stop_bits | word_length); + m_registers_io_window->write8(3, (m_registers_io_window->read8(3) & ~0x3f) | parity_select | stop_bits | word_length); } void SerialDevice::set_break_enable(bool break_enable) { m_break_enable = break_enable; - m_base_addr.offset(3).out(m_base_addr.offset(3).in() & (break_enable ? 0xff : 0xbf)); + m_registers_io_window->write8(3, m_registers_io_window->read8(3) & (break_enable ? 0xff : 0xbf)); } void SerialDevice::set_modem_control(u8 modem_control) { m_modem_control = modem_control; - m_base_addr.offset(4).out(modem_control); + m_registers_io_window->write8(4, modem_control); } u8 SerialDevice::get_line_status() const { - return m_base_addr.offset(5).in(); + return m_registers_io_window->read8(5); } } diff --git a/Kernel/Devices/SerialDevice.h b/Kernel/Devices/SerialDevice.h index ef724522f5..11448aca61 100644 --- a/Kernel/Devices/SerialDevice.h +++ b/Kernel/Devices/SerialDevice.h @@ -6,8 +6,8 @@ #pragma once -#include #include +#include namespace Kernel { @@ -104,7 +104,7 @@ public: }; private: - SerialDevice(IOAddress base_addr, unsigned minor); + SerialDevice(NonnullOwnPtr registers_io_window, unsigned minor); friend class PCISerialDevice; @@ -120,7 +120,7 @@ private: void set_modem_control(u8 modem_control); u8 get_line_status() const; - IOAddress m_base_addr; + mutable NonnullOwnPtr m_registers_io_window; bool m_interrupt_enable { false }; u8 m_fifo_control { 0 }; Baud m_baud { Baud38400 }; diff --git a/Kernel/Firmware/ACPI/Parser.cpp b/Kernel/Firmware/ACPI/Parser.cpp index 0afc5ce03d..b9cd91f41a 100644 --- a/Kernel/Firmware/ACPI/Parser.cpp +++ b/Kernel/Firmware/ACPI/Parser.cpp @@ -7,10 +7,13 @@ */ #include +#include #include #include #include -#include +#if ARCH(I386) || ARCH(X86_64) +# include +#endif #include #include #include @@ -216,6 +219,7 @@ void Parser::access_generic_address(Structures::GenericAddressStructure const& s { switch ((GenericAddressStructure::AddressSpace)structure.address_space) { case GenericAddressStructure::AddressSpace::SystemIO: { +#if ARCH(I386) || ARCH(X86_64) IOAddress address(structure.address); dbgln("ACPI: Sending value {:x} to {}", value, address); switch (structure.access_size) { @@ -236,6 +240,7 @@ void Parser::access_generic_address(Structures::GenericAddressStructure const& s address.out(value, (8 << (structure.access_size - 1))); break; } +#endif return; } case GenericAddressStructure::AddressSpace::SystemMemory: { diff --git a/Kernel/Graphics/VMWare/GraphicsAdapter.cpp b/Kernel/Graphics/VMWare/GraphicsAdapter.cpp index a7a7b19646..ceddafb8ef 100644 --- a/Kernel/Graphics/VMWare/GraphicsAdapter.cpp +++ b/Kernel/Graphics/VMWare/GraphicsAdapter.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -27,29 +28,30 @@ UNMAP_AFTER_INIT LockRefPtr VMWareGraphicsAdapter::try_in // Note: We only support VMWare SVGA II adapter if (id.device_id != 0x0405) return {}; - auto adapter = MUST(adopt_nonnull_lock_ref_or_enomem(new (nothrow) VMWareGraphicsAdapter(pci_device_identifier))); + auto registers_io_window = MUST(IOWindow::create_for_pci_device_bar(pci_device_identifier, PCI::HeaderType0BaseRegister::BAR0)); + auto adapter = MUST(adopt_nonnull_lock_ref_or_enomem(new (nothrow) VMWareGraphicsAdapter(pci_device_identifier, move(registers_io_window)))); MUST(adapter->initialize_adapter()); return adapter; } -UNMAP_AFTER_INIT VMWareGraphicsAdapter::VMWareGraphicsAdapter(PCI::DeviceIdentifier const& pci_device_identifier) +UNMAP_AFTER_INIT VMWareGraphicsAdapter::VMWareGraphicsAdapter(PCI::DeviceIdentifier const& pci_device_identifier, NonnullOwnPtr registers_io_window) : PCI::Device(pci_device_identifier.address()) - , m_io_registers_base(PCI::get_BAR0(pci_device_identifier.address()) & 0xfffffff0) + , m_registers_io_window(move(registers_io_window)) { - dbgln("VMWare SVGA @ {}, {}", pci_device_identifier.address(), m_io_registers_base); + dbgln("VMWare SVGA @ {}, {}", pci_device_identifier.address(), m_registers_io_window); } u32 VMWareGraphicsAdapter::read_io_register(VMWareDisplayRegistersOffset register_offset) const { SpinlockLocker locker(m_io_access_lock); - m_io_registers_base.out(to_underlying(register_offset)); - return m_io_registers_base.offset(1).in(); + m_registers_io_window->write32(0, to_underlying(register_offset)); + return m_registers_io_window->read32_unaligned(1); } void VMWareGraphicsAdapter::write_io_register(VMWareDisplayRegistersOffset register_offset, u32 value) { SpinlockLocker locker(m_io_access_lock); - m_io_registers_base.out(to_underlying(register_offset)); - m_io_registers_base.offset(1).out(value); + m_registers_io_window->write32(0, to_underlying(register_offset)); + m_registers_io_window->write32_unaligned(1, value); } UNMAP_AFTER_INIT ErrorOr VMWareGraphicsAdapter::negotiate_device_version() diff --git a/Kernel/Graphics/VMWare/GraphicsAdapter.h b/Kernel/Graphics/VMWare/GraphicsAdapter.h index 8872e992a9..ae27a30d25 100644 --- a/Kernel/Graphics/VMWare/GraphicsAdapter.h +++ b/Kernel/Graphics/VMWare/GraphicsAdapter.h @@ -7,10 +7,10 @@ #pragma once #include -#include #include #include #include +#include #include #include #include @@ -46,11 +46,11 @@ private: void print_svga_capabilities() const; void modeset_primary_screen_resolution(size_t width, size_t height); - explicit VMWareGraphicsAdapter(PCI::DeviceIdentifier const&); + VMWareGraphicsAdapter(PCI::DeviceIdentifier const&, NonnullOwnPtr registers_io_window); Memory::TypedMapping m_fifo_registers; LockRefPtr m_display_connector; - const IOAddress m_io_registers_base; + mutable NonnullOwnPtr m_registers_io_window; mutable Spinlock m_io_access_lock { LockRank::None }; mutable RecursiveSpinlock m_operation_lock { LockRank::None }; }; diff --git a/Kernel/IOWindow.cpp b/Kernel/IOWindow.cpp new file mode 100644 index 0000000000..fd0920d78c --- /dev/null +++ b/Kernel/IOWindow.cpp @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2022, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +namespace Kernel { + +#if ARCH(I386) || ARCH(X86_64) +ErrorOr> IOWindow::create_for_io_space(IOAddress address, u64 space_length) +{ + VERIFY(!Checked::addition_would_overflow(address.get(), space_length)); + auto io_address_range = TRY(adopt_nonnull_own_or_enomem(new (nothrow) IOAddressData(address.get(), space_length))); + return TRY(adopt_nonnull_own_or_enomem(new (nothrow) IOWindow(move(io_address_range)))); +} + +IOWindow::IOWindow(NonnullOwnPtr io_range) + : m_space_type(SpaceType::IO) + , m_io_range(move(io_range)) +{ +} +#endif + +ErrorOr> IOWindow::create_from_io_window_with_offset(u64 offset, u64 space_length) +{ +#if ARCH(I386) || ARCH(X86_64) + if (m_space_type == SpaceType::IO) { + VERIFY(m_io_range); + if (Checked::addition_would_overflow(m_io_range->address(), space_length)) + return Error::from_errno(EOVERFLOW); + auto io_address_range = TRY(adopt_nonnull_own_or_enomem(new (nothrow) IOAddressData(as_io_address().offset(offset).get(), space_length))); + return TRY(adopt_nonnull_own_or_enomem(new (nothrow) IOWindow(move(io_address_range)))); + } +#endif + VERIFY(space_type() == SpaceType::Memory); + VERIFY(m_memory_mapped_range); + +// Note: x86-IA32 is the only 32 bit CPU architecture currently being supported and +// probably will be the only such in the foreseeable future. +#if ARCH(I386) + if (Checked::addition_would_overflow(m_memory_mapped_range->paddr.get(), offset)) + return Error::from_errno(EOVERFLOW); + if (Checked::addition_would_overflow(m_memory_mapped_range->paddr.get() + offset, space_length)) + return Error::from_errno(EOVERFLOW); +#else + if (Checked::addition_would_overflow(m_memory_mapped_range->paddr.get(), offset)) + return Error::from_errno(EOVERFLOW); + if (Checked::addition_would_overflow(m_memory_mapped_range->paddr.get() + offset, space_length)) + return Error::from_errno(EOVERFLOW); +#endif + + auto memory_mapped_range = TRY(Memory::adopt_new_nonnull_own_typed_mapping(m_memory_mapped_range->paddr.offset(offset), space_length, Memory::Region::Access::ReadWrite)); + return TRY(adopt_nonnull_own_or_enomem(new (nothrow) IOWindow(move(memory_mapped_range)))); +} + +ErrorOr> IOWindow::create_from_io_window_with_offset(u64 offset) +{ + +#if ARCH(I386) || ARCH(X86_64) + if (m_space_type == SpaceType::IO) { + VERIFY(m_io_range); + VERIFY(m_io_range->space_length() >= offset); + return create_from_io_window_with_offset(offset, m_io_range->space_length() - offset); + } +#endif + VERIFY(space_type() == SpaceType::Memory); + VERIFY(m_memory_mapped_range); + VERIFY(m_memory_mapped_range->length >= offset); + return create_from_io_window_with_offset(offset, m_memory_mapped_range->length - offset); +} + +ErrorOr> IOWindow::create_for_pci_device_bar(PCI::Address const& pci_address, PCI::HeaderType0BaseRegister pci_bar, u64 space_length) +{ + u64 pci_bar_value = PCI::get_BAR(pci_address, pci_bar); + auto pci_bar_space_type = PCI::get_BAR_space_type(pci_bar_value); + if (pci_bar_space_type == PCI::BARSpaceType::Memory64BitSpace) { + // FIXME: In theory, BAR5 cannot be assigned to 64 bit as it is the last one... + // however, there might be 64 bit BAR5 for real bare metal hardware, so remove this + // if it makes a problem. + if (pci_bar == PCI::HeaderType0BaseRegister::BAR5) { + return Error::from_errno(EINVAL); + } + u64 next_pci_bar_value = PCI::get_BAR(pci_address, static_cast(to_underlying(pci_bar) + 1)); + pci_bar_value |= next_pci_bar_value << 32; + } + + auto pci_bar_space_size = PCI::get_BAR_space_size(pci_address, pci_bar); + if (pci_bar_space_size < space_length) + return Error::from_errno(EIO); + + if (pci_bar_space_type == PCI::BARSpaceType::IOSpace) { +#if ARCH(I386) || ARCH(X86_64) + if (Checked::addition_would_overflow(pci_bar_value, space_length)) + return Error::from_errno(EOVERFLOW); + auto io_address_range = TRY(adopt_nonnull_own_or_enomem(new (nothrow) IOAddressData((pci_bar_value & 0xfffffffc), space_length))); + return TRY(adopt_nonnull_own_or_enomem(new (nothrow) IOWindow(move(io_address_range)))); +#else + // Note: For non-x86 platforms, IO PCI BARs are simply not useable. + return Error::from_errno(ENOTSUP); +#endif + } + + if (pci_bar_space_type == PCI::BARSpaceType::Memory32BitSpace && Checked::addition_would_overflow(pci_bar_value, space_length)) + return Error::from_errno(EOVERFLOW); + if (pci_bar_space_type == PCI::BARSpaceType::Memory16BitSpace && Checked::addition_would_overflow(pci_bar_value, space_length)) + return Error::from_errno(EOVERFLOW); + if (pci_bar_space_type == PCI::BARSpaceType::Memory64BitSpace && Checked::addition_would_overflow(pci_bar_value, space_length)) + return Error::from_errno(EOVERFLOW); + auto memory_mapped_range = TRY(Memory::adopt_new_nonnull_own_typed_mapping(PhysicalAddress(pci_bar_value & 0xfffffff0), space_length, Memory::Region::Access::ReadWrite)); + return TRY(adopt_nonnull_own_or_enomem(new (nothrow) IOWindow(move(memory_mapped_range)))); +} + +ErrorOr> IOWindow::create_for_pci_device_bar(PCI::Address const& pci_address, PCI::HeaderType0BaseRegister pci_bar) +{ + u64 pci_bar_space_size = PCI::get_BAR_space_size(pci_address, pci_bar); + return create_for_pci_device_bar(pci_address, pci_bar, pci_bar_space_size); +} + +ErrorOr> IOWindow::create_for_pci_device_bar(PCI::DeviceIdentifier const& pci_device_identifier, PCI::HeaderType0BaseRegister pci_bar) +{ + u64 pci_bar_space_size = PCI::get_BAR_space_size(pci_device_identifier.address(), pci_bar); + return create_for_pci_device_bar(pci_device_identifier.address(), pci_bar, pci_bar_space_size); +} + +ErrorOr> IOWindow::create_for_pci_device_bar(PCI::DeviceIdentifier const& pci_device_identifier, PCI::HeaderType0BaseRegister pci_bar, u64 space_length) +{ + return create_for_pci_device_bar(pci_device_identifier.address(), pci_bar, space_length); +} + +IOWindow::IOWindow(NonnullOwnPtr> memory_mapped_range) + : m_space_type(SpaceType::Memory) + , m_memory_mapped_range(move(memory_mapped_range)) +{ +} + +IOWindow::~IOWindow() = default; + +bool IOWindow::is_access_aligned(u64 offset, size_t byte_size_access) const +{ + return (offset % byte_size_access) == 0; +} + +bool IOWindow::is_access_in_range(u64 offset, size_t byte_size_access) const +{ + if (Checked::addition_would_overflow(offset, byte_size_access)) + return false; +#if ARCH(I386) || ARCH(X86_64) + if (m_space_type == SpaceType::IO) { + VERIFY(m_io_range); + VERIFY(!Checked::addition_would_overflow(m_io_range->address(), m_io_range->space_length())); + // To understand how we treat IO address space with the corresponding calculation, the Intel Software Developer manual + // helps us to understand the layout of the IO address space - + // + // Intel® 64 and IA-32 Architectures Software Developer’s Manual, Volume 1: Basic Architecture, 16.3 I/O ADDRESS SPACE, page 16-1 wrote: + // Any two consecutive 8-bit ports can be treated as a 16-bit port, and any four consecutive ports can be a 32-bit port. + // In this manner, the processor can transfer 8, 16, or 32 bits to or from a device in the I/O address space. + // Like words in memory, 16-bit ports should be aligned to even addresses (0, 2, 4, ...) so that all 16 bits can be transferred in a single bus cycle. + // Likewise, 32-bit ports should be aligned to addresses that are multiples of four (0, 4, 8, ...). + // The processor supports data transfers to unaligned ports, but there is a performance penalty because one or more + // extra bus cycle must be used. + return (m_io_range->address() + m_io_range->space_length()) >= (offset + byte_size_access); + } +#endif + VERIFY(space_type() == SpaceType::Memory); + VERIFY(m_memory_mapped_range); + VERIFY(!Checked::addition_would_overflow(m_memory_mapped_range->offset, m_memory_mapped_range->length)); + return (m_memory_mapped_range->offset + m_memory_mapped_range->length) >= (offset + byte_size_access); +} + +u8 IOWindow::read8(u64 offset) +{ + VERIFY(is_access_in_range(offset, sizeof(u8))); + u8 data { 0 }; + in(offset, data); + return data; +} +u16 IOWindow::read16(u64 offset) +{ + // Note: Although it might be OK to allow unaligned access on regular memory, + // for memory mapped IO access, it should always be considered a bug. + // The same goes for port mapped IO access, because in x86 unaligned access to ports + // is possible but there's a performance penalty. + VERIFY(is_access_in_range(offset, sizeof(u16))); + VERIFY(is_access_aligned(offset, sizeof(u16))); + u16 data { 0 }; + in(offset, data); + return data; +} +u32 IOWindow::read32(u64 offset) +{ + // Note: Although it might be OK to allow unaligned access on regular memory, + // for memory mapped IO access, it should always be considered a bug. + // The same goes for port mapped IO access, because in x86 unaligned access to ports + // is possible but there's a performance penalty. + VERIFY(is_access_in_range(offset, sizeof(u32))); + VERIFY(is_access_aligned(offset, sizeof(u32))); + u32 data { 0 }; + in(offset, data); + return data; +} + +void IOWindow::write8(u64 offset, u8 data) +{ + VERIFY(is_access_in_range(offset, sizeof(u8))); + out(offset, data); +} +void IOWindow::write16(u64 offset, u16 data) +{ + // Note: Although it might be OK to allow unaligned access on regular memory, + // for memory mapped IO access, it should always be considered a bug. + // The same goes for port mapped IO access, because in x86 unaligned access to ports + // is possible but there's a performance penalty. + VERIFY(is_access_in_range(offset, sizeof(u16))); + VERIFY(is_access_aligned(offset, sizeof(u16))); + out(offset, data); +} +void IOWindow::write32(u64 offset, u32 data) +{ + // Note: Although it might be OK to allow unaligned access on regular memory, + // for memory mapped IO access, it should always be considered a bug. + // The same goes for port mapped IO access, because in x86 unaligned access to ports + // is possible but there's a performance penalty. + VERIFY(is_access_in_range(offset, sizeof(u32))); + VERIFY(is_access_aligned(offset, sizeof(u32))); + out(offset, data); +} + +void IOWindow::write32_unaligned(u64 offset, u32 data) +{ + // Note: We only verify that we access IO in the expected range. + // Note: for port mapped IO access, because in x86 unaligned access to ports + // is possible but there's a performance penalty, we can still allow that to happen. + // However, it should be noted that most cases should not use unaligned access + // to hardware IO, so this is a valid case in emulators or hypervisors only. + // Note: Using this for memory mapped IO will fail for unaligned access, because + // there's no valid use case for it (yet). + VERIFY(space_type() != SpaceType::Memory); + VERIFY(is_access_in_range(offset, sizeof(u32))); + out(offset, data); +} + +u32 IOWindow::read32_unaligned(u64 offset) +{ + // Note: We only verify that we access IO in the expected range. + // Note: for port mapped IO access, because in x86 unaligned access to ports + // is possible but there's a performance penalty, we can still allow that to happen. + // However, it should be noted that most cases should not use unaligned access + // to hardware IO, so this is a valid case in emulators or hypervisors only. + // Note: Using this for memory mapped IO will fail for unaligned access, because + // there's no valid use case for it (yet). + VERIFY(space_type() != SpaceType::Memory); + VERIFY(is_access_in_range(offset, sizeof(u32))); + u32 data { 0 }; + in(offset, data); + return data; +} + +PhysicalAddress IOWindow::as_physical_memory_address() const +{ + VERIFY(space_type() == SpaceType::Memory); + VERIFY(m_memory_mapped_range); + return m_memory_mapped_range->paddr; +} + +u8 volatile* IOWindow::as_memory_address_pointer() +{ + VERIFY(space_type() == SpaceType::Memory); + VERIFY(m_memory_mapped_range); + return m_memory_mapped_range->ptr(); +} + +#if ARCH(I386) || ARCH(X86_64) +IOAddress IOWindow::as_io_address() const +{ + VERIFY(space_type() == SpaceType::IO); + VERIFY(m_io_range); + return IOAddress(m_io_range->address()); +} +#endif + +} diff --git a/Kernel/IOWindow.h b/Kernel/IOWindow.h new file mode 100644 index 0000000000..89927f2b01 --- /dev/null +++ b/Kernel/IOWindow.h @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2022, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#if ARCH(I386) || ARCH(X86_64) +# include +#endif +#include +#include +#include + +namespace Kernel { + +class IOWindow { +public: + enum class SpaceType { +#if ARCH(I386) || ARCH(X86_64) + IO, +#endif + Memory, + }; + + SpaceType space_type() const { return m_space_type; } + + template + void write(); + +#if ARCH(I386) || ARCH(X86_64) + static ErrorOr> create_for_io_space(IOAddress, u64 space_length); +#endif + static ErrorOr> create_for_pci_device_bar(PCI::DeviceIdentifier const&, PCI::HeaderType0BaseRegister, u64 space_length); + static ErrorOr> create_for_pci_device_bar(PCI::DeviceIdentifier const&, PCI::HeaderType0BaseRegister); + + static ErrorOr> create_for_pci_device_bar(PCI::Address const&, PCI::HeaderType0BaseRegister, u64 space_length); + static ErrorOr> create_for_pci_device_bar(PCI::Address const&, PCI::HeaderType0BaseRegister); + + ErrorOr> create_from_io_window_with_offset(u64 offset, u64 space_length); + ErrorOr> create_from_io_window_with_offset(u64 offset); + + bool is_access_valid(u64 offset, size_t byte_size_access) const; + + u8 read8(u64 offset); + u16 read16(u64 offset); + u32 read32(u64 offset); + + void write8(u64 offset, u8); + void write16(u64 offset, u16); + void write32(u64 offset, u32); + + // Note: These methods are useful in exceptional cases where we need to do unaligned + // access. This mostly happens on emulators and hypervisors (such as VMWare) because they don't enforce aligned access + // to IO and sometimes even require such access, so we have to use these functions. + void write32_unaligned(u64 offset, u32); + u32 read32_unaligned(u64 offset); + + bool operator==(IOWindow const& other) const = delete; + bool operator!=(IOWindow const& other) const = delete; + bool operator>(IOWindow const& other) const = delete; + bool operator>=(IOWindow const& other) const = delete; + bool operator<(IOWindow const& other) const = delete; + bool operator<=(IOWindow const& other) const = delete; + + ~IOWindow(); + + PhysicalAddress as_physical_memory_address() const; +#if ARCH(I386) || ARCH(X86_64) + IOAddress as_io_address() const; +#endif + +private: + explicit IOWindow(NonnullOwnPtr>); + + u8 volatile* as_memory_address_pointer(); + +#if ARCH(I386) || ARCH(X86_64) + struct IOAddressData { + public: + IOAddressData(u64 address, u64 space_length) + : m_address(address) + , m_space_length(space_length) + { + } + u64 address() const { return m_address; } + u64 space_length() const { return m_space_length; } + + private: + u64 m_address { 0 }; + u64 m_space_length { 0 }; + }; + + explicit IOWindow(NonnullOwnPtr); +#endif + + bool is_access_in_range(u64 offset, size_t byte_size_access) const; + bool is_access_aligned(u64 offset, size_t byte_size_access) const; + + template + ALWAYS_INLINE void in(u64 start_offset, T& data) + { +#if ARCH(I386) || ARCH(X86_64) + if (m_space_type == SpaceType::IO) { + data = as_io_address().offset(start_offset).in(); + return; + } +#endif + VERIFY(m_space_type == SpaceType::Memory); + VERIFY(m_memory_mapped_range); + // Note: For memory-mapped IO we simply never allow unaligned access as it + // can cause problems with strict bare metal hardware. For example, some XHCI USB controllers + // might completely lock up because of an unaligned memory access to their registers. + VERIFY((start_offset % sizeof(T)) == 0); + data = *(volatile T*)(as_memory_address_pointer() + start_offset); + } + + template + ALWAYS_INLINE void out(u64 start_offset, T value) + { +#if ARCH(I386) || ARCH(X86_64) + if (m_space_type == SpaceType::IO) { + VERIFY(m_io_range); + as_io_address().offset(start_offset).out(value); + return; + } +#endif + VERIFY(m_space_type == SpaceType::Memory); + VERIFY(m_memory_mapped_range); + // Note: For memory-mapped IO we simply never allow unaligned access as it + // can cause problems with strict bare metal hardware. For example, some XHCI USB controllers + // might completely lock up because of an unaligned memory access to their registers. + VERIFY((start_offset % sizeof(T)) == 0); + *(volatile T*)(as_memory_address_pointer() + start_offset) = value; + } + + SpaceType m_space_type { SpaceType::Memory }; + + OwnPtr> m_memory_mapped_range; + +#if ARCH(I386) || ARCH(X86_64) + OwnPtr m_io_range; +#endif +}; + +} + +template<> +struct AK::Formatter : AK::Formatter { + ErrorOr format(FormatBuilder& builder, Kernel::IOWindow const& value) + { +#if ARCH(I386) || ARCH(X86_64) + if (value.space_type() == Kernel::IOWindow::SpaceType::IO) + return Formatter::format(builder, "{}"sv, value.as_io_address()); +#endif + VERIFY(value.space_type() == Kernel::IOWindow::SpaceType::Memory); + return Formatter::format(builder, "Memory {}"sv, value.as_physical_memory_address()); + } +}; diff --git a/Kernel/Memory/TypedMapping.h b/Kernel/Memory/TypedMapping.h index 033d346473..4412357579 100644 --- a/Kernel/Memory/TypedMapping.h +++ b/Kernel/Memory/TypedMapping.h @@ -23,7 +23,9 @@ struct TypedMapping { const T& operator*() const { return *ptr(); } T& operator*() { return *ptr(); } OwnPtr region; + PhysicalAddress paddr; size_t offset { 0 }; + size_t length { 0 }; }; template @@ -34,6 +36,8 @@ static ErrorOr>> adopt_new_nonnull_own_typed_mappi auto table = TRY(adopt_nonnull_own_or_enomem(new (nothrow) Memory::TypedMapping())); table->region = move(region); table->offset = paddr.offset_in_page(); + table->paddr = paddr; + table->length = length; return table; } @@ -44,6 +48,8 @@ static ErrorOr> map_typed(PhysicalAddress paddr, size_t length, auto mapping_length = TRY(page_round_up(paddr.offset_in_page() + length)); table.region = TRY(MM.allocate_kernel_region(paddr.page_base(), mapping_length, {}, access)); table.offset = paddr.offset_in_page(); + table.paddr = paddr; + table.length = length; return table; } diff --git a/Kernel/Net/Intel/E1000ENetworkAdapter.cpp b/Kernel/Net/Intel/E1000ENetworkAdapter.cpp index beb210381b..cdbf1fab46 100644 --- a/Kernel/Net/Intel/E1000ENetworkAdapter.cpp +++ b/Kernel/Net/Intel/E1000ENetworkAdapter.cpp @@ -192,7 +192,8 @@ UNMAP_AFTER_INIT LockRefPtr E1000ENetworkAdapter::try_to_i auto interface_name_or_error = NetworkingManagement::generate_interface_name_from_pci_address(pci_device_identifier); if (interface_name_or_error.is_error()) return {}; - auto adapter = adopt_lock_ref_if_nonnull(new (nothrow) E1000ENetworkAdapter(pci_device_identifier.address(), irq, interface_name_or_error.release_value())); + auto registers_io_window = IOWindow::create_for_pci_device_bar(pci_device_identifier, PCI::HeaderType0BaseRegister::BAR0).release_value_but_fixme_should_propagate_errors(); + auto adapter = adopt_lock_ref_if_nonnull(new (nothrow) E1000ENetworkAdapter(pci_device_identifier.address(), irq, move(registers_io_window), interface_name_or_error.release_value())); if (!adapter) return {}; if (adapter->initialize()) @@ -203,21 +204,9 @@ UNMAP_AFTER_INIT LockRefPtr E1000ENetworkAdapter::try_to_i UNMAP_AFTER_INIT bool E1000ENetworkAdapter::initialize() { dmesgln("E1000e: Found @ {}", pci_address()); - - m_io_base = IOAddress(PCI::get_BAR2(pci_address()) & ~1); - enable_bus_mastering(pci_address()); - size_t mmio_base_size = PCI::get_BAR_space_size(pci_address(), PCI::HeaderType0BaseRegister::BAR0); - auto region_or_error = MM.allocate_kernel_region(PhysicalAddress(page_base_of(PCI::get_BAR0(pci_address()))), Memory::page_round_up(mmio_base_size).release_value_but_fixme_should_propagate_errors(), "E1000e MMIO"sv, Memory::Region::Access::ReadWrite, Memory::Region::Cacheable::No); - if (region_or_error.is_error()) - return false; - m_mmio_region = region_or_error.release_value(); - m_mmio_base = m_mmio_region->vaddr(); - m_use_mmio = true; - dmesgln("E1000e: port base: {}", m_io_base); - dmesgln("E1000e: MMIO base: {}", PhysicalAddress(PCI::get_BAR0(pci_address()) & 0xfffffffc)); - dmesgln("E1000e: MMIO base size: {} bytes", mmio_base_size); + dmesgln("E1000e: IO base: {}", m_registers_io_window); dmesgln("E1000e: Interrupt line: {}", interrupt_number()); detect_eeprom(); dmesgln("E1000e: Has EEPROM? {}", m_has_eeprom); @@ -233,8 +222,8 @@ UNMAP_AFTER_INIT bool E1000ENetworkAdapter::initialize() return true; } -UNMAP_AFTER_INIT E1000ENetworkAdapter::E1000ENetworkAdapter(PCI::Address address, u8 irq, NonnullOwnPtr interface_name) - : E1000NetworkAdapter(address, irq, move(interface_name)) +UNMAP_AFTER_INIT E1000ENetworkAdapter::E1000ENetworkAdapter(PCI::Address address, u8 irq, NonnullOwnPtr registers_io_window, NonnullOwnPtr interface_name) + : E1000NetworkAdapter(address, irq, move(registers_io_window), move(interface_name)) { } diff --git a/Kernel/Net/Intel/E1000ENetworkAdapter.h b/Kernel/Net/Intel/E1000ENetworkAdapter.h index ce679dfbba..64ef266401 100644 --- a/Kernel/Net/Intel/E1000ENetworkAdapter.h +++ b/Kernel/Net/Intel/E1000ENetworkAdapter.h @@ -8,9 +8,9 @@ #include #include -#include #include #include +#include #include #include #include @@ -30,7 +30,7 @@ public: virtual StringView purpose() const override { return class_name(); } private: - E1000ENetworkAdapter(PCI::Address, u8 irq, NonnullOwnPtr); + E1000ENetworkAdapter(PCI::Address, u8 irq, NonnullOwnPtr registers_io_window, NonnullOwnPtr); virtual StringView class_name() const override { return "E1000ENetworkAdapter"sv; } diff --git a/Kernel/Net/Intel/E1000NetworkAdapter.cpp b/Kernel/Net/Intel/E1000NetworkAdapter.cpp index 7ea0fe6379..18fd59ac8e 100644 --- a/Kernel/Net/Intel/E1000NetworkAdapter.cpp +++ b/Kernel/Net/Intel/E1000NetworkAdapter.cpp @@ -170,7 +170,8 @@ UNMAP_AFTER_INIT LockRefPtr E1000NetworkAdapter::try_to_ini auto interface_name_or_error = NetworkingManagement::generate_interface_name_from_pci_address(pci_device_identifier); if (interface_name_or_error.is_error()) return {}; - auto adapter = adopt_lock_ref_if_nonnull(new (nothrow) E1000NetworkAdapter(pci_device_identifier.address(), irq, interface_name_or_error.release_value())); + auto registers_io_window = IOWindow::create_for_pci_device_bar(pci_device_identifier, PCI::HeaderType0BaseRegister::BAR0).release_value_but_fixme_should_propagate_errors(); + auto adapter = adopt_lock_ref_if_nonnull(new (nothrow) E1000NetworkAdapter(pci_device_identifier.address(), irq, move(registers_io_window), interface_name_or_error.release_value())); if (!adapter) return {}; if (adapter->initialize()) @@ -197,18 +198,7 @@ UNMAP_AFTER_INIT bool E1000NetworkAdapter::initialize() dmesgln("E1000: Found @ {}", pci_address()); enable_bus_mastering(pci_address()); - m_io_base = IOAddress(PCI::get_BAR1(pci_address()) & ~1); - - size_t mmio_base_size = PCI::get_BAR_space_size(pci_address(), PCI::HeaderType0BaseRegister::BAR0); - auto region_or_error = MM.allocate_kernel_region(PhysicalAddress(page_base_of(PCI::get_BAR0(pci_address()))), Memory::page_round_up(mmio_base_size).release_value_but_fixme_should_propagate_errors(), "E1000 MMIO"sv, Memory::Region::Access::ReadWrite, Memory::Region::Cacheable::No); - if (region_or_error.is_error()) - return false; - m_mmio_region = region_or_error.release_value(); - m_mmio_base = m_mmio_region->vaddr(); - m_use_mmio = true; - dmesgln("E1000: port base: {}", m_io_base); - dmesgln("E1000: MMIO base: {}", PhysicalAddress(PCI::get_BAR0(pci_address()) & 0xfffffffc)); - dmesgln("E1000: MMIO base size: {} bytes", mmio_base_size); + dmesgln("E1000: IO base: {}", m_registers_io_window); dmesgln("E1000: Interrupt line: {}", interrupt_number()); detect_eeprom(); dmesgln("E1000: Has EEPROM? {}", m_has_eeprom); @@ -227,10 +217,11 @@ UNMAP_AFTER_INIT bool E1000NetworkAdapter::initialize() return true; } -UNMAP_AFTER_INIT E1000NetworkAdapter::E1000NetworkAdapter(PCI::Address address, u8 irq, NonnullOwnPtr interface_name) +UNMAP_AFTER_INIT E1000NetworkAdapter::E1000NetworkAdapter(PCI::Address address, u8 irq, NonnullOwnPtr registers_io_window, NonnullOwnPtr interface_name) : NetworkAdapter(move(interface_name)) , PCI::Device(address) , IRQHandler(irq) + , m_registers_io_window(move(registers_io_window)) , m_rx_descriptors_region(MM.allocate_contiguous_kernel_region(Memory::page_round_up(sizeof(e1000_rx_desc) * number_of_rx_descriptors).release_value_but_fixme_should_propagate_errors(), "E1000 RX Descriptors"sv, Memory::Region::Access::ReadWrite).release_value()) , m_tx_descriptors_region(MM.allocate_contiguous_kernel_region(Memory::page_round_up(sizeof(e1000_tx_desc) * number_of_tx_descriptors).release_value_but_fixme_should_propagate_errors(), "E1000 TX Descriptors"sv, Memory::Region::Access::ReadWrite).release_value()) { @@ -369,58 +360,37 @@ UNMAP_AFTER_INIT void E1000NetworkAdapter::initialize_tx_descriptors() void E1000NetworkAdapter::out8(u16 address, u8 data) { dbgln_if(E1000_DEBUG, "E1000: OUT8 {:#02x} @ {:#04x}", data, address); - if (m_use_mmio) { - auto* ptr = (u8 volatile*)(m_mmio_base.get() + address); - *ptr = data; - return; - } - m_io_base.offset(address).out(data); + m_registers_io_window->write8(address, data); } void E1000NetworkAdapter::out16(u16 address, u16 data) { dbgln_if(E1000_DEBUG, "E1000: OUT16 {:#04x} @ {:#04x}", data, address); - if (m_use_mmio) { - auto* ptr = (u16 volatile*)(m_mmio_base.get() + address); - *ptr = data; - return; - } - m_io_base.offset(address).out(data); + m_registers_io_window->write16(address, data); } void E1000NetworkAdapter::out32(u16 address, u32 data) { dbgln_if(E1000_DEBUG, "E1000: OUT32 {:#08x} @ {:#04x}", data, address); - if (m_use_mmio) { - auto* ptr = (u32 volatile*)(m_mmio_base.get() + address); - *ptr = data; - return; - } - m_io_base.offset(address).out(data); + m_registers_io_window->write32(address, data); } u8 E1000NetworkAdapter::in8(u16 address) { dbgln_if(E1000_DEBUG, "E1000: IN8 @ {:#04x}", address); - if (m_use_mmio) - return *(u8 volatile*)(m_mmio_base.get() + address); - return m_io_base.offset(address).in(); + return m_registers_io_window->read8(address); } u16 E1000NetworkAdapter::in16(u16 address) { dbgln_if(E1000_DEBUG, "E1000: IN16 @ {:#04x}", address); - if (m_use_mmio) - return *(u16 volatile*)(m_mmio_base.get() + address); - return m_io_base.offset(address).in(); + return m_registers_io_window->read16(address); } u32 E1000NetworkAdapter::in32(u16 address) { dbgln_if(E1000_DEBUG, "E1000: IN32 @ {:#04x}", address); - if (m_use_mmio) - return *(u32 volatile*)(m_mmio_base.get() + address); - return m_io_base.offset(address).in(); + return m_registers_io_window->read32(address); } void E1000NetworkAdapter::send_raw(ReadonlyBytes payload) diff --git a/Kernel/Net/Intel/E1000NetworkAdapter.h b/Kernel/Net/Intel/E1000NetworkAdapter.h index f4d5c775e5..3f40733fbf 100644 --- a/Kernel/Net/Intel/E1000NetworkAdapter.h +++ b/Kernel/Net/Intel/E1000NetworkAdapter.h @@ -7,9 +7,9 @@ #pragma once #include -#include #include #include +#include #include #include #include @@ -37,7 +37,7 @@ protected: void setup_interrupts(); void setup_link(); - E1000NetworkAdapter(PCI::Address, u8 irq, NonnullOwnPtr); + E1000NetworkAdapter(PCI::Address, u8 irq, NonnullOwnPtr registers_io_window, NonnullOwnPtr); virtual bool handle_irq(RegisterState const&) override; virtual StringView class_name() const override { return "E1000NetworkAdapter"sv; } @@ -82,17 +82,15 @@ protected: static constexpr size_t number_of_rx_descriptors = 256; static constexpr size_t number_of_tx_descriptors = 256; - IOAddress m_io_base; - VirtualAddress m_mmio_base; + NonnullOwnPtr m_registers_io_window; + OwnPtr m_rx_descriptors_region; OwnPtr m_tx_descriptors_region; OwnPtr m_rx_buffer_region; OwnPtr m_tx_buffer_region; Array m_rx_buffers; Array m_tx_buffers; - OwnPtr m_mmio_region; bool m_has_eeprom { false }; - bool m_use_mmio { false }; bool m_link_up { false }; EntropySource m_entropy_source; diff --git a/Kernel/Net/NE2000/NetworkAdapter.cpp b/Kernel/Net/NE2000/NetworkAdapter.cpp index 14bbc9ca7c..30dd731c3d 100644 --- a/Kernel/Net/NE2000/NetworkAdapter.cpp +++ b/Kernel/Net/NE2000/NetworkAdapter.cpp @@ -5,9 +5,9 @@ */ #include -#include #include #include +#include #include #include #include @@ -162,18 +162,19 @@ UNMAP_AFTER_INIT LockRefPtr NE2000NetworkAdapter::try_to_i auto interface_name_or_error = NetworkingManagement::generate_interface_name_from_pci_address(pci_device_identifier); if (interface_name_or_error.is_error()) return {}; - return adopt_lock_ref_if_nonnull(new (nothrow) NE2000NetworkAdapter(pci_device_identifier.address(), irq, interface_name_or_error.release_value())); + auto registers_io_window = MUST(IOWindow::create_for_pci_device_bar(pci_device_identifier, PCI::HeaderType0BaseRegister::BAR0)); + return adopt_lock_ref_if_nonnull(new (nothrow) NE2000NetworkAdapter(pci_device_identifier.address(), irq, move(registers_io_window), interface_name_or_error.release_value())); } -UNMAP_AFTER_INIT NE2000NetworkAdapter::NE2000NetworkAdapter(PCI::Address address, u8 irq, NonnullOwnPtr interface_name) +UNMAP_AFTER_INIT NE2000NetworkAdapter::NE2000NetworkAdapter(PCI::Address address, u8 irq, NonnullOwnPtr registers_io_window, NonnullOwnPtr interface_name) : NetworkAdapter(move(interface_name)) , PCI::Device(address) , IRQHandler(irq) - , m_io_base(PCI::get_BAR0(pci_address()) & ~3) + , m_registers_io_window(move(registers_io_window)) { dmesgln("NE2000: Found @ {}", pci_address()); - dmesgln("NE2000: Port base: {}", m_io_base); + dmesgln("NE2000: Port base: {}", m_registers_io_window); dmesgln("NE2000: Interrupt line: {}", interrupt_number()); int ram_errors = ram_test(); @@ -237,7 +238,6 @@ bool NE2000NetworkAdapter::handle_irq(RegisterState const&) UNMAP_AFTER_INIT int NE2000NetworkAdapter::ram_test() { - IOAddress io(PCI::get_BAR0(pci_address()) & ~3); int errors = 0; out8(REG_RW_COMMAND, BIT_COMMAND_DMA_ABORT | BIT_COMMAND_STOP); @@ -454,23 +454,22 @@ void NE2000NetworkAdapter::receive() void NE2000NetworkAdapter::out8(u16 address, u8 data) { - m_io_base.offset(address).out(data); + m_registers_io_window->write8(address, data); } void NE2000NetworkAdapter::out16(u16 address, u16 data) { - m_io_base.offset(address).out(data); + m_registers_io_window->write16(address, data); } u8 NE2000NetworkAdapter::in8(u16 address) { - u8 data = m_io_base.offset(address).in(); - return data; + return m_registers_io_window->read8(address); } u16 NE2000NetworkAdapter::in16(u16 address) { - return m_io_base.offset(address).in(); + return m_registers_io_window->read16(address); } } diff --git a/Kernel/Net/NE2000/NetworkAdapter.h b/Kernel/Net/NE2000/NetworkAdapter.h index 18f5f92b2d..1460da1614 100644 --- a/Kernel/Net/NE2000/NetworkAdapter.h +++ b/Kernel/Net/NE2000/NetworkAdapter.h @@ -7,9 +7,9 @@ #pragma once #include -#include #include #include +#include #include #include #include @@ -41,7 +41,7 @@ public: virtual StringView purpose() const override { return class_name(); } private: - NE2000NetworkAdapter(PCI::Address, u8, NonnullOwnPtr); + NE2000NetworkAdapter(PCI::Address, u8, NonnullOwnPtr registers_io_window, NonnullOwnPtr); virtual bool handle_irq(RegisterState const&) override; virtual StringView class_name() const override { return "NE2000NetworkAdapter"sv; } @@ -58,7 +58,7 @@ private: u8 in8(u16 address); u16 in16(u16 address); - IOAddress m_io_base; + NonnullOwnPtr m_registers_io_window; int m_ring_read_ptr; u8 m_interrupt_line { 0 }; diff --git a/Kernel/Net/NetworkingManagement.cpp b/Kernel/Net/NetworkingManagement.cpp index ca3892e62d..e3fe772bf6 100644 --- a/Kernel/Net/NetworkingManagement.cpp +++ b/Kernel/Net/NetworkingManagement.cpp @@ -5,7 +5,6 @@ */ #include -#include #include #include #include diff --git a/Kernel/Net/Realtek/RTL8139NetworkAdapter.cpp b/Kernel/Net/Realtek/RTL8139NetworkAdapter.cpp index b66496aca1..da87dfa8ba 100644 --- a/Kernel/Net/Realtek/RTL8139NetworkAdapter.cpp +++ b/Kernel/Net/Realtek/RTL8139NetworkAdapter.cpp @@ -5,7 +5,6 @@ */ #include -#include #include #include #include @@ -123,14 +122,15 @@ UNMAP_AFTER_INIT LockRefPtr RTL8139NetworkAdapter::try_to auto interface_name_or_error = NetworkingManagement::generate_interface_name_from_pci_address(pci_device_identifier); if (interface_name_or_error.is_error()) return {}; - return adopt_lock_ref_if_nonnull(new (nothrow) RTL8139NetworkAdapter(pci_device_identifier.address(), irq, interface_name_or_error.release_value())); + auto registers_io_window = MUST(IOWindow::create_for_pci_device_bar(pci_device_identifier, PCI::HeaderType0BaseRegister::BAR0)); + return adopt_lock_ref_if_nonnull(new (nothrow) RTL8139NetworkAdapter(pci_device_identifier.address(), irq, move(registers_io_window), interface_name_or_error.release_value())); } -UNMAP_AFTER_INIT RTL8139NetworkAdapter::RTL8139NetworkAdapter(PCI::Address address, u8 irq, NonnullOwnPtr interface_name) +UNMAP_AFTER_INIT RTL8139NetworkAdapter::RTL8139NetworkAdapter(PCI::Address address, u8 irq, NonnullOwnPtr registers_io_window, NonnullOwnPtr interface_name) : NetworkAdapter(move(interface_name)) , PCI::Device(address) , IRQHandler(irq) - , m_io_base(PCI::get_BAR0(pci_address()) & ~1) + , m_registers_io_window(move(registers_io_window)) , m_rx_buffer(MM.allocate_contiguous_kernel_region(Memory::page_round_up(RX_BUFFER_SIZE + PACKET_SIZE_MAX).release_value_but_fixme_should_propagate_errors(), "RTL8139 RX"sv, Memory::Region::Access::ReadWrite).release_value()) , m_packet_buffer(MM.allocate_contiguous_kernel_region(Memory::page_round_up(PACKET_SIZE_MAX).release_value_but_fixme_should_propagate_errors(), "RTL8139 Packet buffer"sv, Memory::Region::Access::ReadWrite).release_value()) { @@ -140,7 +140,7 @@ UNMAP_AFTER_INIT RTL8139NetworkAdapter::RTL8139NetworkAdapter(PCI::Address addre enable_bus_mastering(pci_address()); - dmesgln("RTL8139: I/O port base: {}", m_io_base); + dmesgln("RTL8139: I/O port base: {}", m_registers_io_window); dmesgln("RTL8139: Interrupt line: {}", interrupt_number()); // we add space to account for overhang from the last packet - the rtl8139 @@ -349,32 +349,32 @@ void RTL8139NetworkAdapter::receive() void RTL8139NetworkAdapter::out8(u16 address, u8 data) { - m_io_base.offset(address).out(data); + m_registers_io_window->write8(address, data); } void RTL8139NetworkAdapter::out16(u16 address, u16 data) { - m_io_base.offset(address).out(data); + m_registers_io_window->write16(address, data); } void RTL8139NetworkAdapter::out32(u16 address, u32 data) { - m_io_base.offset(address).out(data); + m_registers_io_window->write32(address, data); } u8 RTL8139NetworkAdapter::in8(u16 address) { - return m_io_base.offset(address).in(); + return m_registers_io_window->read8(address); } u16 RTL8139NetworkAdapter::in16(u16 address) { - return m_io_base.offset(address).in(); + return m_registers_io_window->read16(address); } u32 RTL8139NetworkAdapter::in32(u16 address) { - return m_io_base.offset(address).in(); + return m_registers_io_window->read32(address); } bool RTL8139NetworkAdapter::link_full_duplex() diff --git a/Kernel/Net/Realtek/RTL8139NetworkAdapter.h b/Kernel/Net/Realtek/RTL8139NetworkAdapter.h index 506590a962..72d82f08af 100644 --- a/Kernel/Net/Realtek/RTL8139NetworkAdapter.h +++ b/Kernel/Net/Realtek/RTL8139NetworkAdapter.h @@ -7,9 +7,9 @@ #pragma once #include -#include #include #include +#include #include #include #include @@ -34,7 +34,7 @@ public: virtual StringView purpose() const override { return class_name(); } private: - RTL8139NetworkAdapter(PCI::Address, u8 irq, NonnullOwnPtr); + RTL8139NetworkAdapter(PCI::Address, u8 irq, NonnullOwnPtr registers_io_window, NonnullOwnPtr); virtual bool handle_irq(RegisterState const&) override; virtual StringView class_name() const override { return "RTL8139NetworkAdapter"sv; } @@ -50,7 +50,7 @@ private: u16 in16(u16 address); u32 in32(u16 address); - IOAddress m_io_base; + NonnullOwnPtr m_registers_io_window; u8 m_interrupt_line { 0 }; OwnPtr m_rx_buffer; u16 m_rx_buffer_offset { 0 }; diff --git a/Kernel/Net/Realtek/RTL8168NetworkAdapter.cpp b/Kernel/Net/Realtek/RTL8168NetworkAdapter.cpp index d8317a9be5..85ce6b437b 100644 --- a/Kernel/Net/Realtek/RTL8168NetworkAdapter.cpp +++ b/Kernel/Net/Realtek/RTL8168NetworkAdapter.cpp @@ -193,7 +193,8 @@ UNMAP_AFTER_INIT LockRefPtr RTL8168NetworkAdapter::try_to auto interface_name_or_error = NetworkingManagement::generate_interface_name_from_pci_address(pci_device_identifier); if (interface_name_or_error.is_error()) return {}; - return adopt_lock_ref_if_nonnull(new (nothrow) RTL8168NetworkAdapter(pci_device_identifier.address(), irq, interface_name_or_error.release_value())); + auto registers_io_window = MUST(IOWindow::create_for_pci_device_bar(pci_device_identifier, PCI::HeaderType0BaseRegister::BAR0)); + return adopt_lock_ref_if_nonnull(new (nothrow) RTL8168NetworkAdapter(pci_device_identifier.address(), irq, move(registers_io_window), interface_name_or_error.release_value())); } bool RTL8168NetworkAdapter::determine_supported_version() const @@ -241,16 +242,16 @@ bool RTL8168NetworkAdapter::determine_supported_version() const } } -UNMAP_AFTER_INIT RTL8168NetworkAdapter::RTL8168NetworkAdapter(PCI::Address address, u8 irq, NonnullOwnPtr interface_name) +UNMAP_AFTER_INIT RTL8168NetworkAdapter::RTL8168NetworkAdapter(PCI::Address address, u8 irq, NonnullOwnPtr registers_io_window, NonnullOwnPtr interface_name) : NetworkAdapter(move(interface_name)) , PCI::Device(address) , IRQHandler(irq) - , m_io_base(PCI::get_BAR0(pci_address()) & ~1) + , m_registers_io_window(move(registers_io_window)) , m_rx_descriptors_region(MM.allocate_contiguous_kernel_region(Memory::page_round_up(sizeof(TXDescriptor) * (number_of_rx_descriptors + 1)).release_value_but_fixme_should_propagate_errors(), "RTL8168 RX"sv, Memory::Region::Access::ReadWrite).release_value()) , m_tx_descriptors_region(MM.allocate_contiguous_kernel_region(Memory::page_round_up(sizeof(RXDescriptor) * (number_of_tx_descriptors + 1)).release_value_but_fixme_should_propagate_errors(), "RTL8168 TX"sv, Memory::Region::Access::ReadWrite).release_value()) { dmesgln("RTL8168: Found @ {}", pci_address()); - dmesgln("RTL8168: I/O port base: {}", m_io_base); + dmesgln("RTL8168: I/O port base: {}", m_registers_io_window); identify_chip_version(); dmesgln("RTL8168: Version detected - {} ({}{})", possible_device_name(), (u8)m_version, m_version_uncertain ? "?" : ""); @@ -1258,39 +1259,39 @@ void RTL8168NetworkAdapter::receive() void RTL8168NetworkAdapter::out8(u16 address, u8 data) { - m_io_base.offset(address).out(data); + m_registers_io_window->write8(address, data); } void RTL8168NetworkAdapter::out16(u16 address, u16 data) { - m_io_base.offset(address).out(data); + m_registers_io_window->write16(address, data); } void RTL8168NetworkAdapter::out32(u16 address, u32 data) { - m_io_base.offset(address).out(data); + m_registers_io_window->write32(address, data); } void RTL8168NetworkAdapter::out64(u16 address, u64 data) { // ORDER MATTERS: Some NICs require the high part of the address to be written first - m_io_base.offset(address + 4).out((u32)(data >> 32)); - m_io_base.offset(address).out((u32)(data & 0xFFFFFFFF)); + m_registers_io_window->write32(address + 4, (u32)(data >> 32)); + m_registers_io_window->write32(address, (u32)(data & 0xFFFFFFFF)); } u8 RTL8168NetworkAdapter::in8(u16 address) { - return m_io_base.offset(address).in(); + return m_registers_io_window->read8(address); } u16 RTL8168NetworkAdapter::in16(u16 address) { - return m_io_base.offset(address).in(); + return m_registers_io_window->read16(address); } u32 RTL8168NetworkAdapter::in32(u16 address) { - return m_io_base.offset(address).in(); + return m_registers_io_window->read32(address); } void RTL8168NetworkAdapter::phy_out(u8 address, u16 data) diff --git a/Kernel/Net/Realtek/RTL8168NetworkAdapter.h b/Kernel/Net/Realtek/RTL8168NetworkAdapter.h index 9eb9ed6c19..a0ca643c2b 100644 --- a/Kernel/Net/Realtek/RTL8168NetworkAdapter.h +++ b/Kernel/Net/Realtek/RTL8168NetworkAdapter.h @@ -8,9 +8,9 @@ #include #include -#include #include #include +#include #include #include #include @@ -38,7 +38,7 @@ private: static constexpr size_t number_of_rx_descriptors = 64; static constexpr size_t number_of_tx_descriptors = 16; - RTL8168NetworkAdapter(PCI::Address, u8 irq, NonnullOwnPtr); + RTL8168NetworkAdapter(PCI::Address, u8 irq, NonnullOwnPtr registers_io_window, NonnullOwnPtr); virtual bool handle_irq(RegisterState const&) override; virtual StringView class_name() const override { return "RTL8168NetworkAdapter"sv; } @@ -199,7 +199,7 @@ private: ChipVersion m_version { ChipVersion::Unknown }; bool m_version_uncertain { true }; - IOAddress m_io_base; + NonnullOwnPtr m_registers_io_window; u32 m_ocp_base_address { 0 }; OwnPtr m_rx_descriptors_region; NonnullOwnPtrVector m_rx_buffers_regions; diff --git a/Kernel/Storage/ATA/GenericIDE/Channel.cpp b/Kernel/Storage/ATA/GenericIDE/Channel.cpp index c003e30b75..06750e9679 100644 --- a/Kernel/Storage/ATA/GenericIDE/Channel.cpp +++ b/Kernel/Storage/ATA/GenericIDE/Channel.cpp @@ -8,8 +8,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -24,16 +24,16 @@ namespace Kernel { #define PATA_PRIMARY_IRQ 14 #define PATA_SECONDARY_IRQ 15 -UNMAP_AFTER_INIT NonnullLockRefPtr IDEChannel::create(IDEController const& controller, IOAddressGroup io_group, ChannelType type) +UNMAP_AFTER_INIT NonnullLockRefPtr IDEChannel::create(IDEController const& controller, IOWindowGroup io_window_group, ChannelType type) { auto ata_identify_data_buffer = KBuffer::try_create_with_size("ATA Identify Page"sv, 4096, Memory::Region::Access::ReadWrite, AllocationStrategy::AllocateNow).release_value(); - return adopt_lock_ref(*new IDEChannel(controller, io_group, type, move(ata_identify_data_buffer))); + return adopt_lock_ref(*new IDEChannel(controller, move(io_window_group), type, move(ata_identify_data_buffer))); } -UNMAP_AFTER_INIT NonnullLockRefPtr IDEChannel::create(IDEController const& controller, u8 irq, IOAddressGroup io_group, ChannelType type) +UNMAP_AFTER_INIT NonnullLockRefPtr IDEChannel::create(IDEController const& controller, u8 irq, IOWindowGroup io_window_group, ChannelType type) { auto ata_identify_data_buffer = KBuffer::try_create_with_size("ATA Identify Page"sv, 4096, Memory::Region::Access::ReadWrite, AllocationStrategy::AllocateNow).release_value(); - return adopt_lock_ref(*new IDEChannel(controller, irq, io_group, type, move(ata_identify_data_buffer))); + return adopt_lock_ref(*new IDEChannel(controller, irq, move(io_window_group), type, move(ata_identify_data_buffer))); } StringView IDEChannel::channel_type_string() const @@ -47,10 +47,10 @@ bool IDEChannel::select_device_and_wait_until_not_busy(DeviceType device_type, s { microseconds_delay(20); u8 slave = device_type == DeviceType::Slave; - m_io_group.io_base().offset(ATA_REG_HDDEVSEL).out(0xA0 | (slave << 4)); // First, we need to select the drive itself + m_io_window_group.io_window().write8(ATA_REG_HDDEVSEL, 0xA0 | (slave << 4)); // First, we need to select the drive itself microseconds_delay(20); size_t time_elapsed = 0; - while (m_io_group.control_base().in() & ATA_SR_BSY && time_elapsed <= milliseconds_timeout) { + while (m_io_window_group.control_window().read8(0) & ATA_SR_BSY && time_elapsed <= milliseconds_timeout) { microseconds_delay(1000); time_elapsed++; } @@ -62,13 +62,13 @@ ErrorOr IDEChannel::port_phy_reset() MutexLocker locker(m_lock); SpinlockLocker hard_locker(m_hard_lock); // reset the channel - u8 device_control = m_io_group.control_base().in(); + u8 device_control = m_io_window_group.control_window().read8(0); // Wait 30 milliseconds microseconds_delay(30000); - m_io_group.control_base().out(device_control | (1 << 2)); + m_io_window_group.control_window().write8(0, device_control | (1 << 2)); // Wait 30 milliseconds microseconds_delay(30000); - m_io_group.control_base().out(device_control); + m_io_window_group.control_window().write8(0, device_control); // Wait up to 30 seconds before failing if (!select_device_and_wait_until_not_busy(DeviceType::Master, 30000)) { dbgln("IDEChannel: reset failed, busy flag on master stuck"); @@ -95,16 +95,16 @@ ErrorOr IDEChannel::allocate_resources_for_isa_ide_controller(Badge IDEChannel::allocate_resources(bool force_pio) { - dbgln_if(PATA_DEBUG, "IDEChannel: {} IO base: {}", channel_type_string(), m_io_group.io_base()); - dbgln_if(PATA_DEBUG, "IDEChannel: {} control base: {}", channel_type_string(), m_io_group.control_base()); - if (m_io_group.bus_master_base().has_value()) - dbgln_if(PATA_DEBUG, "IDEChannel: {} bus master base: {}", channel_type_string(), m_io_group.bus_master_base().value()); + dbgln_if(PATA_DEBUG, "IDEChannel: {} IO base: {}", channel_type_string(), m_io_window_group.io_window()); + dbgln_if(PATA_DEBUG, "IDEChannel: {} control base: {}", channel_type_string(), m_io_window_group.control_window()); + if (m_io_window_group.bus_master_window()) + dbgln_if(PATA_DEBUG, "IDEChannel: {} bus master base: {}", channel_type_string(), m_io_window_group.bus_master_window()); else dbgln_if(PATA_DEBUG, "IDEChannel: {} bus master base disabled", channel_type_string()); if (!force_pio) { m_dma_enabled = true; - VERIFY(m_io_group.bus_master_base().has_value()); + VERIFY(m_io_window_group.bus_master_window()); // Let's try to set up DMA transfers. m_prdt_region = TRY(MM.allocate_dma_buffer_page("IDE PRDT"sv, Memory::Region::Access::ReadWrite, m_prdt_page)); @@ -115,24 +115,24 @@ UNMAP_AFTER_INIT ErrorOr IDEChannel::allocate_resources(bool force_pio) prdt().end_of_table = 0x8000; // clear bus master interrupt status - m_io_group.bus_master_base().value().offset(2).out(m_io_group.bus_master_base().value().offset(2).in() | 4); + m_io_window_group.bus_master_window()->write8(2, m_io_window_group.bus_master_window()->read8(2) | 4); } return {}; } -UNMAP_AFTER_INIT IDEChannel::IDEChannel(IDEController const& controller, u8 irq, IOAddressGroup io_group, ChannelType type, NonnullOwnPtr ata_identify_data_buffer) +UNMAP_AFTER_INIT IDEChannel::IDEChannel(IDEController const& controller, u8 irq, IOWindowGroup io_group, ChannelType type, NonnullOwnPtr ata_identify_data_buffer) : ATAPort(controller, (type == ChannelType::Primary ? 0 : 1), move(ata_identify_data_buffer)) , IRQHandler(irq) , m_channel_type(type) - , m_io_group(io_group) + , m_io_window_group(move(io_group)) { } -UNMAP_AFTER_INIT IDEChannel::IDEChannel(IDEController const& controller, IOAddressGroup io_group, ChannelType type, NonnullOwnPtr ata_identify_data_buffer) +UNMAP_AFTER_INIT IDEChannel::IDEChannel(IDEController const& controller, IOWindowGroup io_group, ChannelType type, NonnullOwnPtr ata_identify_data_buffer) : ATAPort(controller, (type == ChannelType::Primary ? 0 : 1), move(ata_identify_data_buffer)) , IRQHandler(type == ChannelType::Primary ? PATA_PRIMARY_IRQ : PATA_SECONDARY_IRQ) , m_channel_type(type) - , m_io_group(io_group) + , m_io_window_group(move(io_group)) { } @@ -149,36 +149,37 @@ bool IDEChannel::handle_irq(RegisterState const&) ErrorOr IDEChannel::stop_busmastering() { VERIFY(m_lock.is_locked()); - VERIFY(m_io_group.bus_master_base().has_value()); - m_io_group.bus_master_base().value().out(0); + VERIFY(m_io_window_group.bus_master_window()); + m_io_window_group.bus_master_window()->write8(0, 0); return {}; } ErrorOr IDEChannel::start_busmastering(TransactionDirection direction) { VERIFY(m_lock.is_locked()); - VERIFY(m_io_group.bus_master_base().has_value()); - m_io_group.bus_master_base().value().out(direction != TransactionDirection::Write ? 0x9 : 0x1); + VERIFY(m_io_window_group.bus_master_window()); + m_io_window_group.bus_master_window()->write8(0, (direction != TransactionDirection::Write ? 0x9 : 0x1)); return {}; } ErrorOr IDEChannel::force_busmastering_status_clean() { VERIFY(m_lock.is_locked()); - VERIFY(m_io_group.bus_master_base().has_value()); - m_io_group.bus_master_base().value().offset(2).out(m_io_group.bus_master_base().value().offset(2).in() | 4); + VERIFY(m_io_window_group.bus_master_window()); + m_io_window_group.bus_master_window()->write8(2, m_io_window_group.bus_master_window()->read8(2) | 4); return {}; } ErrorOr IDEChannel::busmastering_status() { - VERIFY(m_io_group.bus_master_base().has_value()); - return m_io_group.bus_master_base().value().offset(2).in(); + VERIFY(m_io_window_group.bus_master_window()); + return m_io_window_group.bus_master_window()->read8(2); } ErrorOr IDEChannel::prepare_transaction_with_busmastering(TransactionDirection direction, PhysicalAddress prdt_buffer) { VERIFY(m_lock.is_locked()); - m_io_group.bus_master_base().value().offset(4).out(prdt_buffer.get()); - m_io_group.bus_master_base().value().out(direction != TransactionDirection::Write ? 0x8 : 0); + m_io_window_group.bus_master_window()->write32(4, prdt_buffer.get()); + m_io_window_group.bus_master_window()->write8(0, direction != TransactionDirection::Write ? 0x8 : 0); + // Turn on "Interrupt" and "Error" flag. The error flag should be cleared by hardware. - m_io_group.bus_master_base().value().offset(2).out(m_io_group.bus_master_base().value().offset(2).in() | 0x6); + m_io_window_group.bus_master_window()->write8(2, m_io_window_group.bus_master_window()->read8(2) | 0x6); return {}; } ErrorOr IDEChannel::initiate_transaction(TransactionDirection) @@ -190,29 +191,29 @@ ErrorOr IDEChannel::initiate_transaction(TransactionDirection) ErrorOr IDEChannel::task_file_status() { VERIFY(m_lock.is_locked()); - return m_io_group.control_base().in(); + return m_io_window_group.control_window().read8(0); } ErrorOr IDEChannel::task_file_error() { VERIFY(m_lock.is_locked()); - return m_io_group.io_base().offset(ATA_REG_ERROR).in(); + return m_io_window_group.io_window().read8(ATA_REG_ERROR); } ErrorOr IDEChannel::detect_presence_on_selected_device() { VERIFY(m_lock.is_locked()); - m_io_group.io_base().offset(ATA_REG_SECCOUNT0).out(0x55); - m_io_group.io_base().offset(ATA_REG_LBA0).out(0xAA); + m_io_window_group.io_window().write8(ATA_REG_SECCOUNT0, 0x55); + m_io_window_group.io_window().write8(ATA_REG_LBA0, 0xAA); - m_io_group.io_base().offset(ATA_REG_SECCOUNT0).out(0xAA); - m_io_group.io_base().offset(ATA_REG_LBA0).out(0x55); + m_io_window_group.io_window().write8(ATA_REG_SECCOUNT0, 0xAA); + m_io_window_group.io_window().write8(ATA_REG_LBA0, 0x55); - m_io_group.io_base().offset(ATA_REG_SECCOUNT0).out(0x55); - m_io_group.io_base().offset(ATA_REG_LBA0).out(0xAA); + m_io_window_group.io_window().write8(ATA_REG_SECCOUNT0, 0x55); + m_io_window_group.io_window().write8(ATA_REG_LBA0, 0xAA); - auto nsectors_value = m_io_group.io_base().offset(ATA_REG_SECCOUNT0).in(); - auto lba0 = m_io_group.io_base().offset(ATA_REG_LBA0).in(); + auto nsectors_value = m_io_window_group.io_window().read8(ATA_REG_SECCOUNT0); + auto lba0 = m_io_window_group.io_window().read8(ATA_REG_LBA0); if (lba0 == 0xAA && nsectors_value == 0x55) return true; @@ -222,7 +223,7 @@ ErrorOr IDEChannel::detect_presence_on_selected_device() ErrorOr IDEChannel::wait_if_busy_until_timeout(size_t timeout_in_milliseconds) { size_t time_elapsed = 0; - while (m_io_group.control_base().in() & ATA_SR_BSY && time_elapsed <= timeout_in_milliseconds) { + while (m_io_window_group.control_window().read8(0) & ATA_SR_BSY && time_elapsed <= timeout_in_milliseconds) { microseconds_delay(1000); time_elapsed++; } @@ -234,7 +235,7 @@ ErrorOr IDEChannel::wait_if_busy_until_timeout(size_t timeout_in_milliseco ErrorOr IDEChannel::force_clear_interrupts() { VERIFY(m_lock.is_locked()); - m_io_group.io_base().offset(ATA_REG_STATUS).in(); + m_io_window_group.io_window().read8(ATA_REG_STATUS); return {}; } @@ -250,21 +251,21 @@ ErrorOr IDEChannel::load_taskfile_into_registers(ATAPort::TaskFile const& } // Note: Preserve the selected drive, always use LBA addressing - auto driver_register = ((m_io_group.io_base().offset(ATA_REG_HDDEVSEL).in() & (1 << 4)) | (head | (1 << 5) | (1 << 6))); - m_io_group.io_base().offset(ATA_REG_HDDEVSEL).out(driver_register); + auto driver_register = ((m_io_window_group.io_window().read8(ATA_REG_HDDEVSEL) & (1 << 4)) | (head | (1 << 5) | (1 << 6))); + m_io_window_group.io_window().write8(ATA_REG_HDDEVSEL, driver_register); microseconds_delay(50); if (lba_mode == LBAMode::FortyEightBit) { - m_io_group.io_base().offset(ATA_REG_SECCOUNT1).out((task_file.count >> 8) & 0xFF); - m_io_group.io_base().offset(ATA_REG_LBA3).out(task_file.lba_high[0]); - m_io_group.io_base().offset(ATA_REG_LBA4).out(task_file.lba_high[1]); - m_io_group.io_base().offset(ATA_REG_LBA5).out(task_file.lba_high[2]); + m_io_window_group.io_window().write8(ATA_REG_SECCOUNT1, (task_file.count >> 8) & 0xFF); + m_io_window_group.io_window().write8(ATA_REG_LBA3, task_file.lba_high[0]); + m_io_window_group.io_window().write8(ATA_REG_LBA4, task_file.lba_high[1]); + m_io_window_group.io_window().write8(ATA_REG_LBA5, task_file.lba_high[2]); } - m_io_group.io_base().offset(ATA_REG_SECCOUNT0).out(task_file.count & 0xFF); - m_io_group.io_base().offset(ATA_REG_LBA0).out(task_file.lba_low[0]); - m_io_group.io_base().offset(ATA_REG_LBA1).out(task_file.lba_low[1]); - m_io_group.io_base().offset(ATA_REG_LBA2).out(task_file.lba_low[2]); + m_io_window_group.io_window().write8(ATA_REG_SECCOUNT0, task_file.count & 0xFF); + m_io_window_group.io_window().write8(ATA_REG_LBA0, task_file.lba_low[0]); + m_io_window_group.io_window().write8(ATA_REG_LBA1, task_file.lba_low[1]); + m_io_window_group.io_window().write8(ATA_REG_LBA2, task_file.lba_low[2]); // FIXME: Set a timeout here? size_t time_elapsed = 0; @@ -272,13 +273,13 @@ ErrorOr IDEChannel::load_taskfile_into_registers(ATAPort::TaskFile const& if (time_elapsed > completion_timeout_in_milliseconds) return Error::from_errno(EBUSY); // FIXME: Use task_file_status method - auto status = m_io_group.control_base().in(); + auto status = m_io_window_group.control_window().read8(0); if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRDY)) break; microseconds_delay(1000); time_elapsed++; } - m_io_group.io_base().offset(ATA_REG_COMMAND).out(task_file.command); + m_io_window_group.io_window().write8(ATA_REG_COMMAND, task_file.command); return {}; } @@ -288,7 +289,7 @@ ErrorOr IDEChannel::device_select(size_t device_index) if (device_index > 1) return Error::from_errno(EINVAL); microseconds_delay(20); - m_io_group.io_base().offset(ATA_REG_HDDEVSEL).out(0xA0 | ((device_index) << 4)); + m_io_window_group.io_window().write8(ATA_REG_HDDEVSEL, (0xA0 | ((device_index) << 4))); microseconds_delay(20); return {}; } @@ -296,14 +297,14 @@ ErrorOr IDEChannel::device_select(size_t device_index) ErrorOr IDEChannel::enable_interrupts() { VERIFY(m_lock.is_locked()); - m_io_group.control_base().out(0); + m_io_window_group.control_window().write8(0, 0); m_interrupts_enabled = true; return {}; } ErrorOr IDEChannel::disable_interrupts() { VERIFY(m_lock.is_locked()); - m_io_group.control_base().out(1 << 1); + m_io_window_group.control_window().write8(0, 1 << 1); m_interrupts_enabled = false; return {}; } @@ -313,7 +314,7 @@ ErrorOr IDEChannel::read_pio_data_to_buffer(UserOrKernelBuffer& buffer, si VERIFY(m_lock.is_locked()); VERIFY(words_count == 256); for (u32 i = 0; i < 256; ++i) { - u16 data = m_io_group.io_base().offset(ATA_REG_DATA).in(); + u16 data = m_io_window_group.io_window().read16(ATA_REG_DATA); // FIXME: Don't assume 512 bytes sector TRY(buffer.write(&data, block_offset * 512 + (i * 2), 2)); } @@ -327,7 +328,7 @@ ErrorOr IDEChannel::write_pio_data_from_buffer(UserOrKernelBuffer const& b u16 buf; // FIXME: Don't assume 512 bytes sector TRY(buffer.read(&buf, block_offset * 512 + (i * 2), 2)); - IO::out16(m_io_group.io_base().offset(ATA_REG_DATA).get(), buf); + m_io_window_group.io_window().write16(ATA_REG_DATA, buf); } return {}; } diff --git a/Kernel/Storage/ATA/GenericIDE/Channel.h b/Kernel/Storage/ATA/GenericIDE/Channel.h index 8199d92119..0b968d6d86 100644 --- a/Kernel/Storage/ATA/GenericIDE/Channel.h +++ b/Kernel/Storage/ATA/GenericIDE/Channel.h @@ -18,8 +18,8 @@ #pragma once #include -#include #include +#include #include #include #include @@ -56,57 +56,40 @@ public: Slave, }; - struct IOAddressGroup { - IOAddressGroup(IOAddress io_base, IOAddress control_base, IOAddress bus_master_base) - : m_io_base(io_base) - , m_control_base(control_base) - , m_bus_master_base(bus_master_base) + struct IOWindowGroup { + IOWindowGroup(NonnullOwnPtr io_window, NonnullOwnPtr control_window, NonnullOwnPtr m_bus_master_window) + : m_io_window(move(io_window)) + , m_control_window(move(control_window)) + , m_bus_master_window(move(m_bus_master_window)) { } - IOAddressGroup(IOAddress io_base, IOAddress control_base, Optional bus_master_base) - : m_io_base(io_base) - , m_control_base(control_base) - , m_bus_master_base(bus_master_base) + IOWindowGroup(NonnullOwnPtr io_window, NonnullOwnPtr control_window) + : m_io_window(move(io_window)) + , m_control_window(move(control_window)) { } - IOAddressGroup(IOAddress io_base, IOAddress control_base) - : m_io_base(io_base) - , m_control_base(control_base) - , m_bus_master_base() - { - } - - IOAddressGroup(IOAddressGroup const& other, IOAddress bus_master_base) - : m_io_base(other.io_base()) - , m_control_base(other.control_base()) - , m_bus_master_base(bus_master_base) - { - } - - IOAddressGroup(IOAddressGroup const&) = default; - // Disable default implementations that would use surprising integer promotion. - bool operator==(IOAddressGroup const&) const = delete; - bool operator<=(IOAddressGroup const&) const = delete; - bool operator>=(IOAddressGroup const&) const = delete; - bool operator<(IOAddressGroup const&) const = delete; - bool operator>(IOAddressGroup const&) const = delete; + bool operator==(IOWindowGroup const&) const = delete; + bool operator<=(IOWindowGroup const&) const = delete; + bool operator>=(IOWindowGroup const&) const = delete; + bool operator<(IOWindowGroup const&) const = delete; + bool operator>(IOWindowGroup const&) const = delete; - IOAddress io_base() const { return m_io_base; }; - IOAddress control_base() const { return m_control_base; } - Optional bus_master_base() const { return m_bus_master_base; } + IOWindow& io_window() const { return *m_io_window; }; + IOWindow& control_window() const { return *m_control_window; } + IOWindow* bus_master_window() const { return m_bus_master_window.ptr(); } private: - IOAddress m_io_base; - IOAddress m_control_base; - Optional m_bus_master_base; + mutable NonnullOwnPtr m_io_window; + mutable NonnullOwnPtr m_control_window; + mutable OwnPtr m_bus_master_window; }; public: - static NonnullLockRefPtr create(IDEController const&, IOAddressGroup, ChannelType type); - static NonnullLockRefPtr create(IDEController const&, u8 irq, IOAddressGroup, ChannelType type); + static NonnullLockRefPtr create(IDEController const&, IOWindowGroup, ChannelType type); + static NonnullLockRefPtr create(IDEController const&, u8 irq, IOWindowGroup, ChannelType type); virtual ~IDEChannel() override; @@ -157,8 +140,8 @@ private: virtual ErrorOr read_pio_data_to_buffer(UserOrKernelBuffer&, size_t block_offset, size_t words_count) override; virtual ErrorOr write_pio_data_from_buffer(UserOrKernelBuffer const&, size_t block_offset, size_t words_count) override; - IDEChannel(IDEController const&, IOAddressGroup, ChannelType type, NonnullOwnPtr ata_identify_data_buffer); - IDEChannel(IDEController const&, u8 irq, IOAddressGroup, ChannelType type, NonnullOwnPtr ata_identify_data_buffer); + IDEChannel(IDEController const&, IOWindowGroup, ChannelType type, NonnullOwnPtr ata_identify_data_buffer); + IDEChannel(IDEController const&, u8 irq, IOWindowGroup, ChannelType type, NonnullOwnPtr ata_identify_data_buffer); //^ IRQHandler virtual bool handle_irq(RegisterState const&) override; @@ -168,6 +151,6 @@ private: bool m_dma_enabled { false }; bool m_interrupts_enabled { true }; - IOAddressGroup m_io_group; + IOWindowGroup m_io_window_group; }; }