Serenity Operating System
1/*
2 * Copyright (c) 2020, Liav A. <liavalb@hotmail.co.il>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <Kernel/PCI/Access.h>
28#include <Kernel/PCI/IOAccess.h>
29
30namespace Kernel {
31namespace PCI {
32
33static Access* s_access;
34
35inline void write8(Address address, u32 field, u8 value) { Access::the().write8_field(address, field, value); }
36inline void write16(Address address, u32 field, u16 value) { Access::the().write16_field(address, field, value); }
37inline void write32(Address address, u32 field, u32 value) { Access::the().write32_field(address, field, value); }
38inline u8 read8(Address address, u32 field) { return Access::the().read8_field(address, field); }
39inline u16 read16(Address address, u32 field) { return Access::the().read16_field(address, field); }
40inline u32 read32(Address address, u32 field) { return Access::the().read32_field(address, field); }
41
42Access& Access::the()
43{
44 if (s_access == nullptr) {
45 ASSERT_NOT_REACHED(); // We failed to initialize the PCI subsystem, so stop here!
46 }
47 return *s_access;
48}
49
50bool Access::is_initialized()
51{
52 return (s_access != nullptr);
53}
54
55Access::Access()
56{
57 s_access = this;
58}
59
60static u16 read_type(Address address)
61{
62 return (read8(address, PCI_CLASS) << 8u) | read8(address, PCI_SUBCLASS);
63}
64
65void Access::enumerate_functions(int type, u8 bus, u8 slot, u8 function, Function<void(Address, ID)>& callback)
66{
67 Address address(0, bus, slot, function);
68 if (type == -1 || type == read_type(address))
69 callback(address, { read16_field(address, PCI_VENDOR_ID), read16_field(address, PCI_DEVICE_ID) });
70 if (read_type(address) == PCI_TYPE_BRIDGE) {
71 u8 secondary_bus = read8_field(address, PCI_SECONDARY_BUS);
72#ifdef PCI_DEBUG
73 klog() << "PCI: Found secondary bus: " << secondary_bus;
74#endif
75 ASSERT(secondary_bus != bus);
76 enumerate_bus(type, secondary_bus, callback);
77 }
78}
79
80void Access::enumerate_slot(int type, u8 bus, u8 slot, Function<void(Address, ID)>& callback)
81{
82 Address address(0, bus, slot, 0);
83 if (read16_field(address, PCI_VENDOR_ID) == PCI_NONE)
84 return;
85 enumerate_functions(type, bus, slot, 0, callback);
86 if (!(read8_field(address, PCI_HEADER_TYPE) & 0x80))
87 return;
88 for (u8 function = 1; function < 8; ++function) {
89 Address address(0, bus, slot, function);
90 if (read16_field(address, PCI_VENDOR_ID) != PCI_NONE)
91 enumerate_functions(type, bus, slot, function, callback);
92 }
93}
94
95void Access::enumerate_bus(int type, u8 bus, Function<void(Address, ID)>& callback)
96{
97 for (u8 slot = 0; slot < 32; ++slot)
98 enumerate_slot(type, bus, slot, callback);
99}
100
101void enumerate_all(Function<void(Address, ID)> callback)
102{
103 Access::the().enumerate_all(callback);
104}
105
106void raw_access(Address address, u32 field, size_t access_size, u32 value)
107{
108 ASSERT(access_size != 0);
109 if (access_size == 1) {
110 write8(address, field, value);
111 return;
112 }
113 if (access_size == 2) {
114 write16(address, field, value);
115 return;
116 }
117 if (access_size == 4) {
118 write32(address, field, value);
119 return;
120 }
121 ASSERT_NOT_REACHED();
122}
123
124ID get_id(Address address)
125{
126 return { read16(address, PCI_VENDOR_ID), read16(address, PCI_DEVICE_ID) };
127}
128
129void enable_interrupt_line(Address address)
130{
131 write16(address, PCI_COMMAND, read16(address, PCI_COMMAND) & ~(1 << 10));
132}
133
134void disable_interrupt_line(Address address)
135{
136 write16(address, PCI_COMMAND, read16(address, PCI_COMMAND) | 1 << 10);
137}
138
139u8 get_interrupt_line(Address address)
140{
141 return read8(address, PCI_INTERRUPT_LINE);
142}
143
144u32 get_BAR0(Address address)
145{
146 return read32(address, PCI_BAR0);
147}
148
149u32 get_BAR1(Address address)
150{
151 return read32(address, PCI_BAR1);
152}
153
154u32 get_BAR2(Address address)
155{
156 return read32(address, PCI_BAR2);
157}
158
159u32 get_BAR3(Address address)
160{
161 return read16(address, PCI_BAR3);
162}
163
164u32 get_BAR4(Address address)
165{
166 return read32(address, PCI_BAR4);
167}
168
169u32 get_BAR5(Address address)
170{
171 return read32(address, PCI_BAR5);
172}
173
174u8 get_revision_id(Address address)
175{
176 return read8(address, PCI_REVISION_ID);
177}
178
179u8 get_subclass(Address address)
180{
181 return read8(address, PCI_SUBCLASS);
182}
183
184u8 get_class(Address address)
185{
186 return read8(address, PCI_CLASS);
187}
188
189u16 get_subsystem_id(Address address)
190{
191 return read16(address, PCI_SUBSYSTEM_ID);
192}
193
194u16 get_subsystem_vendor_id(Address address)
195{
196 return read16(address, PCI_SUBSYSTEM_VENDOR_ID);
197}
198
199void enable_bus_mastering(Address address)
200{
201 auto value = read16(address, PCI_COMMAND);
202 value |= (1 << 2);
203 value |= (1 << 0);
204 write16(address, PCI_COMMAND, value);
205}
206
207void disable_bus_mastering(Address address)
208{
209 auto value = read16(address, PCI_COMMAND);
210 value &= ~(1 << 2);
211 value |= (1 << 0);
212 write16(address, PCI_COMMAND, value);
213}
214
215size_t get_BAR_space_size(Address address, u8 bar_number)
216{
217 // See PCI Spec 2.3, Page 222
218 ASSERT(bar_number < 6);
219 u8 field = (PCI_BAR0 + (bar_number << 2));
220 u32 bar_reserved = read32(address, field);
221 write32(address, field, 0xFFFFFFFF);
222 u32 space_size = read32(address, field);
223 write32(address, field, bar_reserved);
224 space_size &= 0xfffffff0;
225 space_size = (~space_size) + 1;
226 return space_size;
227}
228
229}
230}