Compare commits

..

5 Commits

Author SHA1 Message Date
Bananymous b8622f2b4b Kernel: Implement simple RTL8169 driver
This allows me to use internet when running banan-os on my hardware!
2024-09-19 22:58:03 +03:00
Bananymous 7f0c39fe91 Kernel: Don't crash the kernel if packet is smaller than ethernet hdr 2024-09-19 22:51:55 +03:00
Bananymous a489be0e05 Kernel: Allow parallel LAPIC timer initialization with HPET
HPET supports reading LAPIC counter without locks, so it can be done in
parallel. This makes booting much faster. Previously initializing every
timer took 100 ms, so 16 CPUs took total of 1.6 seconds. This allows
doing it all in 100 ms.
2024-09-19 14:41:59 +03:00
Bananymous 8e08046519 Kernel: Add asserts about having locked TTY's write lock
This for some reason fixes booting on real hardware? :D
2024-09-18 00:56:48 +03:00
Bananymous 999eb53364 Kernel: Release debug lock if its locked during kernel panic
Also kernel panic now prints if it had lock
2024-09-18 00:56:02 +03:00
14 changed files with 575 additions and 13 deletions

View File

@ -65,6 +65,7 @@ set(KERNEL_SOURCES
kernel/Networking/NetworkLayer.cpp
kernel/Networking/NetworkManager.cpp
kernel/Networking/NetworkSocket.cpp
kernel/Networking/RTL8169/RTL8169.cpp
kernel/Networking/TCPSocket.cpp
kernel/Networking/UDPSocket.cpp
kernel/Networking/UNIX/Socket.cpp

View File

@ -0,0 +1,151 @@
#pragma once
#include <stdint.h>
namespace Kernel
{
enum RTL8169_IO_REGS : uint16_t
{
RTL8169_IO_IDR0 = 0x00,
RTL8169_IO_IDR1 = 0x01,
RTL8169_IO_IDR2 = 0x02,
RTL8169_IO_IDR3 = 0x03,
RTL8169_IO_IDR4 = 0x04,
RTL8169_IO_IDR5 = 0x05,
RTL8169_IO_TNPDS = 0x20,
RTL8169_IO_CR = 0x37,
RTL8169_IO_TPPoll = 0x38,
RTL8169_IO_IMR = 0x3C,
RTL8169_IO_ISR = 0x3E,
RTL8169_IO_TCR = 0x40,
RTL8169_IO_RCR = 0x44,
RTL8169_IO_9346CR = 0x50,
RTL8169_IO_PHYSts = 0x6C,
RTL8169_IO_RMS = 0xDA,
RTL8169_IO_RDSAR = 0xE4,
RTL8169_IO_MTPS = 0xEC,
};
enum RTL8169_CR : uint8_t
{
RTL8169_CR_TE = 1u << 2,
RTL8169_CR_RE = 1u << 3,
RTL8169_CR_RST = 1u << 4,
};
enum RTL8169_TPPoll : uint8_t
{
RTL8169_TPPoll_FSWInt = 1u << 0,
RTL8169_TPPoll_NPQ = 1u << 6,
RTL8169_TPPoll_HPQ = 1u << 7,
};
enum RTL8169_IR : uint16_t
{
RTL8169_IR_ROK = 1u << 0,
RTL8169_IR_RER = 1u << 1,
RTL8169_IR_TOK = 1u << 2,
RTL8169_IR_TER = 1u << 3,
RTL8169_IR_RDU = 1u << 4,
RTL8169_IR_LinkChg = 1u << 5,
RTL8169_IR_FVOW = 1u << 6,
RTL8169_IR_TDU = 1u << 7,
RTL8169_IR_SWInt = 1u << 8,
RTL8169_IR_FEmp = 1u << 9,
RTL8169_IR_TimeOut = 1u << 14,
};
enum RTL8169_TCR : uint32_t
{
RTL8169_TCR_MXDMA_16 = 0b000u << 8,
RTL8169_TCR_MXDMA_32 = 0b001u << 8,
RTL8169_TCR_MXDMA_64 = 0b010u << 8,
RTL8169_TCR_MXDMA_128 = 0b011u << 8,
RTL8169_TCR_MXDMA_256 = 0b100u << 8,
RTL8169_TCR_MXDMA_512 = 0b101u << 8,
RTL8169_TCR_MXDMA_1024 = 0b110u << 8,
RTL8169_TCR_MXDMA_UNLIMITED = 0b111u << 8,
RTL8169_TCR_TX_NOCRC = 1u << 16,
RTL8169_TCR_LOOPBACK = 0b01u << 17,
RTL8169_TCR_IFG_0 = (0u << 19) | (0b11 << 24),
RTL8169_TCR_IFG_8 = (1u << 19) | (0b01 << 24),
RTL8169_TCR_IFG_16 = (1u << 19) | (0b11 << 24),
RTL8169_TCR_IFG_24 = (0u << 19) | (0b01 << 24),
RTL8169_TCR_IFG_48 = (0u << 19) | (0b10 << 24),
};
enum RTL8169_RCR : uint32_t
{
RTL8169_RCR_AAP = 1u << 0, // accept all packets with destination
RTL8169_RCR_APM = 1u << 1, // accept physical matches
RTL8169_RCR_AM = 1u << 2, // accept multicast
RTL8169_RCR_AB = 1u << 3, // accept broadcast
RTL8169_RCR_AR = 1u << 4, // accept runt
RTL8169_RCR_AER = 1u << 5, // accept error
RTL8169_RCR_MXDMA_64 = 0b010u << 8,
RTL8169_RCR_MXDMA_128 = 0b011u << 8,
RTL8169_RCR_MXDMA_256 = 0b100u << 8,
RTL8169_RCR_MXDMA_512 = 0b101u << 8,
RTL8169_RCR_MXDMA_1024 = 0b110u << 8,
RTL8169_RCR_MXDMA_UNLIMITED = 0b111u << 8,
RTL8169_RCR_RXFTH_64 = 0b010u << 13,
RTL8169_RCR_RXFTH_128 = 0b011u << 13,
RTL8169_RCR_RXFTH_256 = 0b100u << 13,
RTL8169_RCR_RXFTH_512 = 0b101u << 13,
RTL8169_RCR_RXFTH_1024 = 0b110u << 13,
RTL8169_RCR_RXFTH_NO = 0b111u << 13,
};
enum RTL8169_9346CR : uint8_t
{
RTL8169_9346CR_MODE_NORMAL = 0b00 << 6,
RTL8169_9346CR_MODE_CONFIG = 0b11 << 6,
};
enum RTL8169_PHYSts : uint8_t
{
RTL8169_PHYSts_FullDup = 1u << 0,
RTL8169_PHYSts_LinkSts = 1u << 1,
RTL8169_PHYSts_10M = 1u << 2,
RTL8169_PHYSts_100M = 1u << 3,
RTL8169_PHYSts_1000MF = 1u << 4,
RTL8169_PHYSts_RxFlow = 1u << 5,
RTL8169_PHYSts_TxFlow = 1u << 6,
};
enum RTL8169_RMS : uint16_t
{
// 8192 - 1
RTL8169_RMS_MAX = 0x1FFF,
};
enum RTL8169_MTPS : uint8_t
{
RTL8169_MTPS_MAX = 0x3B,
};
struct RTL8169Descriptor
{
uint32_t command;
uint32_t vlan;
uint32_t buffer_low;
uint32_t buffer_high;
};
enum RTL8169DescriptorCommand : uint32_t
{
RTL8169_DESC_CMD_LGSEN = 1u << 27,
RTL8169_DESC_CMD_LS = 1u << 28,
RTL8169_DESC_CMD_FS = 1u << 29,
RTL8169_DESC_CMD_EOR = 1u << 30,
RTL8169_DESC_CMD_OWN = 1u << 31,
};
}

View File

@ -0,0 +1,75 @@
#pragma once
#include <BAN/UniqPtr.h>
#include <kernel/Interruptable.h>
#include <kernel/Memory/DMARegion.h>
#include <kernel/Networking/NetworkInterface.h>
#include <kernel/PCI.h>
namespace Kernel
{
class RTL8169 final : public NetworkInterface, public Interruptable
{
public:
static bool probe(PCI::Device&);
static BAN::ErrorOr<BAN::RefPtr<RTL8169>> create(PCI::Device&);
virtual BAN::MACAddress get_mac_address() const override { return m_mac_address; }
virtual bool link_up() override { return m_link_up; }
virtual int link_speed() override;
virtual size_t payload_mtu() const override { return 7436 - sizeof(EthernetHeader); }
virtual void handle_irq() override;
protected:
RTL8169(PCI::Device& pci_device)
: m_pci_device(pci_device)
{ }
BAN::ErrorOr<void> initialize();
virtual BAN::ErrorOr<void> send_bytes(BAN::MACAddress destination, EtherType protocol, BAN::ConstByteSpan) override;
virtual bool can_read_impl() const override { return false; }
virtual bool can_write_impl() const override { return false; }
virtual bool has_error_impl() const override { return false; }
private:
BAN::ErrorOr<void> reset();
BAN::ErrorOr<void> initialize_rx();
BAN::ErrorOr<void> initialize_tx();
void enable_link();
BAN::ErrorOr<void> enable_interrupt();
void handle_receive();
protected:
PCI::Device& m_pci_device;
BAN::UniqPtr<PCI::BarRegion> m_io_bar_region;
private:
static constexpr size_t m_rx_descriptor_count = 256;
static constexpr size_t m_tx_descriptor_count = 256;
BAN::UniqPtr<DMARegion> m_rx_buffer_region;
BAN::UniqPtr<DMARegion> m_tx_buffer_region;
BAN::UniqPtr<DMARegion> m_rx_descriptor_region;
BAN::UniqPtr<DMARegion> m_tx_descriptor_region;
SpinLock m_lock;
ThreadBlocker m_thread_blocker;
uint32_t m_rx_current { 0 };
size_t m_tx_current { 0 };
BAN::MACAddress m_mac_address {};
BAN::Atomic<bool> m_link_up { false };
friend class BAN::RefPtr<RTL8169>;
};
}

View File

@ -17,7 +17,11 @@ namespace Kernel
static void panic_impl(const char* location, const char* message, Args&&... args)
{
asm volatile("cli");
const bool had_debug_lock = Debug::s_debug_lock.current_processor_has_lock();
derrorln("Kernel panic at {}", location);
if (had_debug_lock)
derrorln(" while having debug lock...");
derrorln(message, BAN::forward<Args>(args)...);
if (!g_paniced)
{

View File

@ -92,6 +92,7 @@ namespace Kernel
};
Buffer m_output;
protected:
RecursiveSpinLock m_write_lock;
};

View File

@ -19,6 +19,7 @@ namespace Kernel
virtual uint64_t ns_since_boot() const override;
virtual timespec time_since_boot() const override;
virtual bool pre_scheduler_sleep_needs_lock() const override { return false; }
virtual void pre_scheduler_sleep_ns(uint64_t) override;
virtual void handle_irq() override;

View File

@ -16,6 +16,7 @@ namespace Kernel
virtual uint64_t ns_since_boot() const override;
virtual timespec time_since_boot() const override;
virtual bool pre_scheduler_sleep_needs_lock() const override { return true; }
virtual void pre_scheduler_sleep_ns(uint64_t) override;
virtual void handle_irq() override;

View File

@ -17,6 +17,7 @@ namespace Kernel
virtual uint64_t ns_since_boot() const = 0;
virtual timespec time_since_boot() const = 0;
virtual bool pre_scheduler_sleep_needs_lock() const = 0;
virtual void pre_scheduler_sleep_ns(uint64_t) = 0;
void dont_invoke_scheduler() { m_should_invoke_scheduler = false; }
@ -39,6 +40,7 @@ namespace Kernel
virtual uint64_t ns_since_boot() const override;
virtual timespec time_since_boot() const override;
virtual bool pre_scheduler_sleep_needs_lock() const override;
virtual void pre_scheduler_sleep_ns(uint64_t) override;
void sleep_ms(uint64_t ms) const { return sleep_ns(ms * 1'000'000); }

View File

@ -253,11 +253,11 @@ namespace Kernel
const auto send_ipi =
[&](uint8_t processor, uint32_t data, uint64_t ud)
{
while ((read_from_local_apic(LAPIC_ICR_LO_REG) & ICR_LO_delivery_status_send_pending) == ICR_LO_delivery_status_send_pending)
__builtin_ia32_pause();
write_to_local_apic(LAPIC_ICR_HI_REG, (read_from_local_apic(LAPIC_ICR_HI_REG) & 0x00FFFFFF) | (processor << 24));
write_to_local_apic(LAPIC_ICR_LO_REG, data);
udelay(ud);
while ((read_from_local_apic(LAPIC_ICR_LO_REG) & ICR_LO_delivery_status_send_pending) == ICR_LO_delivery_status_send_pending)
__builtin_ia32_pause();
};
dprintln("System has {} processors", m_processors.size());
@ -402,21 +402,26 @@ namespace Kernel
void APIC::initialize_timer()
{
{
constexpr uint64_t measuring_duration_ms = 100;
ASSERT(Kernel::Processor::get_interrupt_state() == InterruptState::Disabled);
SpinLockGuard _(s_timer_init_lock);
constexpr uint64_t measuring_duration_ms = 100;
write_to_local_apic(LAPIC_TIMER_LVT, TimerModeOneShot | TimerMask);
write_to_local_apic(LAPIC_TIMER_DIVIDE_REG, DivideBy2);
write_to_local_apic(LAPIC_TIMER_INITIAL_REG, 0xFFFFFFFF);
SystemTimer::get().pre_scheduler_sleep_ns(measuring_duration_ms * 1'000'000);
const bool needs_lock = SystemTimer::get().pre_scheduler_sleep_needs_lock();
if (needs_lock)
s_timer_init_lock.lock();
const uint32_t counter = read_from_local_apic(LAPIC_TIMER_CURRENT_REG);
m_lapic_timer_frequency_hz = static_cast<uint64_t>(0xFFFFFFFF - counter) * 2 * (1000 / measuring_duration_ms);
write_to_local_apic(LAPIC_TIMER_LVT, TimerModeOneShot | TimerMask);
write_to_local_apic(LAPIC_TIMER_DIVIDE_REG, DivideBy2);
write_to_local_apic(LAPIC_TIMER_INITIAL_REG, 0xFFFFFFFF);
SystemTimer::get().pre_scheduler_sleep_ns(measuring_duration_ms * 1'000'000);
dprintln("CPU {}: lapic timer frequency: {} Hz", Kernel::Processor::current_id(), m_lapic_timer_frequency_hz);
}
const uint32_t counter = read_from_local_apic(LAPIC_TIMER_CURRENT_REG);
m_lapic_timer_frequency_hz = static_cast<uint64_t>(0xFFFFFFFF - counter) * 2 * (1000 / measuring_duration_ms);
if (needs_lock)
s_timer_init_lock.unlock(InterruptState::Disabled);
dprintln("CPU {}: lapic timer frequency: {} Hz", Kernel::Processor::current_id(), m_lapic_timer_frequency_hz);
write_to_local_apic(LAPIC_TIMER_LVT, TimerModePeriodic | (IRQ_VECTOR_BASE + IRQ_TIMER));
write_to_local_apic(LAPIC_TIMER_INITIAL_REG, m_lapic_timer_frequency_hz / 2 / 100);

View File

@ -360,6 +360,8 @@ done:
{
if (g_paniced)
{
while (Debug::s_debug_lock.current_processor_has_lock())
Debug::s_debug_lock.unlock(InterruptState::Disabled);
dprintln("Processor {} halted", Processor::current_id());
if (InterruptController::is_initialized())
InterruptController::get().broadcast_ipi();

View File

@ -5,6 +5,7 @@
#include <kernel/Networking/E1000/E1000E.h>
#include <kernel/Networking/ICMP.h>
#include <kernel/Networking/NetworkManager.h>
#include <kernel/Networking/RTL8169/RTL8169.h>
#include <kernel/Networking/TCPSocket.h>
#include <kernel/Networking/UDPSocket.h>
#include <kernel/Networking/UNIX/Socket.h>
@ -48,6 +49,11 @@ namespace Kernel
interface = TRY(E1000E::create(pci_device));
break;
}
if (RTL8169::probe(pci_device))
{
interface = TRY(RTL8169::create(pci_device));
break;
}
// fall through
default:
dprintln("unsupported network controller (pci {2H}.{2H}.{2H})", pci_device.class_code(), pci_device.subclass(), pci_device.prog_if());
@ -121,6 +127,8 @@ namespace Kernel
void NetworkManager::on_receive(NetworkInterface& interface, BAN::ConstByteSpan packet)
{
if (packet.size() < sizeof(EthernetHeader))
return;
auto ethernet_header = packet.as<const EthernetHeader>();
switch (ethernet_header.ether_type)

View File

@ -0,0 +1,295 @@
#include <kernel/Networking/NetworkManager.h>
#include <kernel/Networking/RTL8169/Definitions.h>
#include <kernel/Networking/RTL8169/RTL8169.h>
#include <kernel/Timer/Timer.h>
namespace Kernel
{
bool RTL8169::probe(PCI::Device& pci_device)
{
if (pci_device.vendor_id() != 0x10ec)
return false;
switch (pci_device.device_id())
{
case 0x8161:
case 0x8168:
case 0x8169:
return true;
default:
return false;
}
}
BAN::ErrorOr<BAN::RefPtr<RTL8169>> RTL8169::create(PCI::Device& pci_device)
{
auto rtl8169 = TRY(BAN::RefPtr<RTL8169>::create(pci_device));
TRY(rtl8169->initialize());
return rtl8169;
}
BAN::ErrorOr<void> RTL8169::initialize()
{
m_pci_device.enable_bus_mastering();
m_io_bar_region = TRY(m_pci_device.allocate_bar_region(0));
if (m_io_bar_region->type() != PCI::BarType::IO)
{
dwarnln("RTL8169 BAR0 is not IO space");
return BAN::Error::from_errno(EINVAL);
}
dprintln("Initializing RTL8169");
TRY(reset());
dprintln(" reset done");
for (size_t i = 0; i < 6; i++)
m_mac_address.address[i] = m_io_bar_region->read8(RTL8169_IO_IDR0 + i);
dprintln(" MAC {}", m_mac_address);
// unlock config registers
m_io_bar_region->write8(RTL8169_IO_9346CR, RTL8169_9346CR_MODE_CONFIG);
TRY(initialize_rx());
TRY(initialize_tx());
m_io_bar_region->write8(RTL8169_IO_CR, RTL8169_CR_RE | RTL8169_CR_TE);
dprintln(" descriptors initialized");
m_link_up = m_io_bar_region->read8(RTL8169_IO_PHYSts) & RTL8169_PHYSts_LinkSts;
if (m_link_up)
dprintln(" link speed {}", link_speed());
TRY(enable_interrupt());
dprintln(" interrupts enabled");
// lock config registers
m_io_bar_region->write8(RTL8169_IO_9346CR, RTL8169_9346CR_MODE_NORMAL);
return {};
}
BAN::ErrorOr<void> RTL8169::reset()
{
m_io_bar_region->write8(RTL8169_IO_CR, RTL8169_CR_RST);
const uint64_t timeout_ms = SystemTimer::get().ms_since_boot() + 100;
while (m_io_bar_region->read8(RTL8169_IO_CR) & RTL8169_CR_RST)
if (SystemTimer::get().ms_since_boot() >= timeout_ms)
return BAN::Error::from_errno(ETIMEDOUT);
return {};
}
BAN::ErrorOr<void> RTL8169::initialize_rx()
{
// each buffer is 7440 bytes + padding = 8192
constexpr size_t buffer_size = 2 * PAGE_SIZE;
m_rx_buffer_region = TRY(DMARegion::create(m_rx_descriptor_count * buffer_size));
m_rx_descriptor_region = TRY(DMARegion::create(m_rx_descriptor_count * sizeof(RTL8169Descriptor)));
for (size_t i = 0; i < m_rx_descriptor_count; i++)
{
const paddr_t rx_buffer_paddr = m_rx_buffer_region->paddr() + i * buffer_size;
uint32_t command = 0x1FF8 | RTL8169_DESC_CMD_OWN;
if (i == m_rx_descriptor_count - 1)
command |= RTL8169_DESC_CMD_EOR;
auto& rx_descriptor = reinterpret_cast<volatile RTL8169Descriptor*>(m_rx_descriptor_region->vaddr())[i];
rx_descriptor.command = command;
rx_descriptor.vlan = 0;
rx_descriptor.buffer_low = rx_buffer_paddr & 0xFFFFFFFF;
rx_descriptor.buffer_high = rx_buffer_paddr >> 32;
}
// configure rx descriptor addresses
m_io_bar_region->write32(RTL8169_IO_RDSAR + 0, m_rx_descriptor_region->paddr() & 0xFFFFFFFF);
m_io_bar_region->write32(RTL8169_IO_RDSAR + 4, m_rx_descriptor_region->paddr() >> 32);
// configure receibe control (no fifo threshold, max dma burst 1024, accept physical match, broadcast, multicast)
m_io_bar_region->write32(RTL8169_IO_RCR,
RTL8169_RCR_RXFTH_NO | RTL8169_RCR_MXDMA_1024 | RTL8169_RCR_AB | RTL8169_RCR_AM | RTL8169_RCR_APM
);
m_io_bar_region->write32(0x44, 0b111111 | (0b111 << 8) | (0b111 << 13));
// configure max rx packet size
m_io_bar_region->write16(RTL8169_IO_RMS, RTL8169_RMS_MAX);
return {};
}
BAN::ErrorOr<void> RTL8169::initialize_tx()
{
// each buffer is 7440 bytes + padding = 8192
constexpr size_t buffer_size = 2 * PAGE_SIZE;
m_tx_buffer_region = TRY(DMARegion::create(m_tx_descriptor_count * buffer_size));
m_tx_descriptor_region = TRY(DMARegion::create(m_tx_descriptor_count * sizeof(RTL8169Descriptor)));
for (size_t i = 0; i < m_tx_descriptor_count; i++)
{
const paddr_t tx_buffer_paddr = m_tx_buffer_region->paddr() + i * buffer_size;
uint32_t command = 0;
if (i == m_tx_descriptor_count - 1)
command |= RTL8169_DESC_CMD_EOR;
auto& tx_descriptor = reinterpret_cast<volatile RTL8169Descriptor*>(m_tx_descriptor_region->vaddr())[i];
tx_descriptor.command = command;
tx_descriptor.vlan = 0;
tx_descriptor.buffer_low = tx_buffer_paddr & 0xFFFFFFFF;
tx_descriptor.buffer_high = tx_buffer_paddr >> 32;
}
// configure tx descriptor addresses
m_io_bar_region->write32(RTL8169_IO_TNPDS + 0, m_tx_descriptor_region->paddr() & 0xFFFFFFFF);
m_io_bar_region->write32(RTL8169_IO_TNPDS + 4, m_tx_descriptor_region->paddr() >> 32);
// configure transmit control (standard ifg, max dma burst 1024)
m_io_bar_region->write32(RTL8169_IO_TCR, RTL8169_TCR_IFG_0 | RTL8169_TCR_MXDMA_1024);
// configure max tx packet size
m_io_bar_region->write8(RTL8169_IO_MTPS, RTL8169_MTPS_MAX);
return {};
}
BAN::ErrorOr<void> RTL8169::enable_interrupt()
{
TRY(m_pci_device.reserve_irqs(1));
set_irq(m_pci_device.get_irq(0));
Interruptable::enable_interrupt();
m_io_bar_region->write16(RTL8169_IO_IMR,
RTL8169_IR_ROK
| RTL8169_IR_RER
| RTL8169_IR_TOK
| RTL8169_IR_TER
| RTL8169_IR_RDU
| RTL8169_IR_LinkChg
| RTL8169_IR_FVOW
| RTL8169_IR_TDU
);
m_io_bar_region->write16(RTL8169_IO_ISR, 0xFFFF);
return {};
}
int RTL8169::link_speed()
{
if (!link_up())
return 0;
const uint8_t phy_status = m_io_bar_region->read8(RTL8169_IO_PHYSts);
if (phy_status & RTL8169_PHYSts_1000MF)
return 1000;
if (phy_status & RTL8169_PHYSts_100M)
return 100;
if (phy_status & RTL8169_PHYSts_10M)
return 10;
return 0;
}
BAN::ErrorOr<void> RTL8169::send_bytes(BAN::MACAddress destination, EtherType protocol, BAN::ConstByteSpan buffer)
{
constexpr size_t buffer_size = 8192;
const uint16_t packet_size = sizeof(EthernetHeader) + buffer.size();
if (packet_size > buffer_size)
return BAN::Error::from_errno(EINVAL);
if (!link_up())
return BAN::Error::from_errno(EADDRNOTAVAIL);
auto state = m_lock.lock();
const uint32_t tx_current = m_tx_current;
m_tx_current = (m_tx_current + 1) % m_tx_descriptor_count;
m_lock.unlock(state);
auto& descriptor = reinterpret_cast<volatile RTL8169Descriptor*>(m_tx_descriptor_region->vaddr())[tx_current];
while (descriptor.command & RTL8169_DESC_CMD_OWN)
m_thread_blocker.block_with_timeout_ms(100);
auto* tx_buffer = reinterpret_cast<uint8_t*>(m_tx_buffer_region->vaddr() + tx_current * buffer_size);
// write packet
auto& ethernet_header = *reinterpret_cast<EthernetHeader*>(tx_buffer);
ethernet_header.dst_mac = destination;
ethernet_header.src_mac = get_mac_address();
ethernet_header.ether_type = protocol;
memcpy(tx_buffer + sizeof(EthernetHeader), buffer.data(), buffer.size());
// give packet ownership to NIC
uint32_t command = packet_size | RTL8169_DESC_CMD_OWN | RTL8169_DESC_CMD_LS | RTL8169_DESC_CMD_FS;
if (tx_current >= m_tx_descriptor_count - 1)
command |= RTL8169_DESC_CMD_EOR;
descriptor.command = command;
// notify NIC about new packet
m_io_bar_region->write8(RTL8169_IO_TPPoll, RTL8169_TPPoll_NPQ);
return {};
}
void RTL8169::handle_irq()
{
const uint16_t interrupt_status = m_io_bar_region->read16(RTL8169_IO_ISR);
m_io_bar_region->write16(RTL8169_IO_ISR, interrupt_status);
if (interrupt_status & RTL8169_IR_LinkChg)
{
m_link_up = m_io_bar_region->read8(RTL8169_IO_PHYSts) & RTL8169_PHYSts_LinkSts;
dprintln("link status -> {}", m_link_up.load());
}
if (interrupt_status & RTL8169_IR_TOK)
m_thread_blocker.unblock();
if (interrupt_status & RTL8169_IR_RER)
dwarnln("Rx error");
if (interrupt_status & RTL8169_IR_TER)
dwarnln("Tx error");
if (interrupt_status & RTL8169_IR_RDU)
dwarnln("Rx descriptor not available");
if (interrupt_status & RTL8169_IR_FVOW)
dwarnln("Rx FIFO overflow");
// dont log TDU is sent after each sent packet
if (!(interrupt_status & RTL8169_IR_ROK))
return;
constexpr size_t buffer_size = 8192;
for (;;)
{
auto& descriptor = reinterpret_cast<volatile RTL8169Descriptor*>(m_rx_descriptor_region->vaddr())[m_rx_current];
if (descriptor.command & RTL8169_DESC_CMD_OWN)
break;
// packet buffer can only hold single packet, so we should not receive any multi-descriptor packets
ASSERT((descriptor.command & RTL8169_DESC_CMD_LS) && (descriptor.command & RTL8169_DESC_CMD_FS));
const uint16_t packet_length = descriptor.command & 0x3FFF;
if (packet_length > buffer_size)
dwarnln("Got {} bytes to {} byte buffer", packet_length, buffer_size);
else if (descriptor.command & (1u << 21))
; // descriptor has an error
else
{
NetworkManager::get().on_receive(*this, BAN::ConstByteSpan {
reinterpret_cast<const uint8_t*>(m_rx_buffer_region->vaddr() + m_rx_current * buffer_size),
packet_length
});
}
m_rx_current = (m_rx_current + 1) % m_rx_descriptor_count;
descriptor.command = descriptor.command | RTL8169_DESC_CMD_OWN;
}
}
}

View File

@ -49,6 +49,7 @@ namespace Kernel
void VirtualTTY::clear()
{
SpinLockGuard _(m_write_lock);
for (uint32_t i = 0; i < m_width * m_height; i++)
m_buffer[i] = { .foreground = m_foreground, .background = m_background, .codepoint = ' ' };
m_terminal_driver->clear(m_background);
@ -56,6 +57,8 @@ namespace Kernel
void VirtualTTY::set_font(const LibFont::Font& font)
{
SpinLockGuard _(m_write_lock);
m_terminal_driver->set_font(font);
uint32_t new_width = m_terminal_driver->width();
@ -86,6 +89,7 @@ namespace Kernel
void VirtualTTY::set_cursor_position(uint32_t x, uint32_t y)
{
ASSERT(m_write_lock.current_processor_has_lock());
static uint32_t last_x = -1;
static uint32_t last_y = -1;
if (last_x != uint32_t(-1) && last_y != uint32_t(-1))
@ -98,6 +102,7 @@ namespace Kernel
void VirtualTTY::reset_ansi()
{
ASSERT(m_write_lock.current_processor_has_lock());
m_ansi_state.index = 0;
m_ansi_state.nums[0] = -1;
m_ansi_state.nums[1] = -1;
@ -107,6 +112,7 @@ namespace Kernel
void VirtualTTY::handle_ansi_csi_color()
{
ASSERT(m_write_lock.current_processor_has_lock());
switch (m_ansi_state.nums[0])
{
case -1:
@ -155,6 +161,7 @@ namespace Kernel
void VirtualTTY::handle_ansi_csi(uint8_t ch)
{
ASSERT(m_write_lock.current_processor_has_lock());
switch (ch)
{
case '0': case '1': case '2': case '3': case '4':
@ -360,6 +367,7 @@ namespace Kernel
void VirtualTTY::render_from_buffer(uint32_t x, uint32_t y)
{
ASSERT(m_write_lock.current_processor_has_lock());
ASSERT(x < m_width && y < m_height);
const auto& cell = m_buffer[y * m_width + x];
m_terminal_driver->putchar_at(cell.codepoint, x, y, cell.foreground, cell.background);
@ -367,6 +375,7 @@ namespace Kernel
void VirtualTTY::putchar_at(uint32_t codepoint, uint32_t x, uint32_t y)
{
ASSERT(m_write_lock.current_processor_has_lock());
ASSERT(x < m_width && y < m_height);
auto& cell = m_buffer[y * m_width + x];
cell.codepoint = codepoint;
@ -377,6 +386,8 @@ namespace Kernel
void VirtualTTY::putchar_impl(uint8_t ch)
{
ASSERT(m_write_lock.current_processor_has_lock());
uint32_t codepoint = ch;
switch (m_state)

View File

@ -69,6 +69,11 @@ namespace Kernel
return m_timer->time_since_boot();
}
bool SystemTimer::pre_scheduler_sleep_needs_lock() const
{
return m_timer->pre_scheduler_sleep_needs_lock();
}
void SystemTimer::pre_scheduler_sleep_ns(uint64_t ns)
{
return m_timer->pre_scheduler_sleep_ns(ns);