mirror of
https://github.com/RGBCube/serenity
synced 2025-05-14 09:14:58 +00:00
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.
This commit is contained in:
parent
6bafbd64e2
commit
05ba034000
36 changed files with 919 additions and 469 deletions
|
@ -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");
|
||||
|
|
51
Kernel/Arch/x86/ISABus/SerialDevice.cpp
Normal file
51
Kernel/Arch/x86/ISABus/SerialDevice.cpp
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <Kernel/Devices/DeviceManagement.h>
|
||||
#include <Kernel/Devices/SerialDevice.h>
|
||||
#include <Kernel/IOWindow.h>
|
||||
#include <Kernel/Sections.h>
|
||||
|
||||
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> 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<SerialDevice> 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<SerialDevice>(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<SerialDevice>(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<SerialDevice>(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<SerialDevice>(move(io_window), 67).release_value();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return serial_device.release_nonnull();
|
||||
}
|
||||
|
||||
}
|
|
@ -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<u8>() >> 5) & 0b11));
|
||||
dbgln("IDE controller @ {}: secondary channel DMA capable? {}", pci_address(), ((bus_master_base.offset(2 + 8).in<u8>() >> 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<IOWindow> primary_base_io_window;
|
||||
OwnPtr<IOWindow> 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<IOWindow> secondary_base_io_window;
|
||||
OwnPtr<IOWindow> 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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -66,7 +66,8 @@ static constexpr u16 UHCI_NUMBER_OF_FRAMES = 1024;
|
|||
ErrorOr<NonnullLockRefPtr<UHCIController>> 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<NonnullLockRefPtr<UHCIController>> UHCIController::try_to_initialize(PCI
|
|||
ErrorOr<void> 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<void> 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<IOWindow> 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)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -10,12 +10,12 @@
|
|||
#include <AK/Array.h>
|
||||
#include <AK/NonnullOwnPtr.h>
|
||||
#include <AK/Platform.h>
|
||||
#include <Kernel/Arch/x86/IO.h>
|
||||
#include <Kernel/Bus/PCI/Device.h>
|
||||
#include <Kernel/Bus/USB/UHCI/UHCIDescriptorPool.h>
|
||||
#include <Kernel/Bus/USB/UHCI/UHCIDescriptorTypes.h>
|
||||
#include <Kernel/Bus/USB/UHCI/UHCIRootHub.h>
|
||||
#include <Kernel/Bus/USB/USBController.h>
|
||||
#include <Kernel/IOWindow.h>
|
||||
#include <Kernel/Interrupts/IRQHandler.h>
|
||||
#include <Kernel/Locking/Spinlock.h>
|
||||
#include <Kernel/Memory/AnonymousVMObject.h>
|
||||
|
@ -54,25 +54,25 @@ public:
|
|||
ErrorOr<void> clear_port_feature(Badge<UHCIRootHub>, u8, HubFeatureSelector);
|
||||
|
||||
private:
|
||||
explicit UHCIController(PCI::DeviceIdentifier const& pci_device_identifier);
|
||||
UHCIController(PCI::DeviceIdentifier const& pci_device_identifier, NonnullOwnPtr<IOWindow> registers_io_window);
|
||||
|
||||
u16 read_usbcmd() { return m_io_base.offset(0).in<u16>(); }
|
||||
u16 read_usbsts() { return m_io_base.offset(0x2).in<u16>(); }
|
||||
u16 read_usbintr() { return m_io_base.offset(0x4).in<u16>(); }
|
||||
u16 read_frnum() { return m_io_base.offset(0x6).in<u16>(); }
|
||||
u32 read_flbaseadd() { return m_io_base.offset(0x8).in<u32>(); }
|
||||
u8 read_sofmod() { return m_io_base.offset(0xc).in<u8>(); }
|
||||
u16 read_portsc1() { return m_io_base.offset(0x10).in<u16>(); }
|
||||
u16 read_portsc2() { return m_io_base.offset(0x12).in<u16>(); }
|
||||
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<IOWindow> m_registers_io_window;
|
||||
|
||||
Spinlock m_schedule_lock;
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <Kernel/Bus/USB/USBHub.h>
|
||||
#include <Kernel/Bus/USB/USBRequest.h>
|
||||
#include <Kernel/FileSystem/SysFS/Subsystems/Bus/USB/BusDirectory.h>
|
||||
#include <Kernel/IOWindow.h>
|
||||
#include <Kernel/StdLib.h>
|
||||
|
||||
namespace Kernel::USB {
|
||||
|
|
|
@ -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<PCI::HeaderType0BaseRegister>(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<PCI::HeaderType0BaseRegister>(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<PCI::HeaderType0BaseRegister>(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<u16>(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<u8>(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<u16>(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<u32>(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<u8>(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<u8>(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<u8>(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<u32>(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<u32>(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<u8>(REG_ISR_STATUS);
|
||||
return base_io_window().read8(REG_ISR_STATUS);
|
||||
return config_read8(*m_isr_cfg, 0);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,10 +7,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/NonnullOwnPtrVector.h>
|
||||
#include <Kernel/Arch/x86/IO.h>
|
||||
#include <Kernel/Bus/PCI/Access.h>
|
||||
#include <Kernel/Bus/PCI/Device.h>
|
||||
#include <Kernel/Bus/VirtIO/Queue.h>
|
||||
#include <Kernel/IOWindow.h>
|
||||
#include <Kernel/Interrupts/IRQHandler.h>
|
||||
#include <Kernel/Memory/MemoryManager.h>
|
||||
|
||||
|
@ -94,30 +94,6 @@ public:
|
|||
protected:
|
||||
virtual StringView class_name() const { return "VirtIO::Device"sv; }
|
||||
explicit Device(PCI::DeviceIdentifier const&);
|
||||
struct MappedMMIO {
|
||||
OwnPtr<Memory::Region> base;
|
||||
size_t size { 0 };
|
||||
|
||||
template<typename T>
|
||||
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<typename T>
|
||||
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<typename T>
|
||||
void out(u16 address, T value)
|
||||
{
|
||||
m_io_base.offset(address).out(value);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T in(u16 address)
|
||||
{
|
||||
return m_io_base.offset(address).in<T>();
|
||||
}
|
||||
|
||||
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<OwnPtr<IOWindow>, 6> m_register_bases;
|
||||
|
||||
StringView const m_class_name;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -23,18 +23,24 @@ static constexpr u16 pcm_sample_rate_maximum = 48000;
|
|||
|
||||
UNMAP_AFTER_INIT ErrorOr<NonnullLockRefPtr<AC97>> 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<AC97Channel> pcm_out_channel, NonnullOwnPtr<IOWindow> mixer_io_window, NonnullOwnPtr<IOWindow> 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<u16>();
|
||||
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<void> 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<u16>();
|
||||
auto extended_audio_id = m_mixer_io_window->read16(NativeAudioMixerRegister::ExtendedAudioID);
|
||||
m_codec_revision = static_cast<AC97Revision>(((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<u16>() << 16 | m_io_mixer_base.offset(NativeAudioMixerRegister::VendorID2).in<u16>();
|
||||
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<u32>();
|
||||
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<u16>(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<u16>();
|
||||
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<void> 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<void> 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<void> AC97::set_pcm_output_sample_rate(u32 sample_rate)
|
||||
|
@ -146,15 +150,14 @@ ErrorOr<void> 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<u16>(shifted_sample_rate);
|
||||
m_sample_rate = static_cast<u32>(pcm_front_dac_rate_register.in<u16>()) << double_rate_shift;
|
||||
m_mixer_io_window->write16(NativeAudioMixerRegister::PCMFrontDACRate, shifted_sample_rate);
|
||||
m_sample_rate = static_cast<u32>(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<AudioChannel> AC97::audio_channel(u32 index) const
|
||||
|
@ -226,14 +229,14 @@ ErrorOr<void> 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<u16>();
|
||||
auto current_index = m_pcm_out_channel.reg(AC97Channel::Register::CurrentIndexValue).in<u8>();
|
||||
int last_valid_index = m_pcm_out_channel.reg(AC97Channel::Register::LastValidIndex).in<u8>();
|
||||
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<void> 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<void> 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<u32>(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<void> AC97::write_single_buffer(UserOrKernelBuffer const& data, size_t o
|
|||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<NonnullOwnPtr<AC97::AC97Channel>> AC97::AC97Channel::create_with_parent_pci_device(PCI::Address pci_device_address, StringView name, NonnullOwnPtr<IOWindow> 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<u8>() & 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<u8>();
|
||||
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;
|
||||
|
|
|
@ -7,11 +7,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/Error.h>
|
||||
#include <Kernel/Arch/x86/IO.h>
|
||||
#include <Kernel/Bus/PCI/API.h>
|
||||
#include <Kernel/Bus/PCI/Device.h>
|
||||
#include <Kernel/Devices/Audio/Controller.h>
|
||||
#include <Kernel/Devices/CharacterDevice.h>
|
||||
#include <Kernel/IOWindow.h>
|
||||
#include <Kernel/Interrupts/IRQHandler.h>
|
||||
#include <Kernel/Locking/SpinlockProtected.h>
|
||||
|
||||
|
@ -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<NonnullOwnPtr<AC97Channel>> create_with_parent_pci_device(PCI::Address pci_device_address, StringView name, NonnullOwnPtr<IOWindow> 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<IOWindow> channel_io_base)
|
||||
: m_channel_io_window(move(channel_io_base))
|
||||
, m_device_pci_address(pci_device_address)
|
||||
, m_name(name)
|
||||
{
|
||||
}
|
||||
|
||||
NonnullOwnPtr<IOWindow> m_channel_io_window;
|
||||
PCI::Address m_device_pci_address;
|
||||
SpinlockProtected<bool> m_dma_running { LockRank::None, false };
|
||||
StringView m_name;
|
||||
};
|
||||
|
||||
explicit AC97(PCI::DeviceIdentifier const&);
|
||||
AC97(PCI::DeviceIdentifier const&, NonnullOwnPtr<AC97Channel> pcm_out_channel, NonnullOwnPtr<IOWindow> mixer_io_window, NonnullOwnPtr<IOWindow> 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<void> initialize();
|
||||
void set_master_output_volume(u8, u8, Muted);
|
||||
ErrorOr<void> 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<IOWindow> m_mixer_io_window;
|
||||
NonnullOwnPtr<IOWindow> m_bus_io_window;
|
||||
WaitQueue m_irq_queue;
|
||||
OwnPtr<Memory::Region> m_output_buffer;
|
||||
u8 m_output_buffer_page_count { 4 };
|
||||
u8 m_output_buffer_page_index { 0 };
|
||||
AC97Channel m_pcm_out_channel;
|
||||
NonnullOwnPtr<AC97Channel> m_pcm_out_channel;
|
||||
u32 m_sample_rate { 0 };
|
||||
bool m_variable_rate_pcm_supported { false };
|
||||
LockRefPtr<AudioChannel> m_audio_channel;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <Kernel/Bus/PCI/API.h>
|
||||
#include <Kernel/Devices/PCISerialDevice.h>
|
||||
#include <Kernel/IOWindow.h>
|
||||
#include <Kernel/Sections.h>
|
||||
|
||||
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<PCI::HeaderType0BaseRegister>(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<PCI::HeaderType0BaseRegister>(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);
|
||||
|
||||
|
|
|
@ -5,49 +5,16 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <Kernel/Arch/x86/IO.h>
|
||||
#include <Kernel/Devices/DeviceManagement.h>
|
||||
#include <Kernel/Devices/SerialDevice.h>
|
||||
#include <Kernel/IOWindow.h>
|
||||
#include <Kernel/Sections.h>
|
||||
|
||||
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> 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<SerialDevice> serial_device;
|
||||
switch (com_number) {
|
||||
case 0: {
|
||||
serial_device = DeviceManagement::try_create_device<SerialDevice>(IOAddress(SERIAL_COM1_ADDR), 64).release_value();
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
serial_device = DeviceManagement::try_create_device<SerialDevice>(IOAddress(SERIAL_COM2_ADDR), 65).release_value();
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
serial_device = DeviceManagement::try_create_device<SerialDevice>(IOAddress(SERIAL_COM3_ADDR), 66).release_value();
|
||||
break;
|
||||
}
|
||||
case 3: {
|
||||
serial_device = DeviceManagement::try_create_device<SerialDevice>(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<IOWindow> 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<size_t> SerialDevice::read(OpenFileDescription&, u64, UserOrKernelBuffer
|
|||
|
||||
return buffer.write_buffered<128>(size, [&](Bytes bytes) {
|
||||
for (auto& byte : bytes)
|
||||
byte = m_base_addr.in<u8>();
|
||||
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<u8>('\r');
|
||||
m_registers_io_window->write8(0, '\r');
|
||||
|
||||
m_base_addr.out<u8>(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<u8>(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<u8>(m_base_addr.offset(3).in<u8>() | 0x80); // turn on DLAB
|
||||
m_base_addr.out<u8>(((u8)(baud)) & 0xff); // lower half of divisor
|
||||
m_base_addr.offset(1).out<u8>(((u8)(baud)) >> 2); // upper half of divisor
|
||||
m_base_addr.offset(3).out<u8>(m_base_addr.offset(3).in<u8>() & 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<u8>(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<u8>((m_base_addr.offset(3).in<u8>() & ~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<u8>(m_base_addr.offset(3).in<u8>() & (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<u8>(modem_control);
|
||||
m_registers_io_window->write8(4, modem_control);
|
||||
}
|
||||
|
||||
u8 SerialDevice::get_line_status() const
|
||||
{
|
||||
return m_base_addr.offset(5).in<u8>();
|
||||
return m_registers_io_window->read8(5);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <Kernel/Arch/x86/IO.h>
|
||||
#include <Kernel/Devices/CharacterDevice.h>
|
||||
#include <Kernel/IOWindow.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
|
@ -104,7 +104,7 @@ public:
|
|||
};
|
||||
|
||||
private:
|
||||
SerialDevice(IOAddress base_addr, unsigned minor);
|
||||
SerialDevice(NonnullOwnPtr<IOWindow> 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<IOWindow> m_registers_io_window;
|
||||
bool m_interrupt_enable { false };
|
||||
u8 m_fifo_control { 0 };
|
||||
Baud m_baud { Baud38400 };
|
||||
|
|
|
@ -7,10 +7,13 @@
|
|||
*/
|
||||
|
||||
#include <AK/Format.h>
|
||||
#include <AK/Platform.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <AK/Try.h>
|
||||
#include <Kernel/Arch/InterruptDisabler.h>
|
||||
#include <Kernel/Arch/x86/IO.h>
|
||||
#if ARCH(I386) || ARCH(X86_64)
|
||||
# include <Kernel/Arch/x86/IO.h>
|
||||
#endif
|
||||
#include <Kernel/Bus/PCI/API.h>
|
||||
#include <Kernel/Debug.h>
|
||||
#include <Kernel/Firmware/ACPI/Parser.h>
|
||||
|
@ -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: {
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <Kernel/Graphics/VMWare/Definitions.h>
|
||||
#include <Kernel/Graphics/VMWare/DisplayConnector.h>
|
||||
#include <Kernel/Graphics/VMWare/GraphicsAdapter.h>
|
||||
#include <Kernel/IOWindow.h>
|
||||
#include <Kernel/Memory/TypedMapping.h>
|
||||
#include <Kernel/Sections.h>
|
||||
|
||||
|
@ -27,29 +28,30 @@ UNMAP_AFTER_INIT LockRefPtr<VMWareGraphicsAdapter> 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<IOWindow> 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<u32>(to_underlying(register_offset));
|
||||
return m_io_registers_base.offset(1).in<u32>();
|
||||
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<u32>(to_underlying(register_offset));
|
||||
m_io_registers_base.offset(1).out<u32>(value);
|
||||
m_registers_io_window->write32(0, to_underlying(register_offset));
|
||||
m_registers_io_window->write32_unaligned(1, value);
|
||||
}
|
||||
|
||||
UNMAP_AFTER_INIT ErrorOr<void> VMWareGraphicsAdapter::negotiate_device_version()
|
||||
|
|
|
@ -7,10 +7,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/Types.h>
|
||||
#include <Kernel/Arch/x86/IO.h>
|
||||
#include <Kernel/Bus/PCI/Device.h>
|
||||
#include <Kernel/Graphics/GenericGraphicsAdapter.h>
|
||||
#include <Kernel/Graphics/VMWare/Definitions.h>
|
||||
#include <Kernel/IOWindow.h>
|
||||
#include <Kernel/Locking/Spinlock.h>
|
||||
#include <Kernel/Memory/TypedMapping.h>
|
||||
#include <Kernel/PhysicalAddress.h>
|
||||
|
@ -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<IOWindow> registers_io_window);
|
||||
|
||||
Memory::TypedMapping<volatile VMWareDisplayFIFORegisters> m_fifo_registers;
|
||||
LockRefPtr<VMWareDisplayConnector> m_display_connector;
|
||||
const IOAddress m_io_registers_base;
|
||||
mutable NonnullOwnPtr<IOWindow> m_registers_io_window;
|
||||
mutable Spinlock m_io_access_lock { LockRank::None };
|
||||
mutable RecursiveSpinlock m_operation_lock { LockRank::None };
|
||||
};
|
||||
|
|
285
Kernel/IOWindow.cpp
Normal file
285
Kernel/IOWindow.cpp
Normal file
|
@ -0,0 +1,285 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <Kernel/Bus/PCI/API.h>
|
||||
#include <Kernel/Bus/PCI/Definitions.h>
|
||||
#include <Kernel/IOWindow.h>
|
||||
|
||||
namespace Kernel {
|
||||
|
||||
#if ARCH(I386) || ARCH(X86_64)
|
||||
ErrorOr<NonnullOwnPtr<IOWindow>> IOWindow::create_for_io_space(IOAddress address, u64 space_length)
|
||||
{
|
||||
VERIFY(!Checked<u64>::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<IOAddressData> io_range)
|
||||
: m_space_type(SpaceType::IO)
|
||||
, m_io_range(move(io_range))
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
ErrorOr<NonnullOwnPtr<IOWindow>> 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<u64>::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<u32>::addition_would_overflow(m_memory_mapped_range->paddr.get(), offset))
|
||||
return Error::from_errno(EOVERFLOW);
|
||||
if (Checked<u32>::addition_would_overflow(m_memory_mapped_range->paddr.get() + offset, space_length))
|
||||
return Error::from_errno(EOVERFLOW);
|
||||
#else
|
||||
if (Checked<u64>::addition_would_overflow(m_memory_mapped_range->paddr.get(), offset))
|
||||
return Error::from_errno(EOVERFLOW);
|
||||
if (Checked<u64>::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<volatile u8>(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<NonnullOwnPtr<IOWindow>> 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<NonnullOwnPtr<IOWindow>> 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<PCI::HeaderType0BaseRegister>(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<u64>::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<u32>::addition_would_overflow(pci_bar_value, space_length))
|
||||
return Error::from_errno(EOVERFLOW);
|
||||
if (pci_bar_space_type == PCI::BARSpaceType::Memory16BitSpace && Checked<u16>::addition_would_overflow(pci_bar_value, space_length))
|
||||
return Error::from_errno(EOVERFLOW);
|
||||
if (pci_bar_space_type == PCI::BARSpaceType::Memory64BitSpace && Checked<u64>::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<volatile u8>(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<NonnullOwnPtr<IOWindow>> 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<NonnullOwnPtr<IOWindow>> 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<NonnullOwnPtr<IOWindow>> 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::TypedMapping<volatile u8>> 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<u64>::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<u64>::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<u64>::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<u8>(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<u16>(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<u32>(offset, data);
|
||||
return data;
|
||||
}
|
||||
|
||||
void IOWindow::write8(u64 offset, u8 data)
|
||||
{
|
||||
VERIFY(is_access_in_range(offset, sizeof(u8)));
|
||||
out<u8>(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<u16>(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<u32>(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<u32>(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<u32>(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
|
||||
|
||||
}
|
163
Kernel/IOWindow.h
Normal file
163
Kernel/IOWindow.h
Normal file
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteReader.h>
|
||||
#include <AK/Platform.h>
|
||||
#include <AK/Types.h>
|
||||
#if ARCH(I386) || ARCH(X86_64)
|
||||
# include <Kernel/Arch/x86/IO.h>
|
||||
#endif
|
||||
#include <Kernel/Bus/PCI/Definitions.h>
|
||||
#include <Kernel/Memory/TypedMapping.h>
|
||||
#include <Kernel/PhysicalAddress.h>
|
||||
|
||||
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<typename V>
|
||||
void write();
|
||||
|
||||
#if ARCH(I386) || ARCH(X86_64)
|
||||
static ErrorOr<NonnullOwnPtr<IOWindow>> create_for_io_space(IOAddress, u64 space_length);
|
||||
#endif
|
||||
static ErrorOr<NonnullOwnPtr<IOWindow>> create_for_pci_device_bar(PCI::DeviceIdentifier const&, PCI::HeaderType0BaseRegister, u64 space_length);
|
||||
static ErrorOr<NonnullOwnPtr<IOWindow>> create_for_pci_device_bar(PCI::DeviceIdentifier const&, PCI::HeaderType0BaseRegister);
|
||||
|
||||
static ErrorOr<NonnullOwnPtr<IOWindow>> create_for_pci_device_bar(PCI::Address const&, PCI::HeaderType0BaseRegister, u64 space_length);
|
||||
static ErrorOr<NonnullOwnPtr<IOWindow>> create_for_pci_device_bar(PCI::Address const&, PCI::HeaderType0BaseRegister);
|
||||
|
||||
ErrorOr<NonnullOwnPtr<IOWindow>> create_from_io_window_with_offset(u64 offset, u64 space_length);
|
||||
ErrorOr<NonnullOwnPtr<IOWindow>> 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<Memory::TypedMapping<volatile u8>>);
|
||||
|
||||
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<IOAddressData>);
|
||||
#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<typename T>
|
||||
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<T>();
|
||||
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<typename T>
|
||||
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<T>(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<Memory::TypedMapping<volatile u8>> m_memory_mapped_range;
|
||||
|
||||
#if ARCH(I386) || ARCH(X86_64)
|
||||
OwnPtr<IOAddressData> m_io_range;
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template<>
|
||||
struct AK::Formatter<Kernel::IOWindow> : AK::Formatter<FormatString> {
|
||||
ErrorOr<void> format(FormatBuilder& builder, Kernel::IOWindow const& value)
|
||||
{
|
||||
#if ARCH(I386) || ARCH(X86_64)
|
||||
if (value.space_type() == Kernel::IOWindow::SpaceType::IO)
|
||||
return Formatter<FormatString>::format(builder, "{}"sv, value.as_io_address());
|
||||
#endif
|
||||
VERIFY(value.space_type() == Kernel::IOWindow::SpaceType::Memory);
|
||||
return Formatter<FormatString>::format(builder, "Memory {}"sv, value.as_physical_memory_address());
|
||||
}
|
||||
};
|
|
@ -23,7 +23,9 @@ struct TypedMapping {
|
|||
const T& operator*() const { return *ptr(); }
|
||||
T& operator*() { return *ptr(); }
|
||||
OwnPtr<Region> region;
|
||||
PhysicalAddress paddr;
|
||||
size_t offset { 0 };
|
||||
size_t length { 0 };
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
|
@ -34,6 +36,8 @@ static ErrorOr<NonnullOwnPtr<TypedMapping<T>>> adopt_new_nonnull_own_typed_mappi
|
|||
auto table = TRY(adopt_nonnull_own_or_enomem(new (nothrow) Memory::TypedMapping<T>()));
|
||||
table->region = move(region);
|
||||
table->offset = paddr.offset_in_page();
|
||||
table->paddr = paddr;
|
||||
table->length = length;
|
||||
return table;
|
||||
}
|
||||
|
||||
|
@ -44,6 +48,8 @@ static ErrorOr<TypedMapping<T>> 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -192,7 +192,8 @@ UNMAP_AFTER_INIT LockRefPtr<E1000ENetworkAdapter> 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> 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<KString> interface_name)
|
||||
: E1000NetworkAdapter(address, irq, move(interface_name))
|
||||
UNMAP_AFTER_INIT E1000ENetworkAdapter::E1000ENetworkAdapter(PCI::Address address, u8 irq, NonnullOwnPtr<IOWindow> registers_io_window, NonnullOwnPtr<KString> interface_name)
|
||||
: E1000NetworkAdapter(address, irq, move(registers_io_window), move(interface_name))
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
|
||||
#include <AK/NonnullOwnPtrVector.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <Kernel/Arch/x86/IO.h>
|
||||
#include <Kernel/Bus/PCI/Access.h>
|
||||
#include <Kernel/Bus/PCI/Device.h>
|
||||
#include <Kernel/IOWindow.h>
|
||||
#include <Kernel/Interrupts/IRQHandler.h>
|
||||
#include <Kernel/Net/Intel/E1000NetworkAdapter.h>
|
||||
#include <Kernel/Net/NetworkAdapter.h>
|
||||
|
@ -30,7 +30,7 @@ public:
|
|||
virtual StringView purpose() const override { return class_name(); }
|
||||
|
||||
private:
|
||||
E1000ENetworkAdapter(PCI::Address, u8 irq, NonnullOwnPtr<KString>);
|
||||
E1000ENetworkAdapter(PCI::Address, u8 irq, NonnullOwnPtr<IOWindow> registers_io_window, NonnullOwnPtr<KString>);
|
||||
|
||||
virtual StringView class_name() const override { return "E1000ENetworkAdapter"sv; }
|
||||
|
||||
|
|
|
@ -170,7 +170,8 @@ UNMAP_AFTER_INIT LockRefPtr<E1000NetworkAdapter> 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<KString> interface_name)
|
||||
UNMAP_AFTER_INIT E1000NetworkAdapter::E1000NetworkAdapter(PCI::Address address, u8 irq, NonnullOwnPtr<IOWindow> registers_io_window, NonnullOwnPtr<KString> 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<u8>();
|
||||
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<u16>();
|
||||
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<u32>();
|
||||
return m_registers_io_window->read32(address);
|
||||
}
|
||||
|
||||
void E1000NetworkAdapter::send_raw(ReadonlyBytes payload)
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <Kernel/Arch/x86/IO.h>
|
||||
#include <Kernel/Bus/PCI/Access.h>
|
||||
#include <Kernel/Bus/PCI/Device.h>
|
||||
#include <Kernel/IOWindow.h>
|
||||
#include <Kernel/Interrupts/IRQHandler.h>
|
||||
#include <Kernel/Net/NetworkAdapter.h>
|
||||
#include <Kernel/Random.h>
|
||||
|
@ -37,7 +37,7 @@ protected:
|
|||
void setup_interrupts();
|
||||
void setup_link();
|
||||
|
||||
E1000NetworkAdapter(PCI::Address, u8 irq, NonnullOwnPtr<KString>);
|
||||
E1000NetworkAdapter(PCI::Address, u8 irq, NonnullOwnPtr<IOWindow> registers_io_window, NonnullOwnPtr<KString>);
|
||||
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<IOWindow> m_registers_io_window;
|
||||
|
||||
OwnPtr<Memory::Region> m_rx_descriptors_region;
|
||||
OwnPtr<Memory::Region> m_tx_descriptors_region;
|
||||
OwnPtr<Memory::Region> m_rx_buffer_region;
|
||||
OwnPtr<Memory::Region> m_tx_buffer_region;
|
||||
Array<void*, number_of_rx_descriptors> m_rx_buffers;
|
||||
Array<void*, number_of_tx_descriptors> m_tx_buffers;
|
||||
OwnPtr<Memory::Region> m_mmio_region;
|
||||
bool m_has_eeprom { false };
|
||||
bool m_use_mmio { false };
|
||||
bool m_link_up { false };
|
||||
EntropySource m_entropy_source;
|
||||
|
||||
|
|
|
@ -5,9 +5,9 @@
|
|||
*/
|
||||
|
||||
#include <AK/MACAddress.h>
|
||||
#include <Kernel/Arch/x86/IO.h>
|
||||
#include <Kernel/Bus/PCI/API.h>
|
||||
#include <Kernel/Debug.h>
|
||||
#include <Kernel/IOWindow.h>
|
||||
#include <Kernel/Net/NE2000/NetworkAdapter.h>
|
||||
#include <Kernel/Net/NetworkingManagement.h>
|
||||
#include <Kernel/Sections.h>
|
||||
|
@ -162,18 +162,19 @@ UNMAP_AFTER_INIT LockRefPtr<NE2000NetworkAdapter> 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<KString> interface_name)
|
||||
UNMAP_AFTER_INIT NE2000NetworkAdapter::NE2000NetworkAdapter(PCI::Address address, u8 irq, NonnullOwnPtr<IOWindow> registers_io_window, NonnullOwnPtr<KString> 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<u8>();
|
||||
return data;
|
||||
return m_registers_io_window->read8(address);
|
||||
}
|
||||
|
||||
u16 NE2000NetworkAdapter::in16(u16 address)
|
||||
{
|
||||
return m_io_base.offset(address).in<u16>();
|
||||
return m_registers_io_window->read16(address);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <Kernel/Arch/x86/IO.h>
|
||||
#include <Kernel/Bus/PCI/Access.h>
|
||||
#include <Kernel/Bus/PCI/Device.h>
|
||||
#include <Kernel/IOWindow.h>
|
||||
#include <Kernel/Interrupts/IRQHandler.h>
|
||||
#include <Kernel/Net/NetworkAdapter.h>
|
||||
#include <Kernel/Random.h>
|
||||
|
@ -41,7 +41,7 @@ public:
|
|||
virtual StringView purpose() const override { return class_name(); }
|
||||
|
||||
private:
|
||||
NE2000NetworkAdapter(PCI::Address, u8, NonnullOwnPtr<KString>);
|
||||
NE2000NetworkAdapter(PCI::Address, u8, NonnullOwnPtr<IOWindow> registers_io_window, NonnullOwnPtr<KString>);
|
||||
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<IOWindow> m_registers_io_window;
|
||||
int m_ring_read_ptr;
|
||||
u8 m_interrupt_line { 0 };
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
|
||||
#include <AK/Singleton.h>
|
||||
#include <Kernel/Arch/x86/IO.h>
|
||||
#include <Kernel/Bus/PCI/API.h>
|
||||
#include <Kernel/CommandLine.h>
|
||||
#include <Kernel/KString.h>
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
|
||||
#include <AK/MACAddress.h>
|
||||
#include <Kernel/Arch/x86/IO.h>
|
||||
#include <Kernel/Bus/PCI/API.h>
|
||||
#include <Kernel/Debug.h>
|
||||
#include <Kernel/Net/NetworkingManagement.h>
|
||||
|
@ -123,14 +122,15 @@ UNMAP_AFTER_INIT LockRefPtr<RTL8139NetworkAdapter> 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<KString> interface_name)
|
||||
UNMAP_AFTER_INIT RTL8139NetworkAdapter::RTL8139NetworkAdapter(PCI::Address address, u8 irq, NonnullOwnPtr<IOWindow> registers_io_window, NonnullOwnPtr<KString> 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<u8>();
|
||||
return m_registers_io_window->read8(address);
|
||||
}
|
||||
|
||||
u16 RTL8139NetworkAdapter::in16(u16 address)
|
||||
{
|
||||
return m_io_base.offset(address).in<u16>();
|
||||
return m_registers_io_window->read16(address);
|
||||
}
|
||||
|
||||
u32 RTL8139NetworkAdapter::in32(u16 address)
|
||||
{
|
||||
return m_io_base.offset(address).in<u32>();
|
||||
return m_registers_io_window->read32(address);
|
||||
}
|
||||
|
||||
bool RTL8139NetworkAdapter::link_full_duplex()
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <Kernel/Arch/x86/IO.h>
|
||||
#include <Kernel/Bus/PCI/Access.h>
|
||||
#include <Kernel/Bus/PCI/Device.h>
|
||||
#include <Kernel/IOWindow.h>
|
||||
#include <Kernel/Interrupts/IRQHandler.h>
|
||||
#include <Kernel/Net/NetworkAdapter.h>
|
||||
#include <Kernel/Random.h>
|
||||
|
@ -34,7 +34,7 @@ public:
|
|||
virtual StringView purpose() const override { return class_name(); }
|
||||
|
||||
private:
|
||||
RTL8139NetworkAdapter(PCI::Address, u8 irq, NonnullOwnPtr<KString>);
|
||||
RTL8139NetworkAdapter(PCI::Address, u8 irq, NonnullOwnPtr<IOWindow> registers_io_window, NonnullOwnPtr<KString>);
|
||||
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<IOWindow> m_registers_io_window;
|
||||
u8 m_interrupt_line { 0 };
|
||||
OwnPtr<Memory::Region> m_rx_buffer;
|
||||
u16 m_rx_buffer_offset { 0 };
|
||||
|
|
|
@ -193,7 +193,8 @@ UNMAP_AFTER_INIT LockRefPtr<RTL8168NetworkAdapter> 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<KString> interface_name)
|
||||
UNMAP_AFTER_INIT RTL8168NetworkAdapter::RTL8168NetworkAdapter(PCI::Address address, u8 irq, NonnullOwnPtr<IOWindow> registers_io_window, NonnullOwnPtr<KString> 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<u8>();
|
||||
return m_registers_io_window->read8(address);
|
||||
}
|
||||
|
||||
u16 RTL8168NetworkAdapter::in16(u16 address)
|
||||
{
|
||||
return m_io_base.offset(address).in<u16>();
|
||||
return m_registers_io_window->read16(address);
|
||||
}
|
||||
|
||||
u32 RTL8168NetworkAdapter::in32(u16 address)
|
||||
{
|
||||
return m_io_base.offset(address).in<u32>();
|
||||
return m_registers_io_window->read32(address);
|
||||
}
|
||||
|
||||
void RTL8168NetworkAdapter::phy_out(u8 address, u16 data)
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
|
||||
#include <AK/NonnullOwnPtrVector.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <Kernel/Arch/x86/IO.h>
|
||||
#include <Kernel/Bus/PCI/Access.h>
|
||||
#include <Kernel/Bus/PCI/Device.h>
|
||||
#include <Kernel/IOWindow.h>
|
||||
#include <Kernel/Interrupts/IRQHandler.h>
|
||||
#include <Kernel/Net/NetworkAdapter.h>
|
||||
#include <Kernel/Random.h>
|
||||
|
@ -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<KString>);
|
||||
RTL8168NetworkAdapter(PCI::Address, u8 irq, NonnullOwnPtr<IOWindow> registers_io_window, NonnullOwnPtr<KString>);
|
||||
|
||||
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<IOWindow> m_registers_io_window;
|
||||
u32 m_ocp_base_address { 0 };
|
||||
OwnPtr<Memory::Region> m_rx_descriptors_region;
|
||||
NonnullOwnPtrVector<Memory::Region> m_rx_buffers_regions;
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
#include <AK/Singleton.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <Kernel/Arch/Delay.h>
|
||||
#include <Kernel/Arch/x86/IO.h>
|
||||
#include <Kernel/Bus/PCI/API.h>
|
||||
#include <Kernel/IOWindow.h>
|
||||
#include <Kernel/Memory/MemoryManager.h>
|
||||
#include <Kernel/Process.h>
|
||||
#include <Kernel/Sections.h>
|
||||
|
@ -24,16 +24,16 @@ namespace Kernel {
|
|||
#define PATA_PRIMARY_IRQ 14
|
||||
#define PATA_SECONDARY_IRQ 15
|
||||
|
||||
UNMAP_AFTER_INIT NonnullLockRefPtr<IDEChannel> IDEChannel::create(IDEController const& controller, IOAddressGroup io_group, ChannelType type)
|
||||
UNMAP_AFTER_INIT NonnullLockRefPtr<IDEChannel> 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> IDEChannel::create(IDEController const& controller, u8 irq, IOAddressGroup io_group, ChannelType type)
|
||||
UNMAP_AFTER_INIT NonnullLockRefPtr<IDEChannel> 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<u8>(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<u8>() & 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<void> 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>();
|
||||
u8 device_control = m_io_window_group.control_window().read8(0);
|
||||
// Wait 30 milliseconds
|
||||
microseconds_delay(30000);
|
||||
m_io_group.control_base().out<u8>(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<u8>(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<void> IDEChannel::allocate_resources_for_isa_ide_controller(Badge<ISAIDE
|
|||
|
||||
UNMAP_AFTER_INIT ErrorOr<void> 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<void> 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<u8>(m_io_group.bus_master_base().value().offset(2).in<u8>() | 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<KBuffer> ata_identify_data_buffer)
|
||||
UNMAP_AFTER_INIT IDEChannel::IDEChannel(IDEController const& controller, u8 irq, IOWindowGroup io_group, ChannelType type, NonnullOwnPtr<KBuffer> 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<KBuffer> ata_identify_data_buffer)
|
||||
UNMAP_AFTER_INIT IDEChannel::IDEChannel(IDEController const& controller, IOWindowGroup io_group, ChannelType type, NonnullOwnPtr<KBuffer> 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<void> 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<u8>(0);
|
||||
VERIFY(m_io_window_group.bus_master_window());
|
||||
m_io_window_group.bus_master_window()->write8(0, 0);
|
||||
return {};
|
||||
}
|
||||
ErrorOr<void> 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<u8>(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<void> 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<u8>(m_io_group.bus_master_base().value().offset(2).in<u8>() | 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<u8> IDEChannel::busmastering_status()
|
||||
{
|
||||
VERIFY(m_io_group.bus_master_base().has_value());
|
||||
return m_io_group.bus_master_base().value().offset(2).in<u8>();
|
||||
VERIFY(m_io_window_group.bus_master_window());
|
||||
return m_io_window_group.bus_master_window()->read8(2);
|
||||
}
|
||||
ErrorOr<void> 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<u32>(prdt_buffer.get());
|
||||
m_io_group.bus_master_base().value().out<u8>(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<u8>(m_io_group.bus_master_base().value().offset(2).in<u8>() | 0x6);
|
||||
m_io_window_group.bus_master_window()->write8(2, m_io_window_group.bus_master_window()->read8(2) | 0x6);
|
||||
return {};
|
||||
}
|
||||
ErrorOr<void> IDEChannel::initiate_transaction(TransactionDirection)
|
||||
|
@ -190,29 +191,29 @@ ErrorOr<void> IDEChannel::initiate_transaction(TransactionDirection)
|
|||
ErrorOr<u8> IDEChannel::task_file_status()
|
||||
{
|
||||
VERIFY(m_lock.is_locked());
|
||||
return m_io_group.control_base().in<u8>();
|
||||
return m_io_window_group.control_window().read8(0);
|
||||
}
|
||||
|
||||
ErrorOr<u8> IDEChannel::task_file_error()
|
||||
{
|
||||
VERIFY(m_lock.is_locked());
|
||||
return m_io_group.io_base().offset(ATA_REG_ERROR).in<u8>();
|
||||
return m_io_window_group.io_window().read8(ATA_REG_ERROR);
|
||||
}
|
||||
|
||||
ErrorOr<bool> IDEChannel::detect_presence_on_selected_device()
|
||||
{
|
||||
VERIFY(m_lock.is_locked());
|
||||
m_io_group.io_base().offset(ATA_REG_SECCOUNT0).out<u8>(0x55);
|
||||
m_io_group.io_base().offset(ATA_REG_LBA0).out<u8>(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<u8>(0xAA);
|
||||
m_io_group.io_base().offset(ATA_REG_LBA0).out<u8>(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<u8>(0x55);
|
||||
m_io_group.io_base().offset(ATA_REG_LBA0).out<u8>(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<u8>();
|
||||
auto lba0 = m_io_group.io_base().offset(ATA_REG_LBA0).in<u8>();
|
||||
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<bool> IDEChannel::detect_presence_on_selected_device()
|
|||
ErrorOr<void> IDEChannel::wait_if_busy_until_timeout(size_t timeout_in_milliseconds)
|
||||
{
|
||||
size_t time_elapsed = 0;
|
||||
while (m_io_group.control_base().in<u8>() & 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<void> IDEChannel::wait_if_busy_until_timeout(size_t timeout_in_milliseco
|
|||
ErrorOr<void> IDEChannel::force_clear_interrupts()
|
||||
{
|
||||
VERIFY(m_lock.is_locked());
|
||||
m_io_group.io_base().offset(ATA_REG_STATUS).in<u8>();
|
||||
m_io_window_group.io_window().read8(ATA_REG_STATUS);
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -250,21 +251,21 @@ ErrorOr<void> 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<u8>() & (1 << 4)) | (head | (1 << 5) | (1 << 6)));
|
||||
m_io_group.io_base().offset(ATA_REG_HDDEVSEL).out<u8>(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<u8>((task_file.count >> 8) & 0xFF);
|
||||
m_io_group.io_base().offset(ATA_REG_LBA3).out<u8>(task_file.lba_high[0]);
|
||||
m_io_group.io_base().offset(ATA_REG_LBA4).out<u8>(task_file.lba_high[1]);
|
||||
m_io_group.io_base().offset(ATA_REG_LBA5).out<u8>(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<u8>(task_file.count & 0xFF);
|
||||
m_io_group.io_base().offset(ATA_REG_LBA0).out<u8>(task_file.lba_low[0]);
|
||||
m_io_group.io_base().offset(ATA_REG_LBA1).out<u8>(task_file.lba_low[1]);
|
||||
m_io_group.io_base().offset(ATA_REG_LBA2).out<u8>(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<void> 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<u8>();
|
||||
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<u8>(task_file.command);
|
||||
m_io_window_group.io_window().write8(ATA_REG_COMMAND, task_file.command);
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -288,7 +289,7 @@ ErrorOr<void> 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<u8>(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<void> IDEChannel::device_select(size_t device_index)
|
|||
ErrorOr<void> IDEChannel::enable_interrupts()
|
||||
{
|
||||
VERIFY(m_lock.is_locked());
|
||||
m_io_group.control_base().out<u8>(0);
|
||||
m_io_window_group.control_window().write8(0, 0);
|
||||
m_interrupts_enabled = true;
|
||||
return {};
|
||||
}
|
||||
ErrorOr<void> IDEChannel::disable_interrupts()
|
||||
{
|
||||
VERIFY(m_lock.is_locked());
|
||||
m_io_group.control_base().out<u8>(1 << 1);
|
||||
m_io_window_group.control_window().write8(0, 1 << 1);
|
||||
m_interrupts_enabled = false;
|
||||
return {};
|
||||
}
|
||||
|
@ -313,7 +314,7 @@ ErrorOr<void> 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>();
|
||||
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<void> 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 {};
|
||||
}
|
||||
|
|
|
@ -18,8 +18,8 @@
|
|||
#pragma once
|
||||
|
||||
#include <AK/Error.h>
|
||||
#include <Kernel/Arch/x86/IO.h>
|
||||
#include <Kernel/Devices/Device.h>
|
||||
#include <Kernel/IOWindow.h>
|
||||
#include <Kernel/Interrupts/IRQHandler.h>
|
||||
#include <Kernel/Library/LockRefPtr.h>
|
||||
#include <Kernel/Locking/Mutex.h>
|
||||
|
@ -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<IOWindow> io_window, NonnullOwnPtr<IOWindow> control_window, NonnullOwnPtr<IOWindow> 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<IOAddress> bus_master_base)
|
||||
: m_io_base(io_base)
|
||||
, m_control_base(control_base)
|
||||
, m_bus_master_base(bus_master_base)
|
||||
IOWindowGroup(NonnullOwnPtr<IOWindow> io_window, NonnullOwnPtr<IOWindow> 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<IOAddress> 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<IOAddress> m_bus_master_base;
|
||||
mutable NonnullOwnPtr<IOWindow> m_io_window;
|
||||
mutable NonnullOwnPtr<IOWindow> m_control_window;
|
||||
mutable OwnPtr<IOWindow> m_bus_master_window;
|
||||
};
|
||||
|
||||
public:
|
||||
static NonnullLockRefPtr<IDEChannel> create(IDEController const&, IOAddressGroup, ChannelType type);
|
||||
static NonnullLockRefPtr<IDEChannel> create(IDEController const&, u8 irq, IOAddressGroup, ChannelType type);
|
||||
static NonnullLockRefPtr<IDEChannel> create(IDEController const&, IOWindowGroup, ChannelType type);
|
||||
static NonnullLockRefPtr<IDEChannel> create(IDEController const&, u8 irq, IOWindowGroup, ChannelType type);
|
||||
|
||||
virtual ~IDEChannel() override;
|
||||
|
||||
|
@ -157,8 +140,8 @@ private:
|
|||
virtual ErrorOr<void> read_pio_data_to_buffer(UserOrKernelBuffer&, size_t block_offset, size_t words_count) override;
|
||||
virtual ErrorOr<void> write_pio_data_from_buffer(UserOrKernelBuffer const&, size_t block_offset, size_t words_count) override;
|
||||
|
||||
IDEChannel(IDEController const&, IOAddressGroup, ChannelType type, NonnullOwnPtr<KBuffer> ata_identify_data_buffer);
|
||||
IDEChannel(IDEController const&, u8 irq, IOAddressGroup, ChannelType type, NonnullOwnPtr<KBuffer> ata_identify_data_buffer);
|
||||
IDEChannel(IDEController const&, IOWindowGroup, ChannelType type, NonnullOwnPtr<KBuffer> ata_identify_data_buffer);
|
||||
IDEChannel(IDEController const&, u8 irq, IOWindowGroup, ChannelType type, NonnullOwnPtr<KBuffer> 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;
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue