aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFelix Morgner <felix.morgner@ost.ch>2025-12-12 10:16:23 +0100
committerFelix Morgner <felix.morgner@ost.ch>2025-12-12 10:16:23 +0100
commit67267c4d103de2673191c113c1e99e66965aab5c (patch)
tree6c24b0f31037ce75fc21629244b9d970f8453a64
parentcf8d0d899ee17db734ce8ab7ee618333eb1767f2 (diff)
downloadteachos-67267c4d103de2673191c113c1e99e66965aab5c.tar.xz
teachos-67267c4d103de2673191c113c1e99e66965aab5c.zip
x86_64/device_io: simplify implementation
-rw-r--r--arch/x86_64/include/x86_64/device_io/port_io.hpp155
1 files changed, 53 insertions, 102 deletions
diff --git a/arch/x86_64/include/x86_64/device_io/port_io.hpp b/arch/x86_64/include/x86_64/device_io/port_io.hpp
index 352a4d0..ceabf4a 100644
--- a/arch/x86_64/include/x86_64/device_io/port_io.hpp
+++ b/arch/x86_64/include/x86_64/device_io/port_io.hpp
@@ -1,130 +1,81 @@
#ifndef TEACHOS_X86_64_IO_PORT_IO_HPP
#define TEACHOS_X86_64_IO_PORT_IO_HPP
+#include <array>
#include <cstddef>
#include <cstdint>
+#include <string_view>
#include <type_traits>
namespace teachos::io::x86_64
{
- /**
- * @brief An I/O port of a given size at a given address.
- *
- * @tparam Address The address (port number) of the I/O port.
- * @tparam Size The size (in bytes) of the I/O port.
- */
+
+ //! 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.
template<std::uint16_t Address, std::size_t Size>
+ requires(Size == 1 || Size == 2 || Size == 4)
struct port
{
- static_assert(Size == 1 || Size == 2 || Size == 4, "A port must be either 1, 2, or 4 bytes in size");
-
- /**
- * @brief The type of data available for reading and writing through this port.
- */
- using io_type =
+ //! The type of data available for reading and writing through this port.
+ using value_type =
std::conditional_t<Size == 1, std::byte, std::conditional_t<Size == 2, std::uint16_t, std::uint32_t>>;
- /**
- * @brief Write a byte to the I/O port.
- *
- * @param data The data to write to the I/O port.
- */
- auto static write(io_type data) -> void
- requires(Size == 1)
+ //! Read from the I/O port.
+ //!
+ //! @return The data read from the I/O port.
+ auto static read() noexcept -> value_type
{
- asm volatile("mov %[port], %%dx\n"
- "mov %[data], %%al\n"
- "out %%al, %%dx\n"
- :
- : [port] "i"(Address), [data] "im"(data)
- : "dx", "al");
+ auto data = value_type{};
+ asm volatile((read_code[asm_template_index])
+ : [data] "=m"(data)
+ : [port] "i"(Address)
+ : "dx", (data_register[asm_template_index]));
+ return data;
}
- /**
- * @brief Write a word to the I/O port.
- *
- * @param data The data to write to the I/O port.
- */
- auto static write(io_type data) -> void
- requires(Size == 2)
+ //! Write data to the I/O port.
+ //!
+ //! @param data The data to write to the I/O port.
+ auto static write(value_type data) noexcept -> void
{
- asm volatile("mov %[port], %%dx\n"
- "mov %[data], %%ax\n"
- "out %%ax, %%dx\n"
+ asm volatile((write_code[asm_template_index])
:
: [port] "i"(Address), [data] "im"(data)
- : "dx", "ax");
+ : "dx", (data_register[asm_template_index]));
}
- /**
- * @brief Write a double-word to the I/O port.
- *
- * @param data The data to write to the I/O port.
- */
- auto static write(io_type data) -> void
- requires(Size == 4)
- {
- asm volatile("mov %[port], %%dx\n"
- "mov %[data], %%eax\n"
- "out %%eax, %%dx\n"
- :
- : [port] "i"(Address), [data] "im"(data)
- : "dx", "eax");
- }
+ private:
+ //! The index into the assembly template and register arrays.
+ //!
+ //! This index is used to select the correct assembly templates for the given operation (read or write), as well as
+ //! the clobbered data register, from the relevant template arrays.
+ constexpr auto static asm_template_index = Size / 2;
- /**
- * @brief Read a byte from the I/O port.
- *
- * @return The data read from the I/O port.
- */
- auto static read() -> io_type
- requires(Size == 1)
- {
- auto data = io_type{};
- asm volatile("mov %[port], %%dx\n"
- "in %%dx, %%al\n"
- "mov %%al, %[data]\n"
- : [data] "=m"(data)
- : [port] "i"(Address)
- : "dx", "al");
- return data;
- }
+ //! The assembly templates used for reading from an I/O port.
+ constexpr auto static read_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]"},
+ };
- /**
- * @brief Read a word from the I/O port.
- *
- * @return The data read from the I/O port.
- */
- auto static read() -> io_type
- requires(Size == 2)
- {
- auto data = io_type{};
- asm volatile("mov %[port], %%dx\n"
- "in %%dx, %%ax\n"
- "mov %%ax, %[data]\n"
- : [data] "=m"(data)
- : [port] "i"(Address)
- : "dx", "ax");
- return data;
- }
+ //! The assembly templates used for writing to an I/O port.
+ constexpr auto static write_code = std::array{
+ std::string_view{"mov %[port], %%dx\nmov %%dx, %[data]\nout %%al, %%dx"},
+ std::string_view{"mov %[port], %%dx\nmov %%dx, %[data]\nout %%ax, %%dx"},
+ std::string_view{"mov %[port], %%dx\nmov %%dx, %[data]\nout %%eax, %%dx"},
+ };
- /**
- * @brief Read a double-word from the I/O port.
- *
- * @return The data read from the I/O port.
- */
- auto static read() -> io_type
- requires(Size == 4)
- {
- auto data = io_type{};
- asm volatile("mov %[port], %%dx\n"
- "in %%dx, %%eax\n"
- "mov %%eax, %[data]\n"
- : [data] "=m"(data)
- : [port] "i"(Address)
- : "dx", "eax");
- return data;
- }
+ //! The registers clobbered by the I/O operation.
+ constexpr auto static data_register = std::array{
+ std::string_view{"al"},
+ std::string_view{"ax"},
+ std::string_view{"eax"},
+ };
};
} // namespace teachos::io::x86_64