00001 #if !defined(__PCI_DEVICE_BASE_HPP__)
00002 #define __PCI_DEVICE_BASE_HPP__
00003
00004
00005
00006
00007 #include "core/machine/devices/device_base.hpp"
00008 #include "pci_config_space.hpp"
00009 #include "pci_device.hpp"
00010 #include "pci_devfn.hpp"
00011
00055 template <typename SpaceT = pci_config_space>
00056 struct pci_device_base : public device_base, public pci_device
00057 {
00061 typedef SpaceT config_space_type;
00062
00063 static_assert((is_convertible<config_space_type &, pci_config_header64 &>::conforms),
00064 "SpaceT must be a descendant class of pci_config_header64");
00065
00066 static const bool VERBOSE_ALLOCATION = false;
00067
00071 static const uint8_t NUM_IOREGIONS = pci_config_header64::NUM_BASE_ADDRESSES + 1;
00072
00076 static const uint8_t EXPANSION_ROM_SLOT = NUM_IOREGIONS - 1;
00077
00078 protected:
00082 config_space_type config_space;
00083
00087 pci_ioregion ioregions[NUM_IOREGIONS];
00088
00089 public:
00090 inline pci_device_base(machine_base &machine, const string &name)
00091 : device_base(machine, name)
00092 {}
00093
00094
00095
00096
00097
00098
00099 virtual inline const char *name(void) const
00100 {
00101 return device_base::name();
00102 }
00103
00104 virtual inline void irq_moved(l4_irq_t from, l4_irq_t to)
00105 {
00106 device_base::irq_moved(from, to);
00107 config_space.interrupt_line=to;
00108 }
00109
00110
00111
00112
00113
00114 virtual l4_hva_t map_mmio_region(int flags, l4_gpa_t base, l4_gpa_t size=1);
00115 virtual int unmap_mmio_region(l4_gpa_t base, l4_gpa_t size=1);
00116
00117
00118
00119
00120 virtual int reset(void);
00121
00122 virtual inline const pci_ioregion *get_ioregion(const uint8_t region_num) const
00123 {
00124 return is_valid_ioregion(region_num) ? &ioregions[region_num] : nullptr;
00125 }
00126
00127 virtual const search_result search_ioregion(enum pci_ioregion::type type,
00128 l4_gpa_t base, l4_gpa_t size=1) const;
00129
00130 virtual inline const pci_config_header64 &get_config_space(void) const
00131 {
00132 return config_space;
00133 }
00134
00135 virtual inline l4_umword_t read_config_space(pci_config_header16::offset_t offset,
00136 access_size access_size)
00137 {
00138 return config_space.read_raw(offset, access_size);
00139 }
00140
00141 virtual inline int write_config_space(pci_config_header16::offset_t offset,
00142 l4_umword_t data, access_size access_size);
00143
00144 protected:
00145
00146
00147
00148
00149 virtual inline int use_irq(irq_manager::irq_flags use=irq_manager::VIRTUAL,
00150 l4_irq_t requested_irq=irq_manager::NO_IRQ,
00151 l4_irq_t physical_irq=irq_manager::NO_IRQ,
00152 bool verbose=VERBOSE_ALLOCATION)
00153 {
00154 const int ret=device_base::use_irq(use, requested_irq, physical_irq, verbose);
00155 config_space.interrupt_line=(ret >= 0) ? ret : 0;
00156 config_space.interrupt_pin=(ret >= 0) ? pci_config_header64::PIN_A : pci_config_header64::PIN_NONE;
00157 return ret;
00158 }
00159
00160 virtual inline int free_irq(bool verbose=VERBOSE_ALLOCATION)
00161 {
00162 const int ret=device_base::free_irq(verbose);
00163 config_space.interrupt_line=0;
00164 config_space.interrupt_pin=pci_config_header64::PIN_NONE;
00165 return ret;
00166 }
00167
00168
00169
00170
00175 virtual int use_ioregion(enum pci_ioregion::type type,
00176 l4_gpa_t size, l4_hva_t virtual_base=0);
00177
00182 virtual int use_ioregion(uint8_t region_num, enum pci_ioregion::type type,
00183 l4_gpa_t size, l4_hva_t virtual_base=0);
00184
00191 virtual int ioregion_relocated(uint8_t region_num, l4_gpa_t old_base);
00192
00193 private:
00194
00195
00196
00197 int write_address_register(pci_config_header16::offset_t offset, l4_gpa_t data,
00198 access_size access_size);
00199
00200 public:
00205 static inline bool is_valid_ioregion(const uint8_t region_num)
00206 {
00207 return region_num < NUM_IOREGIONS;
00208 }
00209
00214 static inline uint8_t offset2region_num(const pci_config_header16::offset_t offset)
00215 {
00216 return pci_config_header64::is_base_address(offset) ? (offset-0x10) >> 2 : EXPANSION_ROM_SLOT;
00217 }
00218 };
00219
00220
00221
00222
00223 template <typename SpaceT>
00224 l4_hva_t pci_device_base<SpaceT>::map_mmio_region(int flags, l4_gpa_t base, l4_gpa_t size)
00225 {
00226
00227
00228
00229 search_result result=search_ioregion(pci_ioregion::IOMEMORY, base, size);
00230 assert_logd(L4VMM_DEBUG_PCI, result.is_valid(), "addresses "l4_gpa_fmt"-"l4_gpa_fmt"\n",
00231 base, base + size - 1);
00232
00233 pci_ioregion ®ion=ioregions[result.region_num];
00234 assert_logd(L4VMM_DEBUG_PCI, region.is_iomemory(), "region_num: %d, type: 0x%08x\n",
00235 result.region_num, result.region->type);
00236
00237
00238
00239
00240
00241 return result.is_valid() ? region.virtual_base : 0;
00242 }
00243
00244 template <typename SpaceT>
00245 int pci_device_base<SpaceT>::unmap_mmio_region(l4_gpa_t base, l4_gpa_t size)
00246 {
00247
00248
00249
00250 search_result result=search_ioregion(pci_ioregion::IOMEMORY, base, size);
00251 assert_logd(L4VMM_DEBUG_PCI, result.is_valid(), "addresses "l4_gpa_fmt"-"l4_gpa_fmt"\n",
00252 base, base + size - 1);
00253
00254 pci_ioregion ®ion=ioregions[result.region_num];
00255 assert_logd(L4VMM_DEBUG_PCI, region.is_iomemory(), "region_num: %d, type: 0x%08x\n",
00256 result.region_num, result.region->type);
00257
00258
00259
00260
00261 return 0;
00262 }
00263
00264
00265
00266
00267 template <typename SpaceT>
00268 int pci_device_base<SpaceT>::reset(void)
00269 {
00270 for (uint8_t i=0; i < EXPANSION_ROM_SLOT; i++)
00271 config_space.base_address[i]=ioregions[i].reset();
00272
00273 config_space.expansion_ROM_address=ioregions[EXPANSION_ROM_SLOT].reset();
00274 return device_base::reset();
00275 }
00276
00277 template <typename SpaceT>
00278 const pci_device::search_result pci_device_base<SpaceT>::search_ioregion(enum pci_ioregion::type type,
00279 l4_gpa_t base, l4_gpa_t size) const
00280 {
00281 const pci_ioregion *region=ioregions;
00282 for (int i=0; i < NUM_IOREGIONS; i++, region++)
00283 if (region->is_used() && region->is(type) && region->contains(base, size))
00284 return search_result(region, i);
00285
00286 return search_result();
00287 }
00288
00289 template <typename SpaceT>
00290 int pci_device_base<SpaceT>::write_config_space(pci_config_header16::offset_t offset,
00291 l4_umword_t data, access_size access_size)
00292 {
00293
00294 if (config_space.is_readonly(offset))
00295 return 0;
00296
00297 if (config_space.is_status(offset) && (access_size == access_size::WORD)) {
00298 config_space.status&=~le2cpu<uint16_t>(data);
00299 return 0;
00300 }
00301
00302 if (config_space.is_address(offset) && (access_size == access_size::DWORD)) {
00303 write_address_register(offset, data, access_size);
00304 return 0;
00305 }
00306
00307 if (config_space.is_interrupt_line(offset) && (access_size == access_size::BYTE)) {
00308 config_space.interrupt_line=data;
00309 return 0;
00310 }
00311
00312 if (config_space.is_bist(offset) && (access_size == access_size::BYTE))
00313
00314 if (data & 0x40) config_space.bist&=0x80;
00315
00316
00317 return -L4_ENOTSUPP;
00318 }
00319
00320
00321
00322
00323 template <typename SpaceT>
00324 int pci_device_base<SpaceT>::use_ioregion(enum pci_ioregion::type type,
00325 l4_gpa_t size, l4_hva_t virtual_base)
00326 {
00327 if (type == pci_ioregion::ROM)
00328 return use_ioregion(EXPANSION_ROM_SLOT, type, size, virtual_base);
00329
00330
00331 for (uint8_t i=0; i < EXPANSION_ROM_SLOT; i++)
00332 if (!ioregions[i].is_used()) return use_ioregion(i, type, size, virtual_base);
00333
00334 log::error("%s:\n too many I/O regions, only %u regions available\n",
00335 name(), NUM_IOREGIONS);
00336 return -L4_ENOTAVAIL;
00337 }
00338
00339 template <typename SpaceT>
00340 int pci_device_base<SpaceT>::use_ioregion(uint8_t region_num, enum pci_ioregion::type type,
00341 l4_gpa_t size, l4_hva_t virtual_base)
00342 {
00343 if (region_num >= NUM_IOREGIONS) {
00344 log::error("%s:\n invalid I/O region #%u, only 0-%u available\n",
00345 name(), region_num, NUM_IOREGIONS-1);
00346 return -L4_EINVAL;
00347 }
00348
00349 if ((type == pci_ioregion::ROM) && (region_num != EXPANSION_ROM_SLOT)) {
00350 log::error("%s:\n expansion ROM must be I/O region #%u\n", name(), EXPANSION_ROM_SLOT);
00351 return -L4_EINVAL;
00352 }
00353
00354 if ((1ul << bitops::highest_set_bit(size)) != size) {
00355 log::error("%s:\n I/O region size "l4_gpa_fmt" must be a power of 2\n", name(), size);
00356 return -L4_EINVAL;
00357 }
00358
00359 pci_ioregion ®ion=ioregions[region_num];
00360 if (region.is_used()) {
00361 log::error("%s:\n I/O region #%u is already used\n", name(), region_num);
00362 return -L4_EBUSY;
00363 }
00364
00365
00366 region(type, 0, size, virtual_base);
00367
00368 if (region_num == EXPANSION_ROM_SLOT) config_space.expansion_ROM_address=region;
00369 else config_space.base_address[region_num]=region;
00370 return region_num;
00371 }
00372
00373
00374
00375
00376
00377 template <typename SpaceT>
00378 int pci_device_base<SpaceT>::ioregion_relocated(uint8_t region_num, l4_gpa_t old_base)
00379 {
00380 const pci_ioregion ®ion=ioregions[region_num];
00381
00382
00383
00384
00385 if (region.is_iospace()) {
00386 log::info("%s:\n region #%u: "l4_port_fmt" -> ",
00387 name(), region_num, static_cast<l4_port_t>(old_base));
00388 region.print_short("", "\n");
00389
00390
00391 if ((old_base != 0) && iospace_manager::is_valid_ioport(old_base))
00392 free_iospace(old_base, region.size, true);
00393 if ((region.base != 0) && iospace_manager::is_valid_ioport(region.base))
00394 use_iospace(region.base, region.size, true);
00395 }
00396
00397
00398
00399
00400 else if (region.is_iomemory()) {
00401 log::info("%s:\n region #%u: "l4_gpa_fmt" -> ", name(), region_num, old_base);
00402 region.print_short("", "\n");
00403
00404
00405 if (old_base != 0) free_iomemory(old_base, region.size, true);
00406 if (region.base != 0) use_iomemory(region.base, region.size, true);
00407 }
00408
00409 return 0;
00410 }
00411
00412
00413
00414
00415 template <typename SpaceT>
00416 int pci_device_base<SpaceT>::write_address_register(pci_config_header16::offset_t offset,
00417 l4_gpa_t data, access_size access_size)
00418 {
00419 if (access_size != access_size::DWORD) return -L4_ENOTSUPP;
00420
00421 const uint8_t region_num=offset2region_num(offset);
00422 pci_ioregion ®ion=ioregions[region_num];
00423 if (!region.is_used()) return -L4_EUNUSED;
00424
00425
00426
00427
00428
00429
00430 if (!region.is_located() && (data == ~0u)) {
00431 config_space.write(offset, region.template size2bar<uint32_t>());
00432 return 0;
00433 }
00434
00435
00436
00437
00438 const l4_gpa_t old_base=region.base;
00439 region.base=le2cpu<uint32_t>(data) & region.size_mask();
00440 config_space.write(offset, region.template base2bar<uint32_t>());
00441
00442 return (region.base != old_base) ? ioregion_relocated(region_num, old_base) : 0;
00443 }
00444
00445 #endif
00446
00447
00448