refactor: Optimize the context switch overhead
[lunaix-os.git] / lunaix-os / hal / pci.c
1 /**
2  * @file pci.c
3  * @author Lunaixsky (zelong56@gmail.com)
4  * @brief A software implementation of PCI Local Bus Specification Revision 3.0
5  * @version 0.1
6  * @date 2022-06-28
7  *
8  * @copyright Copyright (c) 2022
9  *
10  */
11 #include <hal/acpi/acpi.h>
12 #include <hal/apic.h>
13 #include <hal/pci.h>
14 #include <klibc/string.h>
15 #include <lunaix/fs/twifs.h>
16 #include <lunaix/mm/valloc.h>
17 #include <lunaix/spike.h>
18 #include <lunaix/syslog.h>
19
20 LOG_MODULE("PCI")
21
22 static DEFINE_LLIST(pci_devices);
23 static DEFINE_LLIST(pci_drivers);
24
25 void
26 pci_probe_msi_info(struct pci_device* device);
27
28 void
29 pci_probe_device(int bus, int dev, int funct)
30 {
31     u32_t base = PCI_ADDRESS(bus, dev, funct);
32     pci_reg_t reg1 = pci_read_cspace(base, 0);
33
34     // Vendor=0xffff则表示设备不存在
35     if (PCI_DEV_VENDOR(reg1) == PCI_VENDOR_INVLD) {
36         return;
37     }
38
39     pci_reg_t hdr_type = pci_read_cspace(base, 0xc);
40     hdr_type = (hdr_type >> 16) & 0xff;
41
42     // 防止堆栈溢出
43     // QEMU的ICH9/Q35实现似乎有点问题,对于多功能设备的每一个功能的header type
44     //  都将第七位置位。而virtualbox 就没有这个毛病。
45     if ((hdr_type & 0x80) && funct == 0) {
46         hdr_type = hdr_type & ~0x80;
47         // 探测多用途设备(multi-function device)
48         for (int i = 1; i < 7; i++) {
49             pci_probe_device(bus, dev, i);
50         }
51     }
52
53     if (hdr_type != PCI_TDEV) {
54         // XXX: 目前忽略所有桥接设备,比如PCI-PCI桥接器,或者是CardBus桥接器
55         return;
56     }
57
58     pci_reg_t intr = pci_read_cspace(base, 0x3c);
59     pci_reg_t class = pci_read_cspace(base, 0x8);
60
61     struct pci_device* device = vzalloc(sizeof(struct pci_device));
62     *device = (struct pci_device){ .cspace_base = base,
63                                    .class_info = class,
64                                    .device_info = reg1,
65                                    .intr_info = intr };
66
67     pci_probe_msi_info(device);
68     pci_probe_bar_info(device);
69
70     llist_append(&pci_devices, &device->dev_chain);
71
72     if (!pci_bind_driver(device)) {
73         kprintf(KWARN "dev.%d:%d:%d %x:%x unknown device\n",
74                 bus,
75                 dev,
76                 funct,
77                 PCI_DEV_VENDOR(reg1),
78                 PCI_DEV_DEVID(reg1));
79     } else {
80         kprintf("dev.%d:%d:%d %x:%x %s\n",
81                 bus,
82                 dev,
83                 funct,
84                 PCI_DEV_VENDOR(reg1),
85                 PCI_DEV_DEVID(reg1),
86                 device->driver.type->name);
87     }
88 }
89
90 void
91 pci_probe()
92 {
93     // 暴力扫描所有PCI设备
94     // XXX: 尽管最多会有256条PCI总线,但就目前而言,只考虑bus #0就足够了
95     for (int bus = 0; bus < 256; bus++) {
96         for (int dev = 0; dev < 32; dev++) {
97             pci_probe_device(bus, dev, 0);
98         }
99     }
100 }
101
102 void
103 pci_probe_bar_info(struct pci_device* device)
104 {
105     u32_t bar;
106     struct pci_base_addr* ba;
107     for (size_t i = 0; i < 6; i++) {
108         ba = &device->bar[i];
109         ba->size = pci_bar_sizing(device, &bar, i + 1);
110         if (PCI_BAR_MMIO(bar)) {
111             ba->start = PCI_BAR_ADDR_MM(bar);
112             ba->type |= PCI_BAR_CACHEABLE(bar) ? BAR_TYPE_CACHABLE : 0;
113             ba->type |= BAR_TYPE_MMIO;
114         } else {
115             ba->start = PCI_BAR_ADDR_IO(bar);
116         }
117     }
118 }
119
120 void
121 pci_probe_msi_info(struct pci_device* device)
122 {
123     // Note that Virtualbox have to use ICH9 chipset for MSI support.
124     // Qemu seems ok with default PIIX3, Bochs is pending to test...
125     //    See https://www.virtualbox.org/manual/ch03.html (section 3.5.1)
126     pci_reg_t status =
127       pci_read_cspace(device->cspace_base, PCI_REG_STATUS_CMD) >> 16;
128
129     if (!(status & 0x10)) {
130         device->msi_loc = 0;
131         return;
132     }
133
134     pci_reg_t cap_ptr = pci_read_cspace(device->cspace_base, 0x34) & 0xff;
135     u32_t cap_hdr;
136
137     while (cap_ptr) {
138         cap_hdr = pci_read_cspace(device->cspace_base, cap_ptr);
139         if ((cap_hdr & 0xff) == 0x5) {
140             // MSI
141             device->msi_loc = cap_ptr;
142             return;
143         }
144         cap_ptr = (cap_hdr >> 8) & 0xff;
145     }
146 }
147
148 void
149 __pci_read_cspace(struct twimap* map)
150 {
151     struct pci_device* pcidev = (struct pci_device*)(map->data);
152
153     for (size_t i = 0; i < 256; i += sizeof(pci_reg_t)) {
154         *(pci_reg_t*)(map->buffer + i) =
155           pci_read_cspace(pcidev->cspace_base, i);
156     }
157
158     map->size_acc = 256;
159 }
160
161 void
162 __pci_read_revid(struct twimap* map)
163 {
164     int class = twimap_data(map, struct pci_device*)->class_info;
165     twimap_printf(map, "0x%x", PCI_DEV_REV(class));
166 }
167
168 void
169 __pci_read_class(struct twimap* map)
170 {
171     int class = twimap_data(map, struct pci_device*)->class_info;
172     twimap_printf(map, "0x%x", PCI_DEV_CLASS(class));
173 }
174
175 void
176 __pci_bar_read(struct twimap* map)
177 {
178     struct pci_device* pcidev = twimap_data(map, struct pci_device*);
179     int bar_index = twimap_index(map, int);
180
181     struct pci_base_addr* bar = &pcidev->bar[bar_index];
182
183     if (!bar->start && !bar->size) {
184         twimap_printf(map, "[%d] not present \n", bar_index);
185         return;
186     }
187
188     twimap_printf(
189       map, "[%d] base=%.8p, size=%.8p, ", bar_index, bar->start, bar->size);
190
191     if ((bar->type & BAR_TYPE_MMIO)) {
192         twimap_printf(map, "mmio");
193         if ((bar->type & BAR_TYPE_CACHABLE)) {
194             twimap_printf(map, ", prefetchable");
195         }
196     } else {
197         twimap_printf(map, "io");
198     }
199
200     twimap_printf(map, "\n");
201 }
202
203 int
204 __pci_bar_gonext(struct twimap* map)
205 {
206     if (twimap_index(map, int) >= 5) {
207         return 0;
208     }
209     map->index += 1;
210     return 1;
211 }
212
213 void
214 pci_build_fsmapping()
215 {
216     struct twifs_node *pci_class = twifs_dir_node(NULL, "pci"), *pci_dev;
217     struct pci_device *pos, *n;
218     struct twimap* map;
219     llist_for_each(pos, n, &pci_devices, dev_chain)
220     {
221         pci_dev = twifs_dir_node(pci_class,
222                                  "%.2d:%.2d:%.2d.%.4x:%.4x",
223                                  PCI_BUS_NUM(pos->cspace_base),
224                                  PCI_SLOT_NUM(pos->cspace_base),
225                                  PCI_FUNCT_NUM(pos->cspace_base),
226                                  PCI_DEV_VENDOR(pos->device_info),
227                                  PCI_DEV_DEVID(pos->device_info));
228
229         map = twifs_mapping(pci_dev, pos, "config");
230         map->read = __pci_read_cspace;
231
232         map = twifs_mapping(pci_dev, pos, "revision");
233         map->read = __pci_read_revid;
234
235         map = twifs_mapping(pci_dev, pos, "class");
236         map->read = __pci_read_class;
237
238         map = twifs_mapping(pci_dev, pos, "io_bases");
239         map->read = __pci_bar_read;
240         map->go_next = __pci_bar_gonext;
241     }
242 }
243
244 size_t
245 pci_bar_sizing(struct pci_device* dev, u32_t* bar_out, u32_t bar_num)
246 {
247     pci_reg_t bar = pci_read_cspace(dev->cspace_base, PCI_REG_BAR(bar_num));
248     if (!bar) {
249         *bar_out = 0;
250         return 0;
251     }
252
253     pci_write_cspace(dev->cspace_base, PCI_REG_BAR(bar_num), 0xffffffff);
254     pci_reg_t sized =
255       pci_read_cspace(dev->cspace_base, PCI_REG_BAR(bar_num)) & ~0x1;
256     if (PCI_BAR_MMIO(bar)) {
257         sized = PCI_BAR_ADDR_MM(sized);
258     }
259     *bar_out = bar;
260     pci_write_cspace(dev->cspace_base, PCI_REG_BAR(bar_num), bar);
261     return ~sized + 1;
262 }
263
264 void
265 pci_setup_msi(struct pci_device* device, int vector)
266 {
267     // Dest: APIC#0, Physical Destination, No redirection
268     u32_t msi_addr = (__APIC_BASE_PADDR);
269
270     // Edge trigger, Fixed delivery
271     u32_t msi_data = vector;
272
273     pci_write_cspace(
274       device->cspace_base, PCI_MSI_ADDR(device->msi_loc), msi_addr);
275
276     pci_reg_t reg1 = pci_read_cspace(device->cspace_base, device->msi_loc);
277     pci_reg_t msg_ctl = reg1 >> 16;
278
279     int offset = !!(msg_ctl & MSI_CAP_64BIT) * 4;
280     pci_write_cspace(device->cspace_base,
281                      PCI_MSI_DATA(device->msi_loc, offset),
282                      msi_data & 0xffff);
283
284     if ((msg_ctl & MSI_CAP_MASK)) {
285         pci_write_cspace(
286           device->cspace_base, PCI_MSI_MASK(device->msi_loc, offset), 0);
287     }
288
289     // manipulate the MSI_CTRL to allow device using MSI to request service.
290     reg1 = (reg1 & 0xff8fffff) | 0x10000;
291     pci_write_cspace(device->cspace_base, device->msi_loc, reg1);
292 }
293
294 struct pci_device*
295 pci_get_device_by_id(u16_t vendorId, u16_t deviceId)
296 {
297     u32_t dev_info = vendorId | (deviceId << 16);
298     struct pci_device *pos, *n;
299     llist_for_each(pos, n, &pci_devices, dev_chain)
300     {
301         if (pos->device_info == dev_info) {
302             return pos;
303         }
304     }
305
306     return NULL;
307 }
308
309 struct pci_device*
310 pci_get_device_by_class(u32_t class)
311 {
312     struct pci_device *pos, *n;
313     llist_for_each(pos, n, &pci_devices, dev_chain)
314     {
315         if (PCI_DEV_CLASS(pos->class_info) == class) {
316             return pos;
317         }
318     }
319
320     return NULL;
321 }
322
323 void
324 pci_add_driver(const char* name,
325                u32_t class,
326                u32_t vendor,
327                u32_t devid,
328                pci_drv_init init)
329 {
330     struct pci_driver* pci_drv = valloc(sizeof(*pci_drv));
331     *pci_drv = (struct pci_driver){ .create_driver = init,
332                                     .dev_info = (vendor << 16) | devid,
333                                     .dev_class = class };
334     if (name) {
335         strncpy(pci_drv->name, name, PCI_DRV_NAME_LEN);
336     }
337
338     llist_append(&pci_drivers, &pci_drv->drivers);
339 }
340
341 int
342 pci_bind_driver(struct pci_device* pci_dev)
343 {
344     struct pci_driver *pos, *n;
345     llist_for_each(pos, n, &pci_drivers, drivers)
346     {
347         if (pos->dev_info) {
348             if (pos->dev_info == pci_dev->device_info) {
349                 goto check_type;
350             }
351             continue;
352         }
353     check_type:
354         if (pos->dev_class) {
355             if (pos->dev_class == PCI_DEV_CLASS(pci_dev->class_info)) {
356                 pci_dev->driver.type = pos;
357                 pci_dev->driver.instance = pos->create_driver(pci_dev);
358                 return 1;
359             }
360         }
361     }
362     return 0;
363 }
364
365 void
366 pci_init()
367 {
368     acpi_context* acpi = acpi_get_context();
369     assert_msg(acpi, "ACPI not initialized.");
370     if (acpi->mcfg.alloc_num) {
371         // PCIe Enhanced Configuration Mechanism is supported.
372         // TODO: support PCIe addressing mechanism
373     }
374     // Otherwise, fallback to use legacy PCI 3.0 method.
375     pci_probe();
376
377     pci_build_fsmapping();
378 }