1/**
2 * \file
3 * \brief PCI configuration space access
4 */
5
6/*
7 * Copyright (c) 2008, 2009, ETH Zurich.
8 * All rights reserved.
9 *
10 * This file is distributed under the terms in the attached LICENSE file.
11 * If you do not find this file, copies can be found by writing to:
12 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
13 */
14
15#include <barrelfish/barrelfish.h>
16#include <stdio.h>
17#include <stdlib.h>
18#include <acpi_client/acpi_client.h>
19#include <pci/confspace/pci_confspace.h>
20
21static uint8_t startbus, endbus;
22static struct memobj_one_frame_lazy *memobj = NULL;
23static struct vregion *vregion = NULL;
24static struct capref region_cap;
25
26/// Enable PCIe. Default is yes.
27static bool pcie_enabled = true;
28
29/* FIXME: XXX: super-hacky bitfield to track if we already mapped something */
30static uint8_t *mapped_bitfield;
31
32int pcie_confspace_init(struct capref ram_cap, lpaddr_t base, uint16_t segment, uint8_t startbusarg,
33                        uint8_t endbusarg)
34{
35    errval_t err;
36
37    assert(segment == 0); // unhandled!
38
39    startbus = startbusarg;
40    endbus = endbusarg;
41
42    // compute size of region
43    size_t region_pages = (endbus + 1 - startbus) << 8;
44    size_t region_bytes = region_pages * BASE_PAGE_SIZE;
45
46    region_cap = ram_cap;
47
48    memobj = malloc(sizeof(struct memobj_one_frame_lazy));
49    assert(memobj);
50    vregion = malloc(sizeof(struct vregion));
51    assert(vregion);
52    err = memobj_create_one_frame_lazy(memobj, region_bytes, 0, region_cap,
53                                       BASE_PAGE_SIZE);
54    if (err_is_fail(err)) {
55        DEBUG_ERR(err, "memobj_create_one_frame_lazy failed");
56        return -1;
57    }
58    err = vregion_map(vregion, get_current_vspace(), &memobj->m, 0, region_bytes,
59                      VREGION_FLAGS_READ_WRITE_NOCACHE);
60    if (err_is_fail(err)) {
61        DEBUG_ERR(err, "vregion_map failed");
62        return -1;
63    }
64
65    /* allocate space for bitfield */
66    mapped_bitfield = calloc(region_pages / NBBY, 1);
67    assert(mapped_bitfield != NULL);
68
69    return 0;
70}
71
72static bool is_mapped(uint64_t page)
73{
74    return mapped_bitfield[page / NBBY] & (1 << page % NBBY);
75}
76
77static void set_mapped(uint64_t page)
78{
79    mapped_bitfield[page / NBBY] |= (1 << page % NBBY);
80}
81
82static lvaddr_t map_page(uint64_t page)
83{
84    errval_t err;
85
86    if (!is_mapped(page)) {
87        err = memobj->m.f.pagefault(&memobj->m, vregion, page * BASE_PAGE_SIZE, 0);
88        if (err_is_fail(err)) {
89            DEBUG_ERR(err, "memobj->f.pagefault failed");
90            return 0;
91        }
92        set_mapped(page);
93    }
94
95    genvaddr_t genvaddr = vregion_get_base_addr(vregion);
96    return genvaddr + page * BASE_PAGE_SIZE;
97}
98
99lvaddr_t pcie_confspace_access(struct pci_address addr)
100{
101    if (vregion == NULL || !pcie_enabled || addr.bus < startbus
102        || addr.bus > endbus) {
103        return 0;
104    }
105
106    uint64_t page = (addr.bus << 8) | (addr.device << 3) | addr.function;
107    return map_page(page);
108}
109
110uint8_t pcie_get_endbus(void)
111{
112    return endbus;
113}
114
115void pcie_enable(void)
116{
117    pcie_enabled = true;
118}
119
120void pcie_disable(void)
121{
122    pcie_enabled = false;
123}
124