1 /*
2 * Copyright (c) 2009 Corey Tabaka
3 * Copyright (c) 2020 Travis Geiselbrecht
4 *
5 * Use of this source code is governed by a MIT-style
6 * license that can be found in the LICENSE file or at
7 * https://opensource.org/licenses/MIT
8 */
9 #include "ecam.h"
10
11 #include <lk/debug.h>
12 #include <lk/err.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <kernel/thread.h>
16 #include <kernel/spinlock.h>
17 #include <dev/bus/pci.h>
18 #include <lk/trace.h>
19
20 #if WITH_KERNEL_VM
21 #include <kernel/vm.h>
22 #endif
23
24 #include "../pci_priv.h"
25
26 #define LOCAL_TRACE 0
27
pci_ecam(paddr_t base,uint16_t segment,uint8_t start_bus,uint8_t end_bus)28 pci_ecam::pci_ecam(paddr_t base, uint16_t segment, uint8_t start_bus, uint8_t end_bus) :
29 base_(base), segment_(segment), start_bus_(start_bus), end_bus_(end_bus) {}
30
~pci_ecam()31 pci_ecam::~pci_ecam() {
32 LTRACE_ENTRY;
33 #if WITH_KERNEL_VM
34 if (ecam_ptr_) {
35 vmm_free_region(vmm_get_kernel_aspace(), (vaddr_t)ecam_ptr_);
36 }
37 #endif
38 }
39
detect(paddr_t base,uint16_t segment,uint8_t start_bus,uint8_t end_bus)40 pci_ecam *pci_ecam::detect(paddr_t base, uint16_t segment, uint8_t start_bus, uint8_t end_bus) {
41 LTRACEF("base %#lx, segment %hu, bus [%hhu...%hhu]\n", base, segment, start_bus, end_bus);
42
43 // we only support a limited configuration at the moment
44 if (segment != 0 || start_bus != 0) {
45 return nullptr;
46 }
47
48 auto ecam = new pci_ecam(base, segment, start_bus, end_bus);
49
50 // initialize the object, which may fail
51 status_t err = ecam->initialize();
52 if (err != NO_ERROR) {
53 delete ecam;
54 return nullptr;
55 }
56
57 return ecam;
58 }
59
initialize()60 status_t pci_ecam::initialize() {
61 // compute the aperture size of this
62 size_t size = ((size_t)end_bus_ - (size_t)start_bus_ + 1) << 20; // each bus occupies 20 bits of address space
63 LTRACEF("aperture size %#zx\n", size);
64
65
66 #if WITH_KERNEL_VM
67 // try to map the aperture
68 // ask for 4MB aligned regions (log2 22) to help with the mmu on most architectures
69 //status_t vmm_alloc_physical(vmm_aspace_t *aspace, const char *name, size_t size, void **ptr, uint8_t align_log2, paddr_t paddr, uint vmm_flags, uint arch_mmu_flags)
70 status_t err = vmm_alloc_physical(vmm_get_kernel_aspace(), "pci_ecam", size, (void **)&ecam_ptr_, 22, base_, 0, ARCH_MMU_FLAG_UNCACHED_DEVICE);
71 LTRACEF("vmm_alloc_physical returns %d, ptr %p\n", err, ecam_ptr_);
72
73 if (err != NO_ERROR) {
74 ecam_ptr_ = nullptr;
75 return err;
76 }
77 #else
78 // no vm, so can directly access the aperture
79 ecam_ptr_ = (uint8_t *)base_;
80 #endif
81
82 set_last_bus(end_bus_);
83
84 return NO_ERROR;
85 }
86
87 // compute the offset into the ecam given the location and register offset
location_to_offset(const pci_location_t state,uint32_t reg)88 inline size_t location_to_offset(const pci_location_t state, uint32_t reg) {
89 //
90 // | 27 - 20 | 19 - 15 | 14 - 12 | 11 - 8 | 7 - 2 | 1 - 0 |
91 // | Bus Nr | Dev Nr | Function Nr | Ext. Register Nr | Register Nr | Byte Enable |
92
93 // TODO: clamp or assert on invalid offset
94 size_t offset = (size_t)state.bus << 20;
95 offset += (size_t)state.dev << 15;
96 offset += (size_t)state.fn << 12;
97 offset += reg;
98 return offset;
99 }
100
101 // templatized routines to access the pci config space using a specific type
102 template <typename T>
read_config(const pci_location_t state,uint32_t reg,T * value,const uint8_t * ecam_ptr)103 inline int read_config(const pci_location_t state, uint32_t reg, T *value, const uint8_t *ecam_ptr) {
104 auto off = location_to_offset(state, reg);
105
106 *value = *reinterpret_cast<const volatile T *>(&ecam_ptr[off]);
107
108 return NO_ERROR;
109 }
110
111 template <typename T>
write_config(const pci_location_t state,uint32_t reg,T value,uint8_t * ecam_ptr)112 inline int write_config(const pci_location_t state, uint32_t reg, T value, uint8_t *ecam_ptr) {
113 auto off = location_to_offset(state, reg);
114
115 *reinterpret_cast<volatile T *>(&ecam_ptr[off]) = value;
116
117 return NO_ERROR;
118 }
119
read_config_byte(const pci_location_t state,uint32_t reg,uint8_t * value)120 int pci_ecam::read_config_byte(const pci_location_t state, uint32_t reg, uint8_t *value) {
121 LTRACEF_LEVEL(2, "state bus %#hhx dev %#hhx %#hhx reg %#x\n", state.bus, state.dev, state.fn, reg);
122 return read_config(state, reg, value, ecam_ptr_);
123 }
124
read_config_half(const pci_location_t state,uint32_t reg,uint16_t * value)125 int pci_ecam::read_config_half(const pci_location_t state, uint32_t reg, uint16_t *value) {
126 LTRACEF_LEVEL(2, "state bus %#hhx dev %#hhx %#hhx reg %#x\n", state.bus, state.dev, state.fn, reg);
127 return read_config(state, reg, value, ecam_ptr_);
128 }
129
read_config_word(const pci_location_t state,uint32_t reg,uint32_t * value)130 int pci_ecam::read_config_word(const pci_location_t state, uint32_t reg, uint32_t *value) {
131 LTRACEF_LEVEL(2, "state bus %#hhx dev %#hhx %#hhx reg %#x\n", state.bus, state.dev, state.fn, reg);
132 return read_config(state, reg, value, ecam_ptr_);
133 }
134
write_config_byte(const pci_location_t state,uint32_t reg,uint8_t value)135 int pci_ecam::write_config_byte(const pci_location_t state, uint32_t reg, uint8_t value) {
136 LTRACEF_LEVEL(2, "state bus %#hhx dev %#hhx %#hhx reg %#x\n", state.bus, state.dev, state.fn, reg);
137 return write_config(state, reg, value, ecam_ptr_);
138 }
139
write_config_half(const pci_location_t state,uint32_t reg,uint16_t value)140 int pci_ecam::write_config_half(const pci_location_t state, uint32_t reg, uint16_t value) {
141 LTRACEF_LEVEL(2, "state bus %#hhx dev %#hhx %#hhx reg %#x\n", state.bus, state.dev, state.fn, reg);
142 return write_config(state, reg, value, ecam_ptr_);
143 }
144
write_config_word(const pci_location_t state,uint32_t reg,uint32_t value)145 int pci_ecam::write_config_word(const pci_location_t state, uint32_t reg, uint32_t value) {
146 LTRACEF_LEVEL(2, "state bus %#hhx dev %#hhx %#hhx reg %#x\n", state.bus, state.dev, state.fn, reg);
147 return write_config(state, reg, value, ecam_ptr_);
148 }
149