1/*
2 * Copyright (c) 2009, 2011, 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, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
8 */
9
10#define _USE_XOPEN // for strdup()
11#include <stdio.h>
12#include <string.h>
13#include <barrelfish/barrelfish.h>
14#include <barrelfish/nameservice_client.h>
15#include <if/ahci_mgmt_defs.h>
16#include <if/ahci_mgmt_defs.h>
17#include <if/ahci_mgmt_defs.h>
18#include <if/ata_rw28_defs.h>
19#include <if/ata_rw28_ahci_defs.h>
20#include <if/ata_rw28_defs.h>
21#include <dev/ata_identify_dev.h>
22#include <dev/ahci_port_dev.h>
23#include <ahci/ahci.h>
24#include <ahci/sata_fis.h>
25#include <ahci/ahci_dma_pool.h>
26
27#include "vfs_blockdevfs.h"
28
29static struct ahci_mgmt_binding *ahci_mgmt_binding;
30static bool ahci_mgmt_bound = false;
31
32#define RFIS_OFFSET_DMA_SETUP_FIS 0x00
33#define RFIS_OFFSET_PIO_SETUP_FIS 0x20
34#define RFIS_OFFSET_D2H_REGISTER_FIS 0x40
35#define RFIS_OFFSET_SET_DEVICE_BITS_FIS 0x58
36#define RFIS_OFFSET_UNKNOWN_FIS 0x60
37
38struct ata_handle {
39    struct ahci_binding *ahci_binding;
40    struct ata_rw28_binding *ata_rw28_binding;
41    uint8_t port_num;
42    bool waiting;
43    errval_t wait_status;
44    size_t bytes_transferred;
45};
46
47static void ahci_init_cb(void *st, errval_t err, struct ahci_binding *binding)
48{
49    struct ata_handle *h = st;
50
51    if (err_is_fail(err)) {
52        printf("ahci_init_cb returned '%s'\n", err_getstring(err));
53        h->wait_status = err;
54        h->waiting = false;
55        return;
56    }
57
58    h->ahci_binding = binding;
59    binding->st = h;
60    h->waiting = false;
61}
62
63errval_t blockdevfs_ata_open(void *handle)
64{
65    VFS_BLK_DEBUG("blockdevfs_ata_open: entering\n");
66    errval_t err;
67    struct ata_handle *h = handle;
68
69    h->wait_status = SYS_ERR_OK;
70    h->waiting = true;
71
72    err = ahci_init(h->port_num, ahci_init_cb, h, get_default_waitset());
73    if (err_is_fail(err)) {
74        printf("ahci_init failed: '%s'\n", err_getstring(err));
75        h->waiting = false;
76        return err;
77    }
78
79    // XXX: block for command completion (broken API!)
80    while (h->waiting) {
81        err = event_dispatch(get_default_waitset());
82        if (err_is_fail(err)) {
83            USER_PANIC_ERR(err, "error in event_dispatch for blockdevfs_ata_open");
84        }
85    }
86
87    struct ahci_ata_rw28_binding *ahci_ata_rw28_binding;
88    ahci_ata_rw28_binding = calloc(1, sizeof(struct ahci_ata_rw28_binding));
89    ahci_ata_rw28_init(ahci_ata_rw28_binding, get_default_waitset(), h->ahci_binding);
90    h->ata_rw28_binding = (struct ata_rw28_binding*)ahci_ata_rw28_binding;
91    ata_rw28_rpc_client_init(h->ata_rw28_binding);
92    VFS_BLK_DEBUG("blockdevfs_ata_open: exiting\n");
93    return h->wait_status;
94}
95
96static void
97ahci_close_cb(void *arg)
98{
99    struct ata_handle *h = arg;
100    h->waiting = false;
101    h->wait_status = SYS_ERR_OK;
102}
103
104errval_t blockdevfs_ata_flush(void *handle)
105{
106    struct ata_handle *h = handle;
107    errval_t err, status;
108    err = h->ata_rw28_binding->rpc_tx_vtbl.flush_cache(h->ata_rw28_binding, &status);
109    if (err_is_fail(err)) {
110        printf("failed calling flush_cache\n");
111        return err;
112    }
113    VFS_BLK_DEBUG("status: %s\n", err_getstring(status));
114    return err;
115}
116
117errval_t blockdevfs_ata_close(void *handle)
118{
119    struct ata_handle *h = handle;
120    errval_t err, status;
121
122    err = h->ata_rw28_binding->rpc_tx_vtbl.flush_cache(h->ata_rw28_binding, &status);
123    if (err_is_fail(err)) {
124        printf("failed calling flush_cache\n");
125        return err;
126    }
127    VFS_BLK_DEBUG("status: %s\n", err_getstring(status));
128
129    free(h->ata_rw28_binding);
130    h->waiting = true;
131    err = ahci_close(h->ahci_binding, MKCLOSURE(ahci_close_cb, h));
132    if (err_is_fail(err)) {
133        printf("ahci_init failed: '%s'\n", err_getstring(err));
134        h->waiting = false;
135        return err;
136    }
137
138    // XXX: block for command completion (broken API!)
139    while (h->waiting) {
140        err = event_dispatch(h->ahci_binding->waitset);
141        if (err_is_fail(err)) {
142            USER_PANIC_ERR(err, "error in event_dispatch for blockdevfs_ata_close");
143        }
144    }
145
146    return h->wait_status;
147}
148
149errval_t blockdevfs_ata_read(void *handle, size_t pos, void *buffer,
150        size_t bytes, size_t *bytes_read)
151{
152    errval_t err;
153    struct ata_handle *h = handle;
154    size_t aligned_bytes = bytes / PR_SIZE * PR_SIZE;
155    size_t blockpos = pos / PR_SIZE;
156
157    //VFS_BLK_DEBUG("bdfs_ahci: read begin: %zu -> %zu\n", bytes, aligned_bytes);
158
159    err = h->ata_rw28_binding->rpc_tx_vtbl.read_dma(h->ata_rw28_binding,
160            aligned_bytes, blockpos, buffer, bytes_read);
161
162    return err;
163}
164
165errval_t blockdevfs_ata_write(void *handle, size_t pos, const void *buffer,
166        size_t bytes, size_t *bytes_written)
167{
168    errval_t err;
169    struct ata_handle *h = handle;
170
171    size_t aligned_bytes = bytes / PR_SIZE * PR_SIZE;
172    size_t blockpos = pos / PR_SIZE;
173
174    errval_t status;
175    err = h->ata_rw28_binding->rpc_tx_vtbl.write_dma(h->ata_rw28_binding,
176            buffer, aligned_bytes, blockpos, &status);
177    *bytes_written = aligned_bytes;
178    return err;
179}
180
181static void ahci_mgmt_bind_cb(void *st, errval_t err, struct ahci_mgmt_binding *b)
182{
183    if (err_is_fail(err)) {
184        USER_PANIC_ERR(err, "ahci_mgmt bind failed in callback");
185    }
186
187    ahci_mgmt_binding = b;
188    ahci_mgmt_rpc_client_init(ahci_mgmt_binding);
189    ahci_mgmt_bound = true;
190
191    // populate list
192    struct ahci_mgmt_list_response__rx_args reply;
193    err = ahci_mgmt_binding->rpc_tx_vtbl.list(ahci_mgmt_binding, reply.port_ids, &reply.len);
194    assert(err_is_ok(err));
195
196    for (size_t i = 0; i < reply.len; i++) {
197        if (i > 9) {
198            break;
199        }
200        struct ahci_mgmt_identify_response__rx_args identify_reply;
201        err = ahci_mgmt_binding->rpc_tx_vtbl.identify(ahci_mgmt_binding, reply.port_ids[i],
202                                          identify_reply.identify_data,
203                                          &identify_reply.data_len);
204        assert(err_is_ok(err));
205        assert(identify_reply.data_len == 512);
206
207        ata_identify_t identify;
208        ata_identify_initialize(&identify, (void *)identify_reply.identify_data);
209
210        //char buf[8192];
211        //ata_identify_pr(buf, 8192, &identify);
212        //printf("New Disk. Identify:\n");
213        //puts(buf);
214
215        // determine sector size
216        size_t sector_size = 512;
217        if (ata_identify_plss_lls_rdf(&identify)) {
218            sector_size = 2*ata_identify_wpls_rd(&identify);
219        }
220
221        // FIXME: support LBA28
222        size_t sectors = ata_identify_tnuas48_rd(&identify);
223
224        VFS_BLK_DEBUG("Disk %" PRIu8 " has %zd sectors of %zd bytes\n",
225                reply.port_ids[i], sectors, sector_size);
226
227        struct ata_handle *handle = calloc(1, sizeof(struct ata_handle));
228        handle->port_num = reply.port_ids[i];
229
230        struct blockdev_entry *newentry = calloc(1, sizeof(struct blockdev_entry));
231        newentry->open = false;
232        newentry->size = sectors * sector_size;
233        newentry->type = blockdev_backend_type_ata;
234        newentry->path = strdup("ataX");
235        newentry->path[3] = '0' + i; // FIXME: make it work for more than 9
236        newentry->backend_handle = handle;
237
238        // append to list
239        blockdev_append_entry(newentry);
240    }
241}
242
243errval_t blockdevfs_ata_init(void)
244{
245    errval_t err;
246    iref_t iref;
247
248    err = nameservice_blocking_lookup("ahcid", &iref);
249    if (err_is_fail(err)) {
250        DEBUG_ERR(err, "nameservice_blocking_lookup for ahcid");
251        return err; // FIXME
252    }
253
254    err = ahci_mgmt_bind(iref, ahci_mgmt_bind_cb, NULL, get_default_waitset(),
255                  IDC_BIND_FLAG_RPC_CAP_TRANSFER);
256
257    if (err_is_fail(err)) {
258        DEBUG_ERR(err, "ahci_mgmt bind failed");
259        return err; // FIXME
260    }
261
262    // init DMA pool
263    ahci_dma_pool_init(1024*1024);
264
265    // XXX: block for bind completion (broken API!)
266    while (!ahci_mgmt_bound) {
267        err = event_dispatch(get_default_waitset());
268        if (err_is_fail(err)) {
269            USER_PANIC_ERR(err, "error in event_dispatch for blockdevfs_ata_init");
270        }
271    }
272
273    return SYS_ERR_OK;
274}
275