summaryrefslogtreecommitdiff
path: root/Kernel/Bus/VirtIO/Device.h
blob: 582576b78d1df6143fe8294ad9fd63e398af4448 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/*
 * Copyright (c) 2021, the SerenityOS developers.
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#pragma once

#include <Kernel/Bus/PCI/Access.h>
#include <Kernel/Bus/PCI/Device.h>
#include <Kernel/Bus/VirtIO/Definitions.h>
#include <Kernel/Bus/VirtIO/Queue.h>
#include <Kernel/IOWindow.h>
#include <Kernel/Interrupts/IRQHandler.h>
#include <Kernel/Memory/MemoryManager.h>

namespace Kernel::VirtIO {

void detect();

class Device
    : public PCI::Device
    , public IRQHandler {
public:
    virtual ~Device() override = default;

    virtual ErrorOr<void> initialize_virtio_resources();

protected:
    virtual StringView class_name() const { return "VirtIO::Device"sv; }
    explicit Device(PCI::DeviceIdentifier const&);

    ErrorOr<Configuration const*> get_config(ConfigurationType cfg_type, u32 index = 0) const
    {
        for (auto const& cfg : m_configs) {
            if (cfg.cfg_type != cfg_type)
                continue;
            if (index > 0) {
                index--;
                continue;
            }
            return &cfg;
        }
        return Error::from_errno(ENXIO);
    }

    template<typename F>
    void read_config_atomic(F f)
    {
        if (m_common_cfg) {
            u8 generation_before, generation_after;
            do {
                generation_before = config_read8(*m_common_cfg, 0x15);
                f();
                generation_after = config_read8(*m_common_cfg, 0x15);
            } while (generation_before != generation_after);
        } else {
            f();
        }
    }

    u8 config_read8(Configuration const&, u32);
    u16 config_read16(Configuration const&, u32);
    u32 config_read32(Configuration const&, u32);
    void config_write8(Configuration const&, u32, u8);
    void config_write16(Configuration const&, u32, u16);
    void config_write32(Configuration const&, u32, u32);
    void config_write64(Configuration const&, u32, u64);

    auto mapping_for_bar(u8) -> IOWindow&;

    u8 read_status_bits();
    void mask_status_bits(u8 status_mask);
    void set_status_bit(u8);
    u64 get_device_features();
    bool setup_queues(u16 requested_queue_count = 0);
    void finish_init();

    Queue& get_queue(u16 queue_index)
    {
        VERIFY(queue_index < m_queue_count);
        return *m_queues[queue_index];
    }

    Queue const& get_queue(u16 queue_index) const
    {
        VERIFY(queue_index < m_queue_count);
        return *m_queues[queue_index];
    }

    template<typename F>
    bool negotiate_features(F f)
    {
        u64 device_features = get_device_features();
        u64 accept_features = f(device_features);
        VERIFY(!(~device_features & accept_features));
        return accept_device_features(device_features, accept_features);
    }

    static bool is_feature_set(u64 feature_set, u64 test_feature)
    {
        // features can have more than one bit
        return (feature_set & test_feature) == test_feature;
    }
    bool is_feature_accepted(u64 feature) const
    {
        VERIFY(m_did_accept_features);
        return is_feature_set(m_accepted_features, feature);
    }

    void supply_chain_and_notify(u16 queue_index, QueueChain& chain);

    virtual bool handle_device_config_change() = 0;
    virtual void handle_queue_update(u16 queue_index) = 0;

private:
    bool accept_device_features(u64 device_features, u64 accepted_features);

    bool setup_queue(u16 queue_index);
    bool activate_queue(u16 queue_index);
    void notify_queue(u16 queue_index);

    void reset_device();

    u8 isr_status();
    virtual bool handle_irq(RegisterState const&) override;

    Vector<NonnullOwnPtr<Queue>> m_queues;
    Vector<Configuration> m_configs;
    Configuration const* m_common_cfg { nullptr }; // Cached due to high usage
    Configuration const* m_notify_cfg { nullptr }; // Cached due to high usage
    Configuration const* m_isr_cfg { nullptr };    // Cached due to high usage

    IOWindow& base_io_window();
    Array<OwnPtr<IOWindow>, 6> m_register_bases;

    StringView const m_class_name;

    u16 m_queue_count { 0 };
    bool m_use_mmio { false };
    u8 m_status { 0 };
    u64 m_accepted_features { 0 };
    bool m_did_accept_features { false };
    bool m_did_setup_queues { false };
    u32 m_notify_multiplier { 0 };
};

}