summaryrefslogtreecommitdiff
path: root/Kernel/Arch/x86/IO.h
blob: f38dfc89bd29fd0e2f73c860968449ec48be0843 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/*
 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#pragma once

#include <AK/Assertions.h>
#include <AK/Format.h>
#include <AK/Types.h>

#include <AK/Platform.h>
VALIDATE_IS_X86()

namespace IO {

// Every character written to this IO port is written to the Bochs console
// (e.g. the console where Qemu is running).
static constexpr u16 BOCHS_DEBUG_PORT = 0xE9;

inline u8 in8(u16 port)
{
    u8 value;
    asm volatile("inb %1, %0"
                 : "=a"(value)
                 : "Nd"(port));
    return value;
}

inline u16 in16(u16 port)
{
    u16 value;
    asm volatile("inw %1, %0"
                 : "=a"(value)
                 : "Nd"(port));
    return value;
}

inline u32 in32(u16 port)
{
    u32 value;
    asm volatile("inl %1, %0"
                 : "=a"(value)
                 : "Nd"(port));
    return value;
}

inline void out8(u16 port, u8 value)
{
    asm volatile("outb %0, %1" ::"a"(value), "Nd"(port));
}

inline void out16(u16 port, u16 value)
{
    asm volatile("outw %0, %1" ::"a"(value), "Nd"(port));
}

inline void out32(u16 port, u32 value)
{
    asm volatile("outl %0, %1" ::"a"(value), "Nd"(port));
}

inline void delay(size_t microseconds)
{
    for (size_t i = 0; i < microseconds; ++i)
        IO::in8(0x80);
}

}

class IOAddress {
public:
    IOAddress() = default;
    explicit IOAddress(u16 address)
        : m_address(address)
    {
    }

    IOAddress offset(u16 o) const { return IOAddress(m_address + o); }
    u16 get() const { return m_address; }
    void set(u16 address) { m_address = address; }
    void mask(u16 m) { m_address &= m; }

    template<typename T>
    ALWAYS_INLINE T in()
    {
        static_assert(sizeof(T) <= 4);
        if constexpr (sizeof(T) == 4)
            return IO::in32(get());
        if constexpr (sizeof(T) == 2)
            return IO::in16(get());
        if constexpr (sizeof(T) == 1)
            return IO::in8(get());
        VERIFY_NOT_REACHED();
    }

    template<typename T>
    ALWAYS_INLINE void out(T value) const
    {
        static_assert(sizeof(T) <= 4);
        if constexpr (sizeof(T) == 4) {
            IO::out32(get(), value);
            return;
        }
        if constexpr (sizeof(T) == 2) {
            IO::out16(get(), value);
            return;
        }
        if constexpr (sizeof(T) == 1) {
            IO::out8(get(), value);
            return;
        }
        VERIFY_NOT_REACHED();
    }

    inline void out(u32 value, u8 bit_width) const
    {
        if (bit_width == 32) {
            IO::out32(get(), value);
            return;
        }
        if (bit_width == 16) {
            IO::out16(get(), value);
            return;
        }
        if (bit_width == 8) {
            IO::out8(get(), value);
            return;
        }
        VERIFY_NOT_REACHED();
    }

    bool is_null() const { return m_address == 0; }

    bool operator==(IOAddress const& other) const { return m_address == other.m_address; }
    bool operator!=(IOAddress const& other) const { return m_address != other.m_address; }
    bool operator>(IOAddress const& other) const { return m_address > other.m_address; }
    bool operator>=(IOAddress const& other) const { return m_address >= other.m_address; }
    bool operator<(IOAddress const& other) const { return m_address < other.m_address; }
    bool operator<=(IOAddress const& other) const { return m_address <= other.m_address; }

private:
    u16 m_address { 0 };
};

template<>
struct AK::Formatter<IOAddress> : AK::Formatter<FormatString> {
    ErrorOr<void> format(FormatBuilder& builder, IOAddress value)
    {
        return Formatter<FormatString>::format(builder, "IO {:x}", value.get());
    }
};