1/*
2 * Copyright (c) 2016 ETH Zurich.
3 * All rights reserved.
4 *
5 * This file is distributed under the terms in the attached LICENSE file.
6 * If you do not find this file, copies can be found by writing to:
7 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
8 */
9
10
11#include <inttypes.h>
12#include <devif/backends/blk/ahci_devq.h>
13#include "ahcid.h"
14#include "test.h"
15
16void* dq = NULL;
17struct waitset disk_ws;
18
19static struct ahci_disk* ad;
20static volatile bool driver_initialized = false;
21static struct device_mem hbabar;
22static struct waitset_chanstate *chan = NULL;
23
24
25struct device_id {
26    uint16_t vendor;
27    uint16_t device;
28};
29
30static void interrupt_handler(void *arg)
31{
32    ahci_interrupt_handler(dq);
33
34#ifdef DISABLE_INTERRUPTS
35    assert(chan != NULL);
36    assert(dq != NULL);
37    errval_t err = waitset_chan_register(&disk_ws, chan, MKCLOSURE(interrupt_handler, dq));
38    if (err_is_fail(err) && err_no(err) == LIB_ERR_CHAN_ALREADY_REGISTERED) {
39        printf("Got actual interrupt?\n");
40    }
41    else if (err_is_fail(err)) {
42        USER_PANIC_ERR(err, "Can't register our dummy channel.");
43    }
44    err = waitset_chan_trigger(chan);
45    if (err_is_fail(err)) {
46        USER_PANIC_ERR(err, "trigger failed.");
47    }
48#endif
49
50}
51
52static void do_ahci_init(void *arg, struct device_mem* bar_info, int nr_allocated_bars)
53{
54    // Although the AHCI specification requires the AHCI memory region to be in
55    // BAR 5 (BAR 0 to 4 are used for legacy IDE mode) the QEMU AHCI emulation
56    // incorrectly uses BAR 0.  Because of this, ahcid consults both BAR 0 and
57    // BAR 5 to find the HBA's memory mapped I/O region.
58    // Since two BARs between 0-5 are I/O ports they are not passed to use by PCI.
59
60    if (nr_allocated_bars == 1) {
61        memcpy(&hbabar, &bar_info[0], sizeof(struct device_mem));
62    } else if (nr_allocated_bars == 3) {
63        memcpy(&hbabar, &bar_info[2], sizeof(struct device_mem));
64    } else {
65        printf("Strange device... not supported\n");
66        abort();
67    }
68
69    errval_t err = blk_ahci_init(&hbabar, &ad);
70    if (err_is_fail(err)) {
71        USER_PANIC_ERR(err, "AHCI HBA init failed.");
72    }
73
74    struct ahci_queue* q;
75    err = ahci_create(&q, ad, 0);
76    if (err_is_fail(err)) {
77        USER_PANIC_ERR(err, "ahci_queue create failed.");
78    }
79
80    dq = (struct devq*) q;
81
82#ifdef DISABLE_INTERRUPTS
83    waitset_init(&disk_ws);
84
85    // Hack: Why don't interrupts work?
86    chan = malloc(sizeof(struct waitset_chanstate));
87    waitset_chanstate_init(chan, CHANTYPE_AHCI);
88
89    err = waitset_chan_register(&disk_ws, chan, MKCLOSURE(interrupt_handler, dq));
90    if (err_is_fail(err)) {
91        USER_PANIC_ERR(err, "waitset_chan_regster failed.");
92    }
93    err = waitset_chan_trigger(chan);
94    if (err_is_fail(err)) {
95        USER_PANIC_ERR(err, "trigger failed.");
96    }
97#endif
98
99    driver_initialized = true;
100}
101
102static void ahci_reregister_handler(void *arg)
103{
104    errval_t err;
105    struct device_id *dev_id = arg;
106    err = pci_reregister_irq_for_device(PCI_CLASS_MASS_STORAGE, PCI_SUB_SATA,
107            PCI_DONT_CARE, dev_id->vendor, dev_id->device, PCI_DONT_CARE, PCI_DONT_CARE,
108            PCI_DONT_CARE, interrupt_handler, NULL,
109            ahci_reregister_handler, dev_id);
110    if (err_is_fail(err)) {
111        DEBUG_ERR(err, "pci_reregister_irq_for_device");
112    }
113
114    return;
115}
116
117int main(int argc, char **argv)
118{
119    int r;
120    r = skb_client_connect();
121    assert(err_is_ok(r));
122    r = pci_client_connect();
123    assert(err_is_ok(r));
124
125    if (argc >= 3) {
126        printf("Got %s as vendor_id:device_id\n", argv[argc-1]);
127        uint64_t vendor_id, device_id;
128        vendor_id = strtol(argv[argc-1], NULL, 16);
129        device_id = strtol(argv[argc-1]+5, NULL, 16);
130
131        struct device_id *dev_id = malloc(sizeof(*dev_id));
132        dev_id->vendor = vendor_id;
133        dev_id->device = device_id;
134
135        r = pci_register_driver_movable_irq(do_ahci_init, NULL, PCI_CLASS_MASS_STORAGE,
136                PCI_SUB_SATA, PCI_DONT_CARE, vendor_id, device_id,
137                PCI_DONT_CARE, PCI_DONT_CARE, PCI_DONT_CARE,
138                interrupt_handler, NULL,
139                ahci_reregister_handler, dev_id);
140        if (err_is_fail(r)) {
141            printf("Couldn't register device %04"PRIx64":%04"PRIx64": %s\n", vendor_id,
142                    device_id, err_getstring(r));
143            return 1;
144        }
145        printf("AHCID: registered device %04"PRIx64":%04"PRIx64"\n", vendor_id, device_id);
146    }
147    else {
148        printf("usage: ahcid <vendor id>:<device id>\n");
149        exit(1);
150    }
151    assert(driver_initialized);
152
153#ifdef TESTING
154    printf("Initialized driver, running tests:\n");
155    if (argc < 4) {
156        return 1;
157    }
158    if (strcmp(argv[2], "read|write") == 0) {
159        test_runner(2, AhciTest_READ, AhciTest_WRITE);
160    }
161    if (strcmp(argv[2], "read") == 0) {
162        test_runner(1, AhciTest_READ);
163    }
164    if (strcmp(argv[2], "write") == 0) {
165        test_runner(1, AhciTest_WRITE);
166    }
167    if (strcmp(argv[2], "verify") == 0) {
168        test_runner(1, AhciTest_VERIFY);
169    }
170#endif
171}
172