From b5450cecbffdca1dd9dffdb58dd295a6a5d7f6eb Mon Sep 17 00:00:00 2001 From: Felix Morgner Date: Mon, 27 Jul 2026 18:09:27 +0200 Subject: x86_64: port PIT to devices resources --- arch/x86_64/arch/debug/qemu_output.hpp | 2 +- arch/x86_64/arch/device_io/port_io.hpp | 112 ---------------------- arch/x86_64/arch/devices/init.cpp | 7 +- arch/x86_64/arch/drivers/pit.cpp | 33 +++++-- arch/x86_64/arch/io/port_io.hpp | 165 +++++++++++++++++++++++++++++++++ 5 files changed, 195 insertions(+), 124 deletions(-) delete mode 100644 arch/x86_64/arch/device_io/port_io.hpp create mode 100644 arch/x86_64/arch/io/port_io.hpp (limited to 'arch') diff --git a/arch/x86_64/arch/debug/qemu_output.hpp b/arch/x86_64/arch/debug/qemu_output.hpp index 5ddd4be3..27898395 100644 --- a/arch/x86_64/arch/debug/qemu_output.hpp +++ b/arch/x86_64/arch/debug/qemu_output.hpp @@ -1,7 +1,7 @@ #ifndef TEACHOS_X86_64_DEBUG_QEMU_OUTPUT_HPP #define TEACHOS_X86_64_DEBUG_QEMU_OUTPUT_HPP -#include +#include #include diff --git a/arch/x86_64/arch/device_io/port_io.hpp b/arch/x86_64/arch/device_io/port_io.hpp deleted file mode 100644 index 4c8d66ae..00000000 --- a/arch/x86_64/arch/device_io/port_io.hpp +++ /dev/null @@ -1,112 +0,0 @@ -#ifndef TEACHOS_X86_64_IO_PORT_IO_HPP -#define TEACHOS_X86_64_IO_PORT_IO_HPP - -#include -#include -#include -#include -#include -#include - -namespace arch::io -{ - - //! The requirements imposed on a type usable for port I/O. - template - concept port_io_type = requires { - requires sizeof(ValueType) == 1 || sizeof(ValueType) == 2 || sizeof(ValueType) == 4; - requires std::default_initializable; - std::bit_cast( - std::conditional_t>{}); - }; - - template - struct port_read - { - //! Read from the I/O port. - //! - //! @return The data read from the I/O port. - auto static read() noexcept - { - auto data = typename Derived::value_type{}; - asm volatile((code[Derived::size / 2]) - : [data] "=m"(data) - : [port] "i"(Derived::address) - : "dx", (Derived::data_register)); - return data; - } - - private: - constexpr port_read() noexcept = default; - friend Derived; - - //! The assembly templates used for reading from an I/O port. - constexpr auto static code = std::array{ - std::string_view{"mov %[port], %%dx\nin %%dx, %%al\nmov %%al, %[data]"}, - std::string_view{"mov %[port], %%dx\nin %%dx, %%ax\nmov %%ax, %[data]"}, - std::string_view{"mov %[port], %%dx\nin %%dx, %%eax\nmov %%eax, %[data]"}, - }; - }; - - template - struct port_write - { - //! Write data to the I/O port. - //! - //! @param data The data to write to the I/O port. - auto static write(std::same_as auto data) noexcept -> void - { - asm volatile((code[Derived::size / 2]) - : - : [port] "i"(Derived::address), [data] "im"(data) - : "dx", (Derived::data_register)); - } - - private: - constexpr port_write() noexcept = default; - friend Derived; - - //! The assembly templates used for writing to an I/O port. - constexpr auto static code = std::array{ - std::string_view{"mov %[port], %%dx\nmov %[data], %%al\nout %%al, %%dx"}, - std::string_view{"mov %[port], %%dx\nmov %[data], %%ax\nout %%ax, %%dx"}, - std::string_view{"mov %[port], %%dx\nmov %[data], %%eax\nout %%eax, %%dx"}, - }; - }; - - //! An I/O port of a given size at a given address. - //! - //! Port I/O leverages a separate address space to communicate with devices via the memory bus, allowing for byte - //! to double-word sized transfers. - //! - //! @tparam Address The address (port number) of the I/O port. - //! @tparam Size The size (in bytes) of the I/O port. - //! @tparam Features The features (readable, writeable) - template typename... Features> - requires(sizeof...(Features) > 0) - struct port : Features>... - { - //! The type of the data of this port. - using value_type = ValueType; - - //! The address of this I/O port. - constexpr auto static address = Address; - - //! The size of this I/O port. - constexpr auto static size = sizeof(value_type); - - //! The register clobbered by the I/O operation. - constexpr auto static data_register = size == 1 ? std::string_view{"al"} - : size == 2 ? std::string_view{"ax"} - : std::string_view{"eax"}; - }; - - auto inline wait() -> void - { - port<0x80, std::uint8_t, port_write>::write(0); - } - -} // namespace arch::io - -#endif \ No newline at end of file diff --git a/arch/x86_64/arch/devices/init.cpp b/arch/x86_64/arch/devices/init.cpp index 03d082f6..e73ffa59 100644 --- a/arch/x86_64/arch/devices/init.cpp +++ b/arch/x86_64/arch/devices/init.cpp @@ -62,7 +62,12 @@ namespace arch::devices auto isa_bus = kstd::make_shared(); root_bus->add_child(isa_bus); - isa_bus->add_child(kstd::make_shared()); + auto pit_device = kstd::make_shared(); + pit_device->set_resources({ + kapi::devices::resource{kapi::devices::io_port{.start = 0x40, .count = 4}}, + kapi::devices::resource{kapi::devices::interrupt_line{.number = 0}}, + }); + isa_bus->add_child(pit_device); } } // namespace arch::devices diff --git a/arch/x86_64/arch/drivers/pit.cpp b/arch/x86_64/arch/drivers/pit.cpp index 650c4245..94fa19e9 100644 --- a/arch/x86_64/arch/drivers/pit.cpp +++ b/arch/x86_64/arch/drivers/pit.cpp @@ -1,7 +1,7 @@ #include #include -#include +#include #include #include @@ -21,14 +21,12 @@ namespace arch::drivers namespace { - using command_port = io::port<0x43, std::uint8_t, io::port_write>; - using channel_0_port = io::port<0x40, std::uint8_t, io::port_write>; - using channel_1_port = io::port<0x41, std::uint8_t, io::port_write>; - using channel_2_port = io::port<0x42, std::uint8_t, io::port_write>; - constexpr auto base_frequency = 1'193'182u; constexpr auto square_wave_mode = 0x36; + constexpr auto channel_0_offset = 0u; + constexpr auto command_offset = 3u; + constexpr auto pit_names = std::array{"pit"sv}; } // namespace @@ -38,18 +36,33 @@ namespace arch::drivers auto pit::probe(kapi::devices::device & device) -> kstd::result { - auto data = kstd::make_shared(0, 0); + auto port = device.request_resource(); + if (!port) + { + return kstd::failure(port.error()); + } + + auto irq = device.request_resource(); + if (!irq) + { + return kstd::failure(irq.error()); + } + + auto base_port = port->start; + auto irq_number = irq->number; + + auto data = kstd::make_shared(irq_number, 0); device.set_driver_data(data); auto divisor = static_cast(base_frequency / m_frequency_in_hz); kapi::interrupts::register_handler(data->irq_number, *this, data); - command_port::write(square_wave_mode); + io::write_port(static_cast(base_port + command_offset), square_wave_mode); io::wait(); - channel_0_port::write(divisor & 0xff); + io::write_port(static_cast(base_port + channel_0_offset), divisor & 0xff); io::wait(); - channel_0_port::write(divisor >> 8 & 0xff); + io::write_port(static_cast(base_port + channel_0_offset), divisor >> 8 & 0xff); io::wait(); return kstd::success(); diff --git a/arch/x86_64/arch/io/port_io.hpp b/arch/x86_64/arch/io/port_io.hpp new file mode 100644 index 00000000..475e223b --- /dev/null +++ b/arch/x86_64/arch/io/port_io.hpp @@ -0,0 +1,165 @@ +#ifndef TEACHOS_X86_64_IO_PORT_IO_HPP +#define TEACHOS_X86_64_IO_PORT_IO_HPP + +#include +#include +#include +#include +#include +#include + +namespace arch::io +{ + + //! The requirements imposed on a type usable for port I/O. + template + concept port_io_type = requires { + requires sizeof(ValueType) == 1 || sizeof(ValueType) == 2 || sizeof(ValueType) == 4; + requires std::default_initializable; + std::bit_cast( + std::conditional_t>{}); + }; + + template + struct port_read + { + //! Read from the I/O port. + //! + //! @return The data read from the I/O port. + auto static read() noexcept + { + auto data = typename Derived::value_type{}; + asm volatile((code[Derived::size / 2]) + : [data] "=m"(data) + : [port] "i"(Derived::address) + : "dx", (Derived::data_register)); + return data; + } + + private: + constexpr port_read() noexcept = default; + friend Derived; + + //! The assembly templates used for reading from an I/O port. + constexpr auto static code = std::array{ + std::string_view{"mov %[port], %%dx\nin %%dx, %%al\nmov %%al, %[data]"}, + std::string_view{"mov %[port], %%dx\nin %%dx, %%ax\nmov %%ax, %[data]"}, + std::string_view{"mov %[port], %%dx\nin %%dx, %%eax\nmov %%eax, %[data]"}, + }; + }; + + template + struct port_write + { + //! Write data to the I/O port. + //! + //! @param data The data to write to the I/O port. + auto static write(std::same_as auto data) noexcept -> void + { + asm volatile((code[Derived::size / 2]) + : + : [port] "i"(Derived::address), [data] "im"(data) + : "dx", (Derived::data_register)); + } + + private: + constexpr port_write() noexcept = default; + friend Derived; + + //! The assembly templates used for writing to an I/O port. + constexpr auto static code = std::array{ + std::string_view{"mov %[port], %%dx\nmov %[data], %%al\nout %%al, %%dx"}, + std::string_view{"mov %[port], %%dx\nmov %[data], %%ax\nout %%ax, %%dx"}, + std::string_view{"mov %[port], %%dx\nmov %[data], %%eax\nout %%eax, %%dx"}, + }; + }; + + //! An I/O port of a given size at a given address. + //! + //! Port I/O leverages a separate address space to communicate with devices via the memory bus, allowing for byte + //! to double-word sized transfers. + //! + //! @tparam Address The address (port number) of the I/O port. + //! @tparam Size The size (in bytes) of the I/O port. + //! @tparam Features The features (readable, writeable) + template typename... Features> + requires(sizeof...(Features) > 0) + struct port : Features>... + { + //! The type of the data of this port. + using value_type = ValueType; + + //! The address of this I/O port. + constexpr auto static address = Address; + + //! The size of this I/O port. + constexpr auto static size = sizeof(value_type); + + //! The register clobbered by the I/O operation. + constexpr auto static data_register = size == 1 ? std::string_view{"al"} + : size == 2 ? std::string_view{"ax"} + : std::string_view{"eax"}; + }; + + auto inline wait() -> void + { + port<0x80, std::uint8_t, port_write>::write(0); + } + + //! Read from a port whose address is only known at runtime. + //! + //! @tparam ValueType The type of the value to read. + //! @param address The address of the port to read from. + //! @return The value read from the port. + template + [[nodiscard]] auto read_port(std::uint16_t address) noexcept -> ValueType + { + if constexpr (sizeof(ValueType) == 1) + { + auto data = std::uint8_t{}; + asm volatile("in %%dx, %%al" : "=a"(data) : "Nd"(address)); + return std::bit_cast(data); + } + else if constexpr (sizeof(ValueType) == 2) + { + auto data = std::uint16_t{}; + asm volatile("in %%dx, %%ax" : "=a"(data) : "Nd"(address)); + return std::bit_cast(data); + } + else + { + auto data = std::uint32_t{}; + asm volatile("in %%dx, %%eax" : "=a"(data) : "Nd"(address)); + return std::bit_cast(data); + } + } + + //! Write to a port whose address is only known at runtime. + //! + //! @tparam ValueType The type of the value to write. + //! @param address The address of the port to write to. + //! @param data The value to write. + template + auto write_port(std::uint16_t address, ValueType data) noexcept -> void + { + if constexpr (sizeof(ValueType) == 1) + { + auto raw = std::bit_cast(data); + asm volatile("out %%al, %%dx" : : "a"(raw), "Nd"(address)); + } + else if constexpr (sizeof(ValueType) == 2) + { + auto raw = std::bit_cast(data); + asm volatile("out %%ax, %%dx" : : "a"(raw), "Nd"(address)); + } + else + { + auto raw = std::bit_cast(data); + asm volatile("out %%eax, %%dx" : : "a"(raw), "Nd"(address)); + } + } + +} // namespace arch::io + +#endif \ No newline at end of file -- cgit v1.2.3