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