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