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, Haldeneggsteig 4, 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 <dev/ata_identify_dev.h>
18#include <dev/ahci_port_dev.h>
19#include <ahci/ahci.h>
20#include <ahci/sata_fis.h>
21#include <ahci/ahci_dma_pool.h>
22
23#include "vfs_blockdevfs.h"
24
25static struct ahci_mgmt_binding *ahci_mgmt_binding;
26static bool ahci_mgmt_bound = false;
27
28#define RFIS_OFFSET_DMA_SETUP_FIS 0x00
29#define RFIS_OFFSET_PIO_SETUP_FIS 0x20
30#define RFIS_OFFSET_D2H_REGISTER_FIS 0x40
31#define RFIS_OFFSET_SET_DEVICE_BITS_FIS 0x58
32#define RFIS_OFFSET_UNKNOWN_FIS 0x60
33
34struct ahci_handle {
35    struct ahci_binding *binding;
36    uint8_t port_num;
37    bool waiting;
38    errval_t wait_status;
39    size_t bytes_transferred;
40};
41
42static void ahci_init_cb(void *st, errval_t err, struct ahci_binding *binding)
43{
44    struct ahci_handle *h = st;
45
46    if (err_is_fail(err)) {
47        printf("ahci_init_cb returned '%s'\n", err_getstring(err));
48        h->wait_status = err;
49        h->waiting = false;
50        return;
51    }
52
53    h->binding = binding;
54    binding->st = h;
55    h->waiting = false;
56}
57
58errval_t blockdevfs_ahci_open(void *handle)
59{
60    VFS_BLK_DEBUG("blockdevfs_ahci_open: entering\n");
61    errval_t err;
62    struct ahci_handle *h = handle;
63
64    h->wait_status = SYS_ERR_OK;
65    h->waiting = true;
66
67    err = ahci_init(h->port_num, ahci_init_cb, h, get_default_waitset());
68    if (err_is_fail(err)) {
69        printf("ahci_init failed: '%s'\n", err_getstring(err));
70        h->waiting = false;
71        return err;
72    }
73
74    // XXX: block for command completion (broken API!)
75    while (h->waiting) {
76        messages_wait_and_handle_next();
77    }
78
79    VFS_BLK_DEBUG("blockdevfs_ahci_open: exiting\n");
80    return h->wait_status;
81}
82
83static void ahci_close_cb(void *arg)
84{
85    struct ahci_handle *h = arg;
86    h->waiting = false;
87}
88
89errval_t blockdevfs_ahci_close(void *handle)
90{
91    struct ahci_handle *h = handle;
92    h->waiting = true;
93    errval_t err = ahci_close(h->binding, MKCLOSURE(ahci_close_cb, h));
94    if (err_is_fail(err)) {
95        printf("ahci_init failed: '%s'\n", err_getstring(err));
96        h->waiting = false;
97        return err;
98    }
99    while (h->waiting) {
100        messages_wait_and_handle_next();
101    }
102    return SYS_ERR_OK;
103}
104
105static void rx_flush_command_completed_cb(struct ahci_binding *binding, void *tag)
106{
107    struct ahci_handle *h = binding->st;
108    VFS_BLK_DEBUG("rx_flush_command_completed_cb(%p, tag: %p): entering\n",
109            binding, tag);
110    h->waiting = false;
111}
112
113errval_t blockdevfs_ahci_flush(void *handle)
114{
115    errval_t err = SYS_ERR_OK;
116    struct ahci_handle *h = handle;
117    h->waiting = true;
118    // setup FIS
119    struct sata_fis_reg_h2d fis;
120    memset(&fis, 0, sizeof(struct sata_fis_reg_h2d));
121    fis.type = SATA_FIS_TYPE_H2D;
122    fis.device = 1 << 6; /* LBA mode, not CHS; ??? */
123    sata_set_command(&fis, 0xE7); /* flush cache; ATA Command Set, 7.24 */
124    sata_set_count(&fis, 0); /* nr. of sectors/blocks */
125    sata_set_lba28(&fis, 0);
126
127    // set handle to waiting
128    h->waiting = true;
129    h->wait_status = SYS_ERR_OK;
130    h->bytes_transferred = 0;
131    h->binding->rx_vtbl.command_completed = rx_flush_command_completed_cb;
132
133    // load fis and fire commands
134    err = ahci_issue_command(h->binding, NOP_CONT, 0,
135            (uint8_t*)&fis, sizeof(fis), false, NULL, 0);
136    if (err_is_fail(err)) {
137        printf("bdfs_ahci: read load_fis failed: 0x%" PRIxPTR "\n", err);
138        h->waiting = false;
139        goto cleanup;
140    }
141
142    while (h->waiting) {
143        messages_wait_and_handle_next();
144    }
145
146cleanup:
147
148    h->binding->rx_vtbl.command_completed = NULL;
149
150    return err;
151}
152
153static void rx_read_command_completed_cb(struct ahci_binding *binding, void *tag)
154{
155    struct ahci_handle *h = binding->st;
156    VFS_BLK_DEBUG("rx_read_command_completed_cb(%p, tag: %p): entering\n",
157            binding, tag);
158
159    h->waiting = false;
160}
161
162errval_t blockdevfs_ahci_read(void *handle, size_t pos, void *buffer, size_t
163        bytes, size_t *bytes_read)
164{
165    errval_t err;
166    struct ahci_handle *h = handle;
167    size_t aligned_bytes = bytes / PR_SIZE * PR_SIZE;
168
169    VFS_BLK_DEBUG("bdfs_ahci: read begin: %zu -> %zu\n", bytes, aligned_bytes);
170
171    // setup DMA regions for receiving data
172    struct ahci_dma_region *bufregion = NULL;
173    err = ahci_dma_region_alloc(aligned_bytes, &bufregion);
174    if (err_is_fail(err)) {
175        printf("bdfs_ahci: read alloc_region failed: 0x%" PRIxPTR  "\n", err);
176        return err;
177    }
178    VFS_BLK_DEBUG("bdfs_ahci_read: bufregion = %p\n", bufregion);
179    VFS_BLK_DEBUG("bdfs_ahci_read: bufregion->vaddr = %p\n", bufregion->vaddr);
180
181    // setup FIS
182    struct sata_fis_reg_h2d read_fis;
183    memset(&read_fis, 0, sizeof(struct sata_fis_reg_h2d));
184    read_fis.type = SATA_FIS_TYPE_H2D;
185    read_fis.device = 1 << 6; // LBA mode, not CHS; ???
186    sata_set_command(&read_fis, 0xC8); // read dma; ATA Command Set, 7.24
187    sata_set_count(&read_fis, aligned_bytes / PR_SIZE); // nr. of sectors/blocks
188    sata_set_lba28(&read_fis, pos / PR_SIZE);
189
190    // set handle to waiting
191    h->waiting = true;
192    h->wait_status = SYS_ERR_OK;
193    h->bytes_transferred = 0;
194    h->binding->rx_vtbl.command_completed = rx_read_command_completed_cb;
195
196    // load fis and fire commands
197    err = ahci_issue_command(h->binding, NOP_CONT, 0,
198            (uint8_t*)&read_fis, sizeof(read_fis), false, bufregion, aligned_bytes);
199    if (err_is_fail(err)) {
200        printf("bdfs_ahci: read load_fis failed: 0x%" PRIxPTR  "\n", err);
201        h->waiting = false;
202        goto cleanup;
203    }
204
205    // XXX: block for command completion (broken API!)
206    while (h->waiting) {
207        messages_wait_and_handle_next();
208    }
209
210    VFS_BLK_DEBUG("bdfs_ahci: read wait status: %lu\n", h->wait_status);
211
212    err = h->wait_status;
213
214    // cleanup and output
215    if (err_is_ok(err)) {
216        ahci_dma_region_copy_out(bufregion, buffer, 0, aligned_bytes);
217        *bytes_read = aligned_bytes;
218    }
219
220cleanup:
221
222    h->binding->rx_vtbl.command_completed = NULL;
223
224    VFS_BLK_DEBUG("read: freeing bufregion (%p)\n", bufregion);
225    ahci_dma_region_free(bufregion);
226
227    return err;
228
229}
230
231static void rx_write_command_completed_cb(struct ahci_binding *binding, void *tag)
232{
233    struct ahci_handle *h = binding->st;
234    VFS_BLK_DEBUG("rx_write_command_completed_cb(%p, tag: %p): entering\n",
235            binding, tag);
236    h->waiting = false;
237}
238
239errval_t blockdevfs_ahci_write(void *handle, size_t pos, const void *buffer,
240        size_t bytes, size_t *bytes_written)
241{
242    errval_t err;
243    struct ahci_handle *h = handle;
244
245    size_t aligned_bytes = bytes / PR_SIZE * PR_SIZE;
246
247    // setup DMA regions and copy data over
248    struct ahci_dma_region *bufregion;
249    err = ahci_dma_region_alloc(aligned_bytes, &bufregion);
250    if (err_is_fail(err)) {
251        return err;
252    }
253    ahci_dma_region_copy_in(bufregion, buffer, 0, aligned_bytes);
254
255    // setup FIS
256    struct sata_fis_reg_h2d write_fis;
257    memset(&write_fis, 0, sizeof(struct sata_fis_reg_h2d));
258    write_fis.type = SATA_FIS_TYPE_H2D;
259    write_fis.device = 1 << 6; // LBA mode, not CHS; ???
260    sata_set_command(&write_fis, 0xCA); // write dma; ATA Command Set, 7.60
261    sata_set_count(&write_fis, aligned_bytes / PR_SIZE); // nr. of sectors/blocks
262    sata_set_lba28(&write_fis, pos / PR_SIZE);
263
264    // set handle to waiting
265    h->waiting = true;
266    h->wait_status = SYS_ERR_OK;
267    h->binding->rx_vtbl.command_completed = rx_write_command_completed_cb;
268
269    // load fis and fire commands
270    err = ahci_issue_command(h->binding, NOP_CONT, 0,
271            (uint8_t*)&write_fis, sizeof(write_fis), true, bufregion, aligned_bytes);
272    if (err_is_fail(err)) {
273        h->waiting = false;
274        ahci_dma_region_free(bufregion);
275        return err;
276    }
277
278    // XXX: block for command completion (broken API!)
279    while (h->waiting) {
280        messages_wait_and_handle_next();
281    }
282
283    // cleanup and output
284    h->binding->rx_vtbl.command_completed = NULL;
285    ahci_dma_region_free(bufregion);
286    if (err_is_ok(h->wait_status)) {
287        *bytes_written = aligned_bytes;
288    }
289    return h->wait_status;
290}
291
292static void ahci_mgmt_bind_cb(void *st, errval_t err, struct ahci_mgmt_binding *b)
293{
294    if (err_is_fail(err)) {
295        USER_PANIC_ERR(err, "ahci_mgmt bind failed in callback");
296    }
297
298    ahci_mgmt_binding = b;
299    ahci_mgmt_rpc_client_init(ahci_mgmt_binding);
300
301    ahci_mgmt_bound = true;
302
303    struct ahci_mgmt_list_response__rx_args reply;
304    err = ahci_mgmt_binding->rpc_tx_vtbl.list(ahci_mgmt_binding, reply.port_ids, &reply.len);
305    assert(err_is_ok(err));
306
307    for (size_t i = 0; i < reply.len; i++) {
308        struct ahci_mgmt_identify_response__rx_args identify_reply;
309        err = ahci_mgmt_binding->rpc_tx_vtbl.identify(ahci_mgmt_binding,
310                reply.port_ids[i], identify_reply.identify_data,
311                &identify_reply.data_len);
312        assert(err_is_ok(err));
313        assert(identify_reply.data_len == 512);
314
315        ata_identify_t identify;
316        ata_identify_initialize(&identify, (void *)identify_reply.identify_data );
317
318        //char buf[8192];
319        //ata_identify_pr(buf, 8192, &identify);
320        //printf("New Disk. Identify:\n");
321        //puts(buf);
322
323        // determine sector size
324        size_t sector_size = 512;
325        if (ata_identify_plss_lls_rdf(&identify)) {
326            sector_size = 2*ata_identify_wpls_rd(&identify);
327        }
328
329        // FIXME: support LBA28
330        size_t sectors = ata_identify_tnuas48_rd(&identify);
331
332        VFS_BLK_DEBUG("Disk %" PRIu8 " has %zd sectors of %zd bytes\n",
333                port_ids[i], sectors, sector_size);
334
335        struct ahci_handle *handle = calloc(1, sizeof(struct ahci_handle));
336        handle->port_num = reply.port_ids[i];
337
338        struct blockdev_entry *newentry = calloc(1, sizeof(struct blockdev_entry));
339        newentry->open = false;
340        newentry->size = sectors * sector_size;
341        newentry->type = blockdev_backend_type_ahci;
342        newentry->path = strdup("ahciX");
343        newentry->path[4] = '0' + i; // FIXME: make it work for more than 9
344        newentry->backend_handle = handle;
345
346        // append to list
347        blockdev_append_entry(newentry);
348    }
349}
350
351errval_t blockdevfs_ahci_init(void)
352{
353    errval_t err;
354    iref_t iref;
355
356    err = nameservice_blocking_lookup("ahcid", &iref);
357    if (err_is_fail(err)) {
358        DEBUG_ERR(err, "nameservice_blocking_lookup for ahcid");
359        return err; // FIXME
360    }
361
362    err = ahci_mgmt_bind(iref, ahci_mgmt_bind_cb, NULL, get_default_waitset(),
363                  IDC_BIND_FLAG_RPC_CAP_TRANSFER);
364
365    // init DMA pool
366    ahci_dma_pool_init(1024*1024);
367
368    if (err_is_fail(err)) {
369        DEBUG_ERR(err, "ahci_mgmt bind failed");
370        return err; // FIXME
371    }
372
373    // XXX: block for bind completion (broken API!)
374    while (!ahci_mgmt_bound) {
375        messages_wait_and_handle_next();
376    }
377
378    return SYS_ERR_OK;
379}
380