Overview   API Reference  

pci_device_base.hpp

00001 #if !defined(__PCI_DEVICE_BASE_HPP__)
00002 #define __PCI_DEVICE_BASE_HPP__
00003 
00004 //
00005 // local includes
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     // device/device_base: override name & irq_moved:
00096     //     to disambuigate calls and to write the irq line back to
00097     //     the PCI configuration space's interrupt line register
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     // mmio_handler: override I/O memory (un)map functions
00112     //     to (un)map PCI I/O memory regions
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     // pci_device: override pci_device interface
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     // device_base: override use_irq & free_irq:
00147     //     to write the irq line back to the PCI configuration space's interrupt line register
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     // I/O resources management functions
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     // internal write address register: calls ioregion_relocated if necessary
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 // mmio_handler: I/O memory (un)map functions
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     // check constraints
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 &region=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     // for virtual devices we do not have to map memory here, so simply return
00239     //     the pointer the device stored for this region
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     // check constraints
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 &region=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     // for virtual devices we do not have to ummap memory here, so simply return success
00260     //
00261     return 0;
00262 }
00263 
00264 //
00265 // pci_device: override pci_device interface
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     // unconditionally return 0, if this offset (register) was handled
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         // reset start bit and set completion code to 0 (OK)
00314         if (data & 0x40) config_space.bist&=0x80;
00315 
00316     // no offset (register) that can be handled generically
00317     return -L4_ENOTSUPP;
00318 }
00319 
00320 //
00321 // I/O resources management functions
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     // search for free slot to use. last entry is expansion ROM address
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 &region=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     // base is set by client operating system
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 // simple implementation which registers itself as I/O space or MMIO handler
00375 //     override if you want different handlers for different I/O regions
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 &region=ioregions[region_num];
00381 
00382     //
00383     // I/O space regions
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         // use VERBOSE_ALLOCATION ?
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     // memory regions (including expansion ROM)
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         // use VERBOSE_ALLOCATION ?
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 // internal write address register: calls ioregion_relocated if necessary
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 &region=ioregions[region_num];
00423     if (!region.is_used()) return -L4_EUNUSED;
00424 
00425     //
00426     // shortcut to omit unnecessary resource relocations:
00427     //     don't locate the region when the client tries to get its size
00428     //     (and if it hasn't been located before). only write the PCI configuration space value.
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     // update I/O region & the PCI configuration space value
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 // ***** end of source ***** //
00448 

L4vmm Reference Manual, written by Mario Schwalbe  © 2006-2008