summaryrefslogtreecommitdiff
path: root/Kernel/Bus/PCI/API.cpp
blob: ff4602acd42264b2d0edf219f6cde10c00676ef7 (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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
/*
 * Copyright (c) 2021, Liav A. <liavalb@hotmail.co.il>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include <Kernel/Bus/PCI/API.h>
#include <Kernel/Bus/PCI/Access.h>
#include <Kernel/Sections.h>

namespace Kernel::PCI {

void write8_locked(DeviceIdentifier const& identifier, PCI::RegisterOffset field, u8 value)
{
    VERIFY(identifier.operation_lock().is_locked());
    Access::the().write8_field(identifier, to_underlying(field), value);
}
void write16_locked(DeviceIdentifier const& identifier, PCI::RegisterOffset field, u16 value)
{
    VERIFY(identifier.operation_lock().is_locked());
    Access::the().write16_field(identifier, to_underlying(field), value);
}
void write32_locked(DeviceIdentifier const& identifier, PCI::RegisterOffset field, u32 value)
{
    VERIFY(identifier.operation_lock().is_locked());
    Access::the().write32_field(identifier, to_underlying(field), value);
}

u8 read8_locked(DeviceIdentifier const& identifier, PCI::RegisterOffset field)
{
    VERIFY(identifier.operation_lock().is_locked());
    return Access::the().read8_field(identifier, to_underlying(field));
}
u16 read16_locked(DeviceIdentifier const& identifier, PCI::RegisterOffset field)
{
    VERIFY(identifier.operation_lock().is_locked());
    return Access::the().read16_field(identifier, to_underlying(field));
}
u32 read32_locked(DeviceIdentifier const& identifier, PCI::RegisterOffset field)
{
    VERIFY(identifier.operation_lock().is_locked());
    return Access::the().read32_field(identifier, to_underlying(field));
}

ErrorOr<void> enumerate(Function<void(DeviceIdentifier const&)> callback)
{
    return Access::the().fast_enumerate(callback);
}

HardwareID get_hardware_id(DeviceIdentifier const& identifier)
{
    SpinlockLocker locker(identifier.operation_lock());
    return { read16_locked(identifier, PCI::RegisterOffset::VENDOR_ID), read16_locked(identifier, PCI::RegisterOffset::DEVICE_ID) };
}

void enable_io_space(DeviceIdentifier const& identifier)
{
    SpinlockLocker locker(identifier.operation_lock());
    write16_locked(identifier, PCI::RegisterOffset::COMMAND, read16_locked(identifier, PCI::RegisterOffset::COMMAND) | (1 << 0));
}
void disable_io_space(DeviceIdentifier const& identifier)
{
    SpinlockLocker locker(identifier.operation_lock());
    write16_locked(identifier, PCI::RegisterOffset::COMMAND, read16_locked(identifier, PCI::RegisterOffset::COMMAND) & ~(1 << 0));
}

void enable_memory_space(DeviceIdentifier const& identifier)
{
    SpinlockLocker locker(identifier.operation_lock());
    write16_locked(identifier, PCI::RegisterOffset::COMMAND, read16_locked(identifier, PCI::RegisterOffset::COMMAND) | (1 << 1));
}
void disable_memory_space(DeviceIdentifier const& identifier)
{
    SpinlockLocker locker(identifier.operation_lock());
    write16_locked(identifier, PCI::RegisterOffset::COMMAND, read16_locked(identifier, PCI::RegisterOffset::COMMAND) & ~(1 << 1));
}

bool is_io_space_enabled(DeviceIdentifier const& identifier)
{
    SpinlockLocker locker(identifier.operation_lock());
    return (read16_locked(identifier, PCI::RegisterOffset::COMMAND) & 1) != 0;
}

void enable_interrupt_line(DeviceIdentifier const& identifier)
{
    SpinlockLocker locker(identifier.operation_lock());
    write16_locked(identifier, PCI::RegisterOffset::COMMAND, read16_locked(identifier, PCI::RegisterOffset::COMMAND) & ~(1 << 10));
}

void disable_interrupt_line(DeviceIdentifier const& identifier)
{
    SpinlockLocker locker(identifier.operation_lock());
    write16_locked(identifier, PCI::RegisterOffset::COMMAND, read16_locked(identifier, PCI::RegisterOffset::COMMAND) | 1 << 10);
}

u32 get_BAR0(DeviceIdentifier const& identifier)
{
    SpinlockLocker locker(identifier.operation_lock());
    return read32_locked(identifier, PCI::RegisterOffset::BAR0);
}

u32 get_BAR1(DeviceIdentifier const& identifier)
{
    SpinlockLocker locker(identifier.operation_lock());
    return read32_locked(identifier, PCI::RegisterOffset::BAR1);
}

u32 get_BAR2(DeviceIdentifier const& identifier)
{
    SpinlockLocker locker(identifier.operation_lock());
    return read32_locked(identifier, PCI::RegisterOffset::BAR2);
}

u32 get_BAR3(DeviceIdentifier const& identifier)
{
    SpinlockLocker locker(identifier.operation_lock());
    return read32_locked(identifier, PCI::RegisterOffset::BAR3);
}

u32 get_BAR4(DeviceIdentifier const& identifier)
{
    SpinlockLocker locker(identifier.operation_lock());
    return read32_locked(identifier, PCI::RegisterOffset::BAR4);
}

u32 get_BAR5(DeviceIdentifier const& identifier)
{
    SpinlockLocker locker(identifier.operation_lock());
    return read32_locked(identifier, PCI::RegisterOffset::BAR5);
}

u32 get_BAR(DeviceIdentifier const& identifier, HeaderType0BaseRegister pci_bar)
{
    VERIFY(to_underlying(pci_bar) <= 5);
    switch (to_underlying(pci_bar)) {
    case 0:
        return get_BAR0(identifier);
    case 1:
        return get_BAR1(identifier);
    case 2:
        return get_BAR2(identifier);
    case 3:
        return get_BAR3(identifier);
    case 4:
        return get_BAR4(identifier);
    case 5:
        return get_BAR5(identifier);
    default:
        VERIFY_NOT_REACHED();
    }
}

BARSpaceType get_BAR_space_type(u32 pci_bar_value)
{
    // Note: For IO space, bit 0 is set to 1.
    if (pci_bar_value & (1 << 0))
        return BARSpaceType::IOSpace;
    auto memory_space_type = (pci_bar_value >> 1) & 0b11;
    switch (memory_space_type) {
    case 0:
        return BARSpaceType::Memory32BitSpace;
    case 1:
        return BARSpaceType::Memory16BitSpace;
    case 2:
        return BARSpaceType::Memory64BitSpace;
    default:
        VERIFY_NOT_REACHED();
    }
}

void enable_bus_mastering(DeviceIdentifier const& identifier)
{
    SpinlockLocker locker(identifier.operation_lock());
    auto value = read16_locked(identifier, PCI::RegisterOffset::COMMAND);
    value |= (1 << 2);
    value |= (1 << 0);
    write16_locked(identifier, PCI::RegisterOffset::COMMAND, value);
}

void disable_bus_mastering(DeviceIdentifier const& identifier)
{
    SpinlockLocker locker(identifier.operation_lock());
    auto value = read16_locked(identifier, PCI::RegisterOffset::COMMAND);
    value &= ~(1 << 2);
    value |= (1 << 0);
    write16_locked(identifier, PCI::RegisterOffset::COMMAND, value);
}

static void write8_offsetted(DeviceIdentifier const& identifier, u32 field, u8 value)
{
    VERIFY(identifier.operation_lock().is_locked());
    Access::the().write8_field(identifier, field, value);
}
static void write16_offsetted(DeviceIdentifier const& identifier, u32 field, u16 value)
{
    VERIFY(identifier.operation_lock().is_locked());
    Access::the().write16_field(identifier, field, value);
}
static void write32_offsetted(DeviceIdentifier const& identifier, u32 field, u32 value)
{
    VERIFY(identifier.operation_lock().is_locked());
    Access::the().write32_field(identifier, field, value);
}
static u8 read8_offsetted(DeviceIdentifier const& identifier, u32 field)
{
    VERIFY(identifier.operation_lock().is_locked());
    return Access::the().read8_field(identifier, field);
}
static u16 read16_offsetted(DeviceIdentifier const& identifier, u32 field)
{
    VERIFY(identifier.operation_lock().is_locked());
    return Access::the().read16_field(identifier, field);
}
static u32 read32_offsetted(DeviceIdentifier const& identifier, u32 field)
{
    VERIFY(identifier.operation_lock().is_locked());
    return Access::the().read32_field(identifier, field);
}

size_t get_BAR_space_size(DeviceIdentifier const& identifier, HeaderType0BaseRegister pci_bar)
{
    SpinlockLocker locker(identifier.operation_lock());
    // See PCI Spec 2.3, Page 222
    VERIFY(to_underlying(pci_bar) < 6);
    u8 field = to_underlying(PCI::RegisterOffset::BAR0) + (to_underlying(pci_bar) << 2);
    u32 bar_reserved = read32_offsetted(identifier, field);
    write32_offsetted(identifier, field, 0xFFFFFFFF);
    u32 space_size = read32_offsetted(identifier, field);
    write32_offsetted(identifier, field, bar_reserved);
    space_size &= 0xfffffff0;
    space_size = (~space_size) + 1;
    return space_size;
}

size_t get_expansion_rom_space_size(DeviceIdentifier const& identifier)
{
    SpinlockLocker locker(identifier.operation_lock());
    u8 field = to_underlying(PCI::RegisterOffset::EXPANSION_ROM_POINTER);
    u32 bar_reserved = read32_offsetted(identifier, field);
    write32_offsetted(identifier, field, 0xFFFFFFFF);
    u32 space_size = read32_offsetted(identifier, field);
    write32_offsetted(identifier, field, bar_reserved);
    space_size &= 0xfffffff0;
    space_size = (~space_size) + 1;
    return space_size;
}

void raw_access(DeviceIdentifier const& identifier, u32 field, size_t access_size, u32 value)
{
    SpinlockLocker locker(identifier.operation_lock());
    VERIFY(access_size != 0);
    if (access_size == 1) {
        write8_offsetted(identifier, field, value);
        return;
    }
    if (access_size == 2) {
        write16_offsetted(identifier, field, value);
        return;
    }
    if (access_size == 4) {
        write32_offsetted(identifier, field, value);
        return;
    }
    VERIFY_NOT_REACHED();
}

u8 Capability::read8(size_t offset) const
{
    auto& identifier = get_device_identifier(m_address);
    SpinlockLocker locker(identifier.operation_lock());
    return read8_offsetted(identifier, m_ptr + offset);
}

u16 Capability::read16(size_t offset) const
{
    auto& identifier = get_device_identifier(m_address);
    SpinlockLocker locker(identifier.operation_lock());
    return read16_offsetted(identifier, m_ptr + offset);
}

u32 Capability::read32(size_t offset) const
{
    auto& identifier = get_device_identifier(m_address);
    SpinlockLocker locker(identifier.operation_lock());
    return read32_offsetted(identifier, m_ptr + offset);
}

DeviceIdentifier const& get_device_identifier(Address address)
{
    return Access::the().get_device_identifier(address);
}

}