diff --git a/Kernel/.bochsrc b/Kernel/.bochsrc index 52b78effd2..778814ecde 100644 --- a/Kernel/.bochsrc +++ b/Kernel/.bochsrc @@ -52,3 +52,4 @@ com1: enabled=true, mode=null com2: enabled=false com3: enabled=false com4: enabled=false +plugin_ctrl: e1000=1 diff --git a/Kernel/E1000NetworkAdapter.cpp b/Kernel/E1000NetworkAdapter.cpp new file mode 100644 index 0000000000..2fa645a05d --- /dev/null +++ b/Kernel/E1000NetworkAdapter.cpp @@ -0,0 +1,172 @@ +#include +#include +#include + +#define REG_CTRL 0x0000 +#define REG_STATUS 0x0008 +#define REG_EEPROM 0x0014 +#define REG_CTRL_EXT 0x0018 +#define REG_IMASK 0x00D0 +#define REG_RCTRL 0x0100 +#define REG_RXDESCLO 0x2800 +#define REG_RXDESCHI 0x2804 +#define REG_RXDESCLEN 0x2808 +#define REG_RXDESCHEAD 0x2810 +#define REG_RXDESCTAIL 0x2818 +#define REG_TCTRL 0x0400 +#define REG_TXDESCLO 0x3800 +#define REG_TXDESCHI 0x3804 +#define REG_TXDESCLEN 0x3808 +#define REG_TXDESCHEAD 0x3810 +#define REG_TXDESCTAIL 0x3818 +#define REG_RDTR 0x2820 // RX Delay Timer Register +#define REG_RXDCTL 0x3828 // RX Descriptor Control +#define REG_RADV 0x282C // RX Int. Absolute Delay Timer +#define REG_RSRPD 0x2C00 // RX Small Packet Detect Interrupt +#define REG_TIPG 0x0410 // Transmit Inter Packet Gap + +OwnPtr E1000NetworkAdapter::autodetect() +{ + static const PCI::ID qemu_bochs_vbox_id = { 0x8086, 0x100e }; + PCI::Address found_address; + PCI::enumerate_all([&] (const PCI::Address& address, PCI::ID id) { + if (id == qemu_bochs_vbox_id) { + found_address = address; + return; + } + }); + if (found_address.is_null()) + return nullptr; + byte irq = PCI::get_interrupt_line(found_address); + return make(found_address, irq); +} + +E1000NetworkAdapter::E1000NetworkAdapter(PCI::Address pci_address, byte irq) + : IRQHandler(irq) + , m_pci_address(pci_address) +{ + kprintf("E1000: Found at PCI address %b:%b:%b\n", pci_address.bus(), pci_address.slot(), pci_address.function()); + m_mmio_base = PhysicalAddress(PCI::get_BAR0(m_pci_address)); + MM.map_for_kernel(LinearAddress(m_mmio_base.get()), m_mmio_base); + m_use_mmio = true; + m_io_base = PCI::get_BAR1(m_pci_address) & ~1; + m_interrupt_line = PCI::get_interrupt_line(m_pci_address); + kprintf("E1000: IO port base: %w\n", m_io_base); + kprintf("E1000: MMIO base: P%x\n", m_mmio_base); + kprintf("E1000: Interrupt line: %u\n", m_interrupt_line); + detect_eeprom(); + kprintf("E1000: Has EEPROM? %u\n", m_has_eeprom); + read_mac_address(); + auto* mac = mac_address(); + kprintf("E1000: MAC address: %b:%b:%b:%b:%b:%b\n", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + enable_irq(); +} + +E1000NetworkAdapter::~E1000NetworkAdapter() +{ +} + +void E1000NetworkAdapter::handle_irq() +{ + kprintf("E1000: IRQ!\n"); +} + +void E1000NetworkAdapter::detect_eeprom() +{ + out32(REG_EEPROM, 0x1); + for (volatile int i = 0; i < 999; ++i) { + dword data = in32(REG_EEPROM); + if (data & 0x10) { + m_has_eeprom = true; + return; + } + } + m_has_eeprom = false; +} + +dword E1000NetworkAdapter::read_eeprom(byte address) +{ + word data = 0; + dword tmp = 0; + if (m_has_eeprom) { + out32(REG_EEPROM, ((dword)address << 8) | 1); + while (!((tmp = in32(REG_EEPROM)) & (1 << 4))) + ; + } else { + out32(REG_EEPROM, ((dword)address << 2) | 1); + while (!((tmp = in32(REG_EEPROM)) & (1 << 1))) + ; + } + data = (tmp >> 16) & 0xffff; + return data; +} + +void E1000NetworkAdapter::read_mac_address() +{ + if (m_has_eeprom) { + byte mac[6]; + dword tmp = read_eeprom(0); + mac[0] = tmp & 0xff; + mac[1] = tmp >> 8; + tmp = read_eeprom(1); + mac[2] = tmp & 0xff; + mac[3] = tmp >> 8; + tmp = read_eeprom(2); + mac[4] = tmp & 0xff; + mac[5] = tmp >> 8; + set_mac_address(mac); + } else { + ASSERT_NOT_REACHED(); + } +} + +void E1000NetworkAdapter::out8(word address, byte data) +{ + if (m_use_mmio) { + auto* ptr = (volatile byte*)(m_mmio_base.get() + address); + *ptr = data; + return; + } + IO::out8(m_io_base + address, data); +} + +void E1000NetworkAdapter::out16(word address, word data) +{ + if (m_use_mmio) { + auto* ptr = (volatile word*)(m_mmio_base.get() + address); + *ptr = data; + return; + } + IO::out16(m_io_base + address, data); +} + +void E1000NetworkAdapter::out32(word address, dword data) +{ + if (m_use_mmio) { + auto* ptr = (volatile dword*)(m_mmio_base.get() + address); + *ptr = data; + return; + } + IO::out32(m_io_base + address, data); +} + +byte E1000NetworkAdapter::in8(word address) +{ + if (m_use_mmio) + return *(volatile byte*)(m_mmio_base.get() + address); + return IO::in8(m_io_base + address); +} + +word E1000NetworkAdapter::in16(word address) +{ + if (m_use_mmio) + return *(volatile word*)(m_mmio_base.get() + address); + return IO::in16(m_io_base + address); +} + +dword E1000NetworkAdapter::in32(word address) +{ + if (m_use_mmio) + return *(volatile dword*)(m_mmio_base.get() + address); + return IO::in32(m_io_base + address); +} diff --git a/Kernel/E1000NetworkAdapter.h b/Kernel/E1000NetworkAdapter.h new file mode 100644 index 0000000000..29e871a863 --- /dev/null +++ b/Kernel/E1000NetworkAdapter.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include +#include +#include + +class E1000NetworkAdapter final : public NetworkAdapter, public IRQHandler { +public: + static OwnPtr autodetect(); + + E1000NetworkAdapter(PCI::Address, byte irq); + virtual ~E1000NetworkAdapter() override; + +private: + virtual void handle_irq() override; + virtual const char* class_name() const override { return "E1000NetworkAdapter"; } + + void detect_eeprom(); + dword read_eeprom(byte address); + void read_mac_address(); + + void write_command(word address, dword); + dword read_command(word address); + + void out8(word address, byte); + void out16(word address, word); + void out32(word address, dword); + byte in8(word address); + word in16(word address); + dword in32(word address); + + PCI::Address m_pci_address; + word m_io_base { 0 }; + PhysicalAddress m_mmio_base; + byte m_interrupt_line { 0 }; + bool m_has_eeprom { false }; + bool m_use_mmio { false }; +}; diff --git a/Kernel/Makefile b/Kernel/Makefile index b97bb78920..3e69a806ca 100644 --- a/Kernel/Makefile +++ b/Kernel/Makefile @@ -33,7 +33,9 @@ KERNEL_OBJS = \ PCI.o \ PS2MouseDevice.o \ Socket.o \ - LocalSocket.o + LocalSocket.o \ + NetworkAdapter.o \ + E1000NetworkAdapter.o VFS_OBJS = \ DiskDevice.o \ diff --git a/Kernel/MemoryManager.cpp b/Kernel/MemoryManager.cpp index 0141cc4171..3df6a60845 100644 --- a/Kernel/MemoryManager.cpp +++ b/Kernel/MemoryManager.cpp @@ -150,7 +150,7 @@ auto MemoryManager::ensure_pte(PageDirectory& page_directory, LinearAddress ladd pde.set_present(true); pde.set_writable(true); } else { - ASSERT(&page_directory != m_kernel_page_directory.ptr()); + //ASSERT(&page_directory != m_kernel_page_directory.ptr()); auto page_table = allocate_page_table(page_directory, page_directory_index); #ifdef MM_DEBUG dbgprintf("MM: PD K%x (%s) at P%x allocated page table #%u (for L%x) at P%x\n", @@ -456,6 +456,16 @@ void MemoryManager::flush_tlb(LinearAddress laddr) asm volatile("invlpg %0": :"m" (*(char*)laddr.get()) : "memory"); } +void MemoryManager::map_for_kernel(LinearAddress laddr, PhysicalAddress paddr) +{ + auto pte = ensure_pte(kernel_page_directory(), laddr); + pte.set_physical_page_base(paddr.get()); + pte.set_present(true); + pte.set_writable(true); + pte.set_user_allowed(false); + flush_tlb(laddr); +} + byte* MemoryManager::quickmap_page(PhysicalPage& physical_page) { ASSERT_INTERRUPTS_DISABLED(); diff --git a/Kernel/MemoryManager.h b/Kernel/MemoryManager.h index da1e350173..6c14e6a4db 100644 --- a/Kernel/MemoryManager.h +++ b/Kernel/MemoryManager.h @@ -258,6 +258,8 @@ public: int user_physical_pages_in_existence() const { return s_user_physical_pages_in_existence; } int super_physical_pages_in_existence() const { return s_super_physical_pages_in_existence; } + void map_for_kernel(LinearAddress, PhysicalAddress); + private: MemoryManager(); ~MemoryManager(); diff --git a/Kernel/NetworkAdapter.cpp b/Kernel/NetworkAdapter.cpp new file mode 100644 index 0000000000..242d31f3cf --- /dev/null +++ b/Kernel/NetworkAdapter.cpp @@ -0,0 +1,16 @@ +#include +#include + +NetworkAdapter::NetworkAdapter() +{ + memset(&m_mac_address, 0, sizeof(m_mac_address)); +} + +NetworkAdapter::~NetworkAdapter() +{ +} + +void NetworkAdapter::set_mac_address(const byte* mac_address) +{ + memcpy(m_mac_address, mac_address, 6); +} diff --git a/Kernel/NetworkAdapter.h b/Kernel/NetworkAdapter.h new file mode 100644 index 0000000000..61abda81de --- /dev/null +++ b/Kernel/NetworkAdapter.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +class NetworkAdapter { +public: + virtual ~NetworkAdapter(); + + virtual const char* class_name() const = 0; + const byte* mac_address() { return m_mac_address; } + +protected: + NetworkAdapter(); + void set_mac_address(const byte*); + +private: + byte m_mac_address[6]; +}; diff --git a/Kernel/PCI.cpp b/Kernel/PCI.cpp index df82b9d6f5..685db03658 100644 --- a/Kernel/PCI.cpp +++ b/Kernel/PCI.cpp @@ -95,6 +95,7 @@ void enumerate_bus(int type, byte bus, Function& callback) enumerate_slot(type, bus, slot, callback); } +byte get_interrupt_line(Address address) { return read_field(address, PCI_INTERRUPT_LINE); } dword get_BAR0(Address address) { return read_field(address, PCI_BAR0); } dword get_BAR1(Address address) { return read_field(address, PCI_BAR1); } dword get_BAR2(Address address) { return read_field(address, PCI_BAR2); } diff --git a/Kernel/PCI.h b/Kernel/PCI.h index 7ffb23172f..bb2950a049 100644 --- a/Kernel/PCI.h +++ b/Kernel/PCI.h @@ -9,6 +9,8 @@ struct ID { word vendor_id { 0 }; word device_id { 0 }; + bool is_null() const { return !vendor_id && !device_id; } + bool operator==(const ID& other) const { return vendor_id == other.vendor_id && device_id == other.device_id; @@ -38,6 +40,7 @@ private: }; void enumerate_all(Function); +byte get_interrupt_line(Address); dword get_BAR0(Address); dword get_BAR1(Address); dword get_BAR2(Address); diff --git a/Kernel/init.cpp b/Kernel/init.cpp index b6827b0e10..b2f5b5f849 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -23,6 +23,7 @@ #include "PTYMultiplexer.h" #include "DevPtsFS.h" #include "BXVGADevice.h" +#include "E1000NetworkAdapter.h" #define SPAWN_LAUNCHER //#define SPAWN_GUITEST2 @@ -160,6 +161,8 @@ VFS* vfs; new BXVGADevice; + auto e1000 = E1000NetworkAdapter::autodetect(); + Retained new_procfs = ProcFS::create(); new_procfs->initialize(); diff --git a/Kernel/run b/Kernel/run index d679046148..ac7127d7d6 100755 --- a/Kernel/run +++ b/Kernel/run @@ -1,7 +1,7 @@ #!/bin/sh if [ "$1" = "q" ]; then - qemu-system-i386 -s -m 32 -drive format=raw,file=.floppy-image,if=floppy -drive format=raw,file=_fs_contents #$@ + qemu-system-i386 -s -m 32 -device e1000 -drive format=raw,file=.floppy-image,if=floppy -drive format=raw,file=_fs_contents #$@ else bochs -q -f .bochsrc fi