Skip to content
Snippets Groups Projects
interrupt.cc 4.34 KiB
Newer Older
  • Learn to ignore specific revisions
  • #include <algorithm>
    #include <list>
    #include <map>
    
    #include "sched.hh"
    #include "drivers/pci-function.hh"
    #include "exceptions.hh"
    #include "interrupt.hh"
    #include "apic.hh"
    
    using namespace pci;
    
    interrupt_manager* interrupt_manager::_instance = nullptr;
    
    msix_vector::msix_vector(pci_function* dev)
        : _num_entries (0), _dev(dev)
    {
        _vector = idt.register_handler([this] { interrupt(); });
    }
    
    msix_vector::~msix_vector()
    {
        idt.unregister_handler(_vector);
    }
    
    pci_function* msix_vector::get_pci_function(void)
    {
        return (_dev);
    }
    
    unsigned msix_vector::get_vector(void)
    {
        return (_vector);
    }
    
    void msix_vector::msix_unmask_entries(void)
    {
        for (auto it=_entryids.begin(); it != _entryids.end(); it++) {
            int entry_id = (int)*it;
            _dev->msix_unmask_entry(entry_id);
        }
    }
    
    void msix_vector::msix_mask_entries(void)
    {
        for (auto it=_entryids.begin(); it != _entryids.end(); it++) {
            int entry_id = (int)*it;
            _dev->msix_mask_entry(entry_id);
        }
    }
    
    void msix_vector::set_handler(std::function<void ()> handler)
    {
        _handler = handler;
    }
    
    void msix_vector::add_entryid(unsigned entry_id)
    {
        _entryids.push_back(entry_id);
    }
    
    void msix_vector::interrupt(void)
    {
        _handler();
    }
    
    interrupt_manager::interrupt_manager()
    {
        for (int i=0; i<256; i++) {
            _vectors[i] = nullptr;
        }
    }
    
    interrupt_manager::~interrupt_manager()
    {
    
    }
    
    bool interrupt_manager::easy_register(pci::pci_function* dev, msix_isr_list& isrs)
    {
        unsigned n = isrs.size();
    
        assigned_vectors assigned = request_vectors(dev, n);
    
        if (assigned._num != n) {
            free_vectors(assigned);
            return (false);
        }
    
        // Enable the device msix capability,
        // masks all interrupts...
        dev->msix_enable();
    
        int idx=0;
    
        for (auto it = isrs.begin(); it != isrs.end(); it++) {
            unsigned vec = assigned._vectors[idx++];
            sched::thread *isr = it->second;
            bool assign_ok = assign_isr(vec, [isr]{ isr->wake(); });
            if (!assign_ok) {
                free_vectors(assigned);
                return false;
            }
            bool setup_ok = setup_entry(it->first, vec);
            if (!setup_ok) {
                free_vectors(assigned);
                return false;
            }
        }
    
        // Save reference for assigned vectors
        _easy_dev2vectors.insert(std::make_pair(dev, assigned));
        unmask_interrupts(assigned);
    
        return (true);
    }
    
    void interrupt_manager::easy_unregister(pci::pci_function* dev)
    {
        auto it = _easy_dev2vectors.find(dev);
        if (it != _easy_dev2vectors.end()) {
            free_vectors(it->second);
            _easy_dev2vectors.erase(it);
        }
    }
    
    assigned_vectors interrupt_manager::request_vectors(pci::pci_function* dev, int num_vectors)
    {
        assigned_vectors results;
        unsigned ctr=0;
    
        results._num = std::min(num_vectors, dev->msix_get_num_entries());
    
        for (int i=0; i<results._num; i++) {
            msix_vector * msix = new msix_vector(dev);
            unsigned vector = msix->get_vector();
            _vectors[vector] = msix;
    
            results._vectors[ctr++] = vector;
        }
    
        return (results);
    }
    
    bool interrupt_manager::assign_isr(unsigned vector, std::function<void ()> handler)
    {
        if (!_vectors[vector]) {
            return (false);
        }
    
        _vectors[vector]->set_handler(handler);
    }
    
    bool interrupt_manager::setup_entry(unsigned entry_id, unsigned vector)
    {
        // vector must be allocated
        if (!_vectors[vector]) {
            return (false);
        }
    
        msix_vector* msix = _vectors[vector];
        pci_function* dev = msix->get_pci_function();
    
        u64 address;
        u32 data;
    
        if (!apic->compose_msix(vector, 0, address, data)) {
            return (false);
        }
    
        if (!dev->msix_write_entry(entry_id, address, data)) {
            return (false);
        }
    
        msix->add_entryid(entry_id);
        return (true);
    }
    
    void interrupt_manager::free_vectors(const assigned_vectors& vectors)
    {
        for (int i=0; i<vectors._num; i++) {
            unsigned vec = vectors._vectors[i];
            if (_vectors[vec] != nullptr) {
                delete _vectors[vec];
                _vectors[vec] = nullptr;
            }
        }
    }
    
    bool interrupt_manager::unmask_interrupts(const assigned_vectors& vectors)
    {
        for (int i=0; i<vectors._num; i++) {
            unsigned vec = vectors._vectors[i];
            msix_vector* msix = _vectors[vec];
            if (msix == nullptr) {
                continue;
            }
    
            msix->msix_unmask_entries();
        }
    
        return (true);
    }