-
Notifications
You must be signed in to change notification settings - Fork 613
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
380 additions
and
19 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -91,5 +91,4 @@ static inline uint arch_curr_cpu_num(void) { | |
#define smp_rmb() CF | ||
#endif | ||
|
||
|
||
#endif // !ASSEMBLY |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
# Fake module that just declares deps on all the PCI drivers in the system. | ||
# | ||
MODULES += dev/bus/pci | ||
|
||
MODULES += dev/net/e1000 | ||
MODULES += dev/virtio/block | ||
MODULES += dev/virtio/net | ||
MODULES += dev/virtio/gpu |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,276 @@ | ||
/* | ||
* Copyright (c) 2024 Travis Geiselbrecht | ||
* | ||
* Use of this source code is governed by a MIT-style | ||
* license that can be found in the LICENSE file or at | ||
* https://opensource.org/licenses/MIT | ||
*/ | ||
#if WITH_DEV_BUS_PCI | ||
|
||
#include <stdlib.h> | ||
#include <dev/virtio.h> | ||
#include <lk/init.h> | ||
#include <lk/err.h> | ||
#include <lk/trace.h> | ||
#include <lk/list.h> | ||
#if WITH_KERNEL_VM | ||
#include <kernel/vm.h> | ||
#endif | ||
|
||
#define LOCAL_TRACE 1 | ||
|
||
#include <dev/bus/pci.h> | ||
|
||
struct virtio_devices { | ||
uint16_t device_id; | ||
bool legacy; | ||
status_t (*init)(pci_location_t loc, const struct virtio_devices *dev, size_t ordinal); | ||
}; | ||
|
||
static status_t init_block(pci_location_t loc, const struct virtio_devices *dev, size_t ordinal); | ||
|
||
const struct virtio_devices devices[] = { | ||
{ 0x1000, true, NULL }, // transitional network | ||
{ 0x1001, true, &init_block }, // transitional block | ||
{ 0x1009, true, NULL }, // legacy virtio 9p | ||
{ 0x1041, false, NULL }, // non-transitional network | ||
{ 0x1042, false, &init_block }, // non-transitional block | ||
{ 0x1043, false, NULL }, // non-transitional console | ||
{ 0x1050, false, NULL }, // non-transitional gpu | ||
{ 0x1052, false, NULL }, // non-transitional input | ||
}; | ||
|
||
struct virtio_pci_cap { | ||
uint8_t cap_vndr; | ||
uint8_t cap_next; | ||
uint8_t cap_len; | ||
/* Common configuration */ | ||
#define VIRTIO_PCI_CAP_COMMON_CFG 1 | ||
/* Notifications */ | ||
#define VIRTIO_PCI_CAP_NOTIFY_CFG 2 | ||
/* ISR Status */ | ||
#define VIRTIO_PCI_CAP_ISR_CFG 3 | ||
/* Device specific configuration */ | ||
#define VIRTIO_PCI_CAP_DEVICE_CFG 4 | ||
/* PCI configuration access */ | ||
#define VIRTIO_PCI_CAP_PCI_CFG 5 | ||
/* Shared memory region */ | ||
#define VIRTIO_PCI_CAP_SHARED_MEMORY_CFG 8 | ||
/* Vendor-specific data */ | ||
#define VIRTIO_PCI_CAP_VENDOR_CFG 9 | ||
uint8_t cfg_type; | ||
uint8_t bar; | ||
uint8_t id; | ||
uint8_t padding[2]; | ||
uint32_t offset; | ||
uint32_t length; | ||
}; | ||
|
||
STATIC_ASSERT(sizeof(struct virtio_pci_cap) == 16); | ||
|
||
static void dump_pci_cap(const struct virtio_pci_cap *cap) { | ||
const char *type; | ||
|
||
switch (cap->cfg_type) { | ||
case VIRTIO_PCI_CAP_COMMON_CFG: type = "common"; break; | ||
case VIRTIO_PCI_CAP_NOTIFY_CFG: type = "notify"; break; | ||
case VIRTIO_PCI_CAP_ISR_CFG: type = "isr"; break; | ||
case VIRTIO_PCI_CAP_DEVICE_CFG: type = "device"; break; | ||
case VIRTIO_PCI_CAP_PCI_CFG: type = "pci"; break; | ||
case VIRTIO_PCI_CAP_SHARED_MEMORY_CFG: type = "shared mem"; break; | ||
case VIRTIO_PCI_CAP_VENDOR_CFG: type = "vendor"; break; | ||
default: type = "unknown"; break; | ||
} | ||
|
||
printf("PCI capability: vendor %#hhx next %#hhx len %#hhx type %#hhx (%s) bar %#hhx id %#hhx offset %#x length %#x\n", | ||
cap->cap_vndr, cap->cap_next, cap->cap_len, cap->cfg_type, type, cap->bar, cap->id, cap->offset, cap->length); | ||
} | ||
|
||
struct virtio_pci_common_cfg { | ||
/* About the whole device. */ | ||
uint32_t device_feature_select; /* read-write */ | ||
uint32_t device_feature; /* read-only for driver */ | ||
uint32_t driver_feature_select; /* read-write */ | ||
uint32_t driver_feature; /* read-write */ | ||
uint16_t config_msix_vector; /* read-write */ | ||
uint16_t num_queues; /* read-only for driver */ | ||
uint8_t device_status; /* read-write */ | ||
uint8_t config_generation; /* read-only for driver */ | ||
|
||
/* About a specific virtqueue. */ | ||
uint16_t queue_select; /* read-write */ | ||
uint16_t queue_size; /* read-write */ | ||
uint16_t queue_msix_vector; /* read-write */ | ||
uint16_t queue_enable; /* read-write */ | ||
uint16_t queue_notify_off; /* read-only for driver */ | ||
uint64_t queue_desc; /* read-write */ | ||
uint64_t queue_driver; /* read-write */ | ||
uint64_t queue_device; /* read-write */ | ||
uint16_t queue_notif_config_data; /* read-only for driver */ | ||
uint16_t queue_reset; /* read-write */ | ||
|
||
/* About the administration virtqueue. */ | ||
uint16_t admin_queue_index; /* read-only for driver */ | ||
uint16_t admin_queue_num; /* read-only for driver */ | ||
}; | ||
|
||
STATIC_ASSERT(sizeof(struct virtio_pci_common_cfg) == 64); | ||
|
||
static void dump_pci_common_config(const volatile struct virtio_pci_common_cfg *cfg) { | ||
printf("PCI common config:\n"); | ||
printf("\tdevice feature select %#x features %#x\n", cfg->device_feature_select, cfg->device_feature); | ||
printf("\tdriver feature select %#x features %#x\n", cfg->driver_feature_select, cfg->driver_feature); | ||
printf("\tmsix vector %#x num queues %#x status %#x config gen %#x\n", cfg->config_msix_vector, cfg->num_queues, | ||
cfg->device_status, cfg->config_generation); | ||
|
||
|
||
} | ||
|
||
struct config_pointer { | ||
bool valid; | ||
int bar; | ||
size_t offset; | ||
size_t length; | ||
}; | ||
|
||
struct mapped_bars { | ||
bool mapped; | ||
void *vaddr; | ||
}; | ||
|
||
struct virtio_pci_device { | ||
pci_location_t loc; | ||
|
||
struct mapped_bars bar_map[6]; | ||
|
||
struct config_pointer common_cfg; | ||
struct config_pointer notify_cfg; | ||
struct config_pointer isr_cfg; | ||
struct config_pointer device_cfg; | ||
struct config_pointer pci_cfg; | ||
}; | ||
|
||
static status_t init_block(pci_location_t loc, const struct virtio_devices *dev, size_t index) { | ||
LTRACE_ENTRY; | ||
|
||
struct virtio_pci_device *pdev = calloc(1, sizeof(struct virtio_pci_device)); | ||
pdev->loc = loc; | ||
|
||
// read all of the capabilities for this virtio device | ||
bool map_bars[6] = {}; | ||
for (size_t i = 0;; i++) { | ||
struct virtio_pci_cap cap; | ||
ssize_t err = pci_read_vendor_capability(loc, i, &cap, sizeof(cap)); | ||
if (err < NO_ERROR || (size_t)err < sizeof(cap)) { | ||
break; | ||
} | ||
if (LOCAL_TRACE) dump_pci_cap(&cap); | ||
|
||
// save off the bar + range of all of the capabilities we care about | ||
struct config_pointer *cfg; | ||
switch (cap.cfg_type) { | ||
case VIRTIO_PCI_CAP_COMMON_CFG: | ||
cfg = &pdev->common_cfg; | ||
goto common; | ||
case VIRTIO_PCI_CAP_NOTIFY_CFG: | ||
cfg = &pdev->notify_cfg; | ||
goto common; | ||
case VIRTIO_PCI_CAP_ISR_CFG: | ||
cfg = &pdev->isr_cfg; | ||
goto common; | ||
case VIRTIO_PCI_CAP_DEVICE_CFG: | ||
cfg = &pdev->device_cfg; | ||
goto common; | ||
case VIRTIO_PCI_CAP_PCI_CFG: | ||
cfg = &pdev->pci_cfg; | ||
// fallthrough | ||
common: | ||
DEBUG_ASSERT(cfg); | ||
cfg->valid = true; | ||
cfg->bar = cap.bar; | ||
cfg->offset = cap.offset; | ||
cfg->length = cap.length; | ||
if (cap.bar < 6) { | ||
map_bars[cap.bar] = true; | ||
} | ||
} | ||
} | ||
|
||
// check that at least the mandatory capabilities are present | ||
if (!pdev->common_cfg.valid) { | ||
return ERR_NOT_FOUND; | ||
} | ||
|
||
// map in the bars we care about | ||
pci_bar_t bars[6]; | ||
status_t err = pci_bus_mgr_read_bars(loc, bars); | ||
if (err != NO_ERROR) { | ||
return err; | ||
} | ||
|
||
LTRACEF("virtio-pci BARS:\n"); | ||
if (LOCAL_TRACE) pci_dump_bars(bars, 6); | ||
|
||
for (int i = 0; i < 6; i++) { | ||
if (map_bars[i] && !bars[i].io) { | ||
#if WITH_KERNEL_VM | ||
char str[32]; | ||
snprintf(str, sizeof(str), "virtio%zu bar%d", index, i); | ||
err = vmm_alloc_physical(vmm_get_kernel_aspace(), str, bars[i].size, &pdev->bar_map[i].vaddr, 0, | ||
bars[i].addr, /* vmm_flags */ 0, ARCH_MMU_FLAG_UNCACHED_DEVICE); | ||
if (err != NO_ERROR) { | ||
printf("error mapping bar %d\n", i); | ||
continue; | ||
} | ||
pdev->bar_map[i].mapped = true; | ||
printf("bar %d mapped at %p\n", i, pdev->bar_map[i].vaddr); | ||
#else | ||
// no need to map, it's already available at the physical address | ||
if (sizeof(void *) < 8 && (bars[i].addr + bars[i].size) > UINT32_MAX) { | ||
TRACEF("aborting due to 64bit BAR on 32bit arch\n"); | ||
return ERR_NO_MEMORY; | ||
} | ||
pdev->bar_map[i].vaddr = (void *)(uintptr_t)bars[i].addr; | ||
#endif | ||
} | ||
} | ||
|
||
// enable the device | ||
pci_bus_mgr_enable_device(loc); | ||
|
||
// look at the common configuration | ||
volatile struct virtio_pci_common_cfg *ccfg; | ||
ccfg = pdev->bar_map[pdev->common_cfg.bar].vaddr + pdev->common_cfg.offset; | ||
if (LOCAL_TRACE) dump_pci_common_config(ccfg); | ||
|
||
return NO_ERROR; | ||
} | ||
|
||
static void virtio_pci_init(uint level) { | ||
LTRACE_ENTRY; | ||
|
||
for (size_t j = 0; j < countof(devices); j++) { | ||
for (size_t i = 0; ; i++) { | ||
pci_location_t loc; | ||
status_t err = pci_bus_mgr_find_device(&loc, devices[j].device_id, 0x1af4, i); | ||
if (err != NO_ERROR) { | ||
break; | ||
} | ||
|
||
char str[14]; | ||
LTRACEF("virtio-pci: looking at device at %s\n", pci_loc_string(loc, str)); | ||
|
||
// call the init routine | ||
if (devices[j].init) { | ||
devices[j].init(loc, &devices[j], i); | ||
} | ||
} | ||
} | ||
|
||
LTRACE_EXIT; | ||
} | ||
|
||
LK_INIT_HOOK(virtio_pci, &virtio_pci_init, LK_INIT_LEVEL_PLATFORM + 1); | ||
|
||
#endif | ||
|
Oops, something went wrong.