vfs: check userland buffers before reading them.
[haiku.git] / src / add-ons / kernel / bus_managers / pci / arch / x86 / pci_msi.cpp
blob53dac7295402b616de3fc6f04a769fd3ee19b34f
1 /*
2 * Copyright 2013, Jérôme Duval, korli@users.berlios.de.
3 * Copyright 2010, Michael Lotz, mmlr@mlotz.ch. All Rights Reserved.
4 * Distributed under the terms of the MIT License.
5 */
7 #include "pci_msi.h"
8 #include "pci_arch_info.h"
9 #include "pci.h"
10 #include "pci_private.h"
12 #include <strings.h>
14 #include <arch/x86/msi.h>
15 #include <debug.h>
17 extern PCI *gPCI;
20 static status_t pci_unconfigure_msix(PCIDev *device);
21 static status_t pci_disable_msix(PCIDev *device);
24 static void
25 pci_ht_msi_map(PCIDev *device, uint64 address)
27 ht_mapping_info *info = &device->arch_info.ht_mapping;
28 if (!info->ht_mapping_capable)
29 return;
31 bool enabled = (info->control_value & PCI_ht_command_msi_enable) != 0;
32 if ((address != 0) != enabled) {
33 if (enabled) {
34 info->control_value &= ~PCI_ht_command_msi_enable;
35 } else {
36 if ((address >> 20) != (info->address_value >> 20))
37 return;
38 dprintf("ht msi mapping enabled\n");
39 info->control_value |= PCI_ht_command_msi_enable;
41 gPCI->WriteConfig(device, info->capability_offset + PCI_ht_command, 2,
42 info->control_value);
47 void
48 pci_read_ht_mapping_info(PCIDev *device)
50 if (!msi_supported())
51 return;
53 ht_mapping_info *info = &device->arch_info.ht_mapping;
54 info->ht_mapping_capable = false;
56 uint8 offset = 0;
57 if (gPCI->FindHTCapability(device, PCI_ht_command_cap_msi_mapping,
58 &offset) == B_OK) {
59 info->control_value = gPCI->ReadConfig(device, offset + PCI_ht_command,
60 2);
61 info->capability_offset = offset;
62 info->ht_mapping_capable = true;
63 if ((info->control_value & PCI_ht_command_msi_fixed) != 0)
64 info->address_value = MSI_ADDRESS_BASE;
65 else {
66 info->address_value = gPCI->ReadConfig(device, offset
67 + PCI_ht_msi_address_high, 4);
68 info->address_value <<= 32;
69 info->address_value |= gPCI->ReadConfig(device, offset
70 + PCI_ht_msi_address_low, 4);
72 dprintf("found an ht msi mapping at %#" B_PRIx64 "\n",
73 info->address_value);
78 uint8
79 pci_get_msi_count(uint8 virtualBus, uint8 _device, uint8 function)
81 if (!msi_supported())
82 return 0;
84 uint8 bus;
85 uint8 domain;
86 if (gPCI->ResolveVirtualBus(virtualBus, &domain, &bus) != B_OK)
87 return 0;
89 PCIDev *device = gPCI->FindDevice(domain, bus, _device, function);
90 if (device == NULL)
91 return 0;
93 msi_info *info = &device->arch_info.msi;
94 if (!info->msi_capable)
95 return 0;
97 return info->message_count;
101 status_t
102 pci_configure_msi(uint8 virtualBus, uint8 _device, uint8 function,
103 uint8 count, uint8 *startVector)
105 if (!msi_supported())
106 return B_UNSUPPORTED;
108 if (count == 0 || startVector == NULL)
109 return B_BAD_VALUE;
111 uint8 bus;
112 uint8 domain;
113 status_t result = gPCI->ResolveVirtualBus(virtualBus, &domain, &bus);
114 if (result != B_OK)
115 return result;
117 PCIDev *device = gPCI->FindDevice(domain, bus, _device, function);
118 if (device == NULL)
119 return B_ERROR;
121 msi_info *info = &device->arch_info.msi;
122 if (!info->msi_capable)
123 return B_UNSUPPORTED;
125 if (count > 32 || count > info->message_count
126 || ((count - 1) & count) != 0 /* needs to be a power of 2 */) {
127 return B_BAD_VALUE;
130 if (info->configured_count != 0)
131 return B_BUSY;
133 result = msi_allocate_vectors(count, &info->start_vector,
134 &info->address_value, &info->data_value);
135 if (result != B_OK)
136 return result;
138 uint8 offset = info->capability_offset;
139 gPCI->WriteConfig(device, offset + PCI_msi_address, 4,
140 info->address_value & 0xffffffff);
141 if (info->control_value & PCI_msi_control_64bit) {
142 gPCI->WriteConfig(device, offset + PCI_msi_address_high, 4,
143 info->address_value >> 32);
144 gPCI->WriteConfig(device, offset + PCI_msi_data_64bit, 2,
145 info->data_value);
146 } else
147 gPCI->WriteConfig(device, offset + PCI_msi_data, 2, info->data_value);
149 info->control_value &= ~PCI_msi_control_mme_mask;
150 info->control_value |= (ffs(count) - 1) << 4;
151 gPCI->WriteConfig(device, offset + PCI_msi_control, 2, info->control_value);
153 info->configured_count = count;
154 *startVector = info->start_vector;
155 return B_OK;
159 status_t
160 pci_unconfigure_msi(uint8 virtualBus, uint8 _device, uint8 function)
162 if (!msi_supported())
163 return B_UNSUPPORTED;
165 uint8 bus;
166 uint8 domain;
167 status_t result = gPCI->ResolveVirtualBus(virtualBus, &domain, &bus);
168 if (result != B_OK)
169 return result;
171 PCIDev *device = gPCI->FindDevice(domain, bus, _device, function);
172 if (device == NULL)
173 return B_ERROR;
175 // try MSI-X
176 result = pci_unconfigure_msix(device);
177 if (result != B_UNSUPPORTED && result != B_NO_INIT)
178 return result;
180 msi_info *info = &device->arch_info.msi;
181 if (!info->msi_capable)
182 return B_UNSUPPORTED;
184 if (info->configured_count == 0)
185 return B_NO_INIT;
187 msi_free_vectors(info->configured_count, info->start_vector);
189 info->control_value &= ~PCI_msi_control_mme_mask;
190 gPCI->WriteConfig(device, info->capability_offset + PCI_msi_control, 2,
191 info->control_value);
193 info->configured_count = 0;
194 info->address_value = 0;
195 info->data_value = 0;
196 return B_OK;
200 status_t
201 pci_enable_msi(uint8 virtualBus, uint8 _device, uint8 function)
203 if (!msi_supported())
204 return B_UNSUPPORTED;
206 uint8 bus;
207 uint8 domain;
208 status_t result = gPCI->ResolveVirtualBus(virtualBus, &domain, &bus);
209 if (result != B_OK)
210 return result;
212 PCIDev *device = gPCI->FindDevice(domain, bus, _device, function);
213 if (device == NULL)
214 return B_ERROR;
216 msi_info *info = &device->arch_info.msi;
217 if (!info->msi_capable)
218 return B_UNSUPPORTED;
220 if (info->configured_count == 0)
221 return B_NO_INIT;
223 // ensure the pinned interrupt is disabled
224 gPCI->WriteConfig(device, PCI_command, 2,
225 gPCI->ReadConfig(device, PCI_command, 2) | PCI_command_int_disable);
227 // enable msi generation
228 info->control_value |= PCI_msi_control_enable;
229 gPCI->WriteConfig(device, info->capability_offset + PCI_msi_control, 2,
230 info->control_value);
232 // enable HT msi mapping (if applicable)
233 pci_ht_msi_map(device, info->address_value);
235 dprintf("msi enabled: 0x%04" B_PRIx32 "\n",
236 gPCI->ReadConfig(device, info->capability_offset + PCI_msi_control, 2));
237 return B_OK;
241 status_t
242 pci_disable_msi(uint8 virtualBus, uint8 _device, uint8 function)
244 if (!msi_supported())
245 return B_UNSUPPORTED;
247 uint8 bus;
248 uint8 domain;
249 status_t result = gPCI->ResolveVirtualBus(virtualBus, &domain, &bus);
250 if (result != B_OK)
251 return result;
253 PCIDev *device = gPCI->FindDevice(domain, bus, _device, function);
254 if (device == NULL)
255 return B_ERROR;
257 // try MSI-X
258 result = pci_disable_msix(device);
259 if (result != B_UNSUPPORTED && result != B_NO_INIT)
260 return result;
262 msi_info *info = &device->arch_info.msi;
263 if (!info->msi_capable)
264 return B_UNSUPPORTED;
266 if (info->configured_count == 0)
267 return B_NO_INIT;
269 // disable HT msi mapping (if applicable)
270 pci_ht_msi_map(device, 0);
272 // disable msi generation
273 info->control_value &= ~PCI_msi_control_enable;
274 gPCI->WriteConfig(device, info->capability_offset + PCI_msi_control, 2,
275 info->control_value);
277 return B_OK;
281 void
282 pci_read_msi_info(PCIDev *device)
284 if (!msi_supported())
285 return;
287 msi_info *info = &device->arch_info.msi;
288 info->msi_capable = false;
289 status_t result = gPCI->FindCapability(device->domain, device->bus,
290 device->device, device->function, PCI_cap_id_msi,
291 &info->capability_offset);
292 if (result != B_OK)
293 return;
295 info->msi_capable = true;
296 info->control_value = gPCI->ReadConfig(device->domain, device->bus,
297 device->device, device->function,
298 info->capability_offset + PCI_msi_control, 2);
299 info->message_count
300 = 1 << ((info->control_value & PCI_msi_control_mmc_mask) >> 1);
301 info->configured_count = 0;
302 info->data_value = 0;
303 info->address_value = 0;
307 uint8
308 pci_get_msix_count(uint8 virtualBus, uint8 _device, uint8 function)
310 if (!msi_supported())
311 return 0;
313 uint8 bus;
314 uint8 domain;
315 if (gPCI->ResolveVirtualBus(virtualBus, &domain, &bus) != B_OK)
316 return 0;
318 PCIDev *device = gPCI->FindDevice(domain, bus, _device, function);
319 if (device == NULL)
320 return 0;
322 msix_info *info = &device->arch_info.msix;
323 if (!info->msix_capable)
324 return 0;
326 return info->message_count;
330 status_t
331 pci_configure_msix(uint8 virtualBus, uint8 _device, uint8 function,
332 uint8 count, uint8 *startVector)
334 if (!msi_supported())
335 return B_UNSUPPORTED;
337 if (count == 0 || startVector == NULL)
338 return B_BAD_VALUE;
340 uint8 bus;
341 uint8 domain;
342 status_t result = gPCI->ResolveVirtualBus(virtualBus, &domain, &bus);
343 if (result != B_OK)
344 return result;
346 PCIDev *device = gPCI->FindDevice(domain, bus, _device, function);
347 if (device == NULL)
348 return B_ERROR;
350 msix_info *info = &device->arch_info.msix;
351 if (!info->msix_capable)
352 return B_UNSUPPORTED;
354 if (count > 32 || count > info->message_count) {
355 return B_BAD_VALUE;
358 if (info->configured_count != 0)
359 return B_BUSY;
361 // map the table bar
362 size_t tableSize = info->message_count * 16;
363 addr_t address;
364 area_id area = map_physical_memory("msi table map",
365 device->info.u.h0.base_registers[info->table_bar],
366 tableSize + info->table_offset,
367 B_ANY_KERNEL_ADDRESS, B_READ_AREA | B_WRITE_AREA, (void**)&address);
368 if (area < 0)
369 return area;
370 info->table_area_id = area;
371 info->table_address = address + info->table_offset;
373 // and the pba bar if necessary
374 if (info->table_bar != info->pba_bar) {
375 area = map_physical_memory("msi pba map",
376 device->info.u.h0.base_registers[info->pba_bar],
377 tableSize + info->pba_offset,
378 B_ANY_KERNEL_ADDRESS, B_READ_AREA | B_WRITE_AREA,
379 (void**)&address);
380 if (area < 0) {
381 delete_area(info->table_area_id);
382 info->table_area_id = -1;
383 return area;
385 info->pba_area_id = area;
386 } else
387 info->pba_area_id = -1;
388 info->pba_address = address + info->pba_offset;
390 result = msi_allocate_vectors(count, &info->start_vector,
391 &info->address_value, &info->data_value);
392 if (result != B_OK) {
393 delete_area(info->pba_area_id);
394 delete_area(info->table_area_id);
395 info->pba_area_id = -1;
396 info->table_area_id = -1;
397 return result;
400 // ensure the memory i/o is enabled
401 gPCI->WriteConfig(device, PCI_command, 2,
402 gPCI->ReadConfig(device, PCI_command, 2) | PCI_command_memory);
404 uint32 data_value = info->data_value;
405 for (uint32 index = 0; index < count; index++) {
406 volatile uint32 *entry = (uint32*)(info->table_address + 16 * index);
407 *(entry + 3) |= PCI_msix_vctrl_mask;
408 *entry++ = info->address_value & 0xffffffff;
409 *entry++ = info->address_value >> 32;
410 *entry++ = data_value++;
411 *entry &= ~PCI_msix_vctrl_mask;
414 info->configured_count = count;
415 *startVector = info->start_vector;
416 dprintf("msix configured for %d vectors\n", count);
417 return B_OK;
421 static status_t
422 pci_unconfigure_msix(PCIDev *device)
424 msix_info *info = &device->arch_info.msix;
425 if (!info->msix_capable)
426 return B_UNSUPPORTED;
428 if (info->configured_count == 0)
429 return B_NO_INIT;
431 // disable msi-x generation
432 info->control_value &= ~PCI_msix_control_enable;
433 gPCI->WriteConfig(device, info->capability_offset + PCI_msix_control, 2,
434 info->control_value);
436 msi_free_vectors(info->configured_count, info->start_vector);
437 for (uint8 index = 0; index < info->configured_count; index++) {
438 volatile uint32 *entry = (uint32*)(info->table_address + 16 * index);
439 if ((*(entry + 3) & PCI_msix_vctrl_mask) == 0)
440 *(entry + 3) |= PCI_msix_vctrl_mask;
443 if (info->pba_area_id != -1)
444 delete_area(info->pba_area_id);
445 if (info->table_area_id != -1)
446 delete_area(info->table_area_id);
447 info->pba_area_id= -1;
448 info->table_area_id = -1;
450 info->configured_count = 0;
451 info->address_value = 0;
452 info->data_value = 0;
453 return B_OK;
457 status_t
458 pci_enable_msix(uint8 virtualBus, uint8 _device, uint8 function)
460 if (!msi_supported())
461 return B_UNSUPPORTED;
463 uint8 bus;
464 uint8 domain;
465 status_t result = gPCI->ResolveVirtualBus(virtualBus, &domain, &bus);
466 if (result != B_OK)
467 return result;
469 PCIDev *device = gPCI->FindDevice(domain, bus, _device, function);
470 if (device == NULL)
471 return B_ERROR;
473 msix_info *info = &device->arch_info.msix;
474 if (!info->msix_capable)
475 return B_UNSUPPORTED;
477 if (info->configured_count == 0)
478 return B_NO_INIT;
480 // ensure the pinned interrupt is disabled
481 gPCI->WriteConfig(device, PCI_command, 2,
482 gPCI->ReadConfig(device, PCI_command, 2) | PCI_command_int_disable);
484 // enable msi-x generation
485 info->control_value |= PCI_msix_control_enable;
486 gPCI->WriteConfig(device, info->capability_offset + PCI_msix_control, 2,
487 info->control_value);
489 // enable HT msi mapping (if applicable)
490 pci_ht_msi_map(device, info->address_value);
492 dprintf("msi-x enabled: 0x%04" B_PRIx32 "\n",
493 gPCI->ReadConfig(device, info->capability_offset + PCI_msix_control, 2));
494 return B_OK;
498 status_t
499 pci_disable_msix(PCIDev *device)
501 msix_info *info = &device->arch_info.msix;
502 if (!info->msix_capable)
503 return B_UNSUPPORTED;
505 if (info->configured_count == 0)
506 return B_NO_INIT;
508 // disable HT msi mapping (if applicable)
509 pci_ht_msi_map(device, 0);
511 // disable msi-x generation
512 info->control_value &= ~PCI_msix_control_enable;
513 gPCI->WriteConfig(device, info->capability_offset + PCI_msix_control, 2,
514 info->control_value);
516 return B_OK;
520 void
521 pci_read_msix_info(PCIDev *device)
523 if (!msi_supported())
524 return;
526 msix_info *info = &device->arch_info.msix;
527 info->msix_capable = false;
528 status_t result = gPCI->FindCapability(device->domain, device->bus,
529 device->device, device->function, PCI_cap_id_msix,
530 &info->capability_offset);
531 if (result != B_OK)
532 return;
534 info->msix_capable = true;
535 info->control_value = gPCI->ReadConfig(device->domain, device->bus,
536 device->device, device->function,
537 info->capability_offset + PCI_msix_control, 2);
538 info->message_count
539 = (info->control_value & PCI_msix_control_table_size) + 1;
540 info->configured_count = 0;
541 info->data_value = 0;
542 info->address_value = 0;
543 info->table_area_id = -1;
544 info->pba_area_id = -1;
545 uint32 table_value = gPCI->ReadConfig(device->domain, device->bus,
546 device->device, device->function,
547 info->capability_offset + PCI_msix_table, 4);
548 uint32 pba_value = gPCI->ReadConfig(device->domain, device->bus,
549 device->device, device->function,
550 info->capability_offset + PCI_msix_pba, 4);
552 info->table_bar = table_value & PCI_msix_bir_mask;
553 info->table_offset = table_value & PCI_msix_offset_mask;
554 info->pba_bar = pba_value & PCI_msix_bir_mask;
555 info->pba_offset = pba_value & PCI_msix_offset_mask;