1/* 2 * Copyright (c) 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#include <barrelfish/barrelfish.h> 11#include <barrelfish/memobj.h> 12#include <barrelfish/vregion.h> 13#include <ahci/ahci.h> 14#include <ahci/ahci_dma_pool.h> 15#include <dev/ahci_hba_dev.h> 16#include <string.h> 17#include "ahci_debug.h" 18#include "ahci_internal.h" 19 20#define AHCI_MAX_PRD_COUNT 65535 21 22// CL is 1K, see AHCI Spec 1.3 Section 3.3.1 23#define AHCI_CL_SIZE 1024 24 25// rFIS is 256 bytes, see AHCI Spec 1.3 Section 3.3.3 26#define AHCI_RFIS_SIZE 256 27 28 29int ahci_find_free_command_slot(struct ahci_port_info *port) 30{ 31 for (int i = 0; i < ahci_hba_cap_ncs_extract(port->hba_capabilities) + 1; i++) { 32 if (!port->command_slots[i].in_use) { 33 return i; 34 } 35 } 36 37 return -1; 38} 39 40errval_t ahci_port_alloc_dma_structs(ahci_port_t *port, 41 struct ahci_dma_region **command_list, 42 struct ahci_dma_region **receive_fis) 43{ 44 errval_t r; 45 46 // allocate frame for command header list 47 r = ahci_dma_region_alloc_aligned(AHCI_CL_SIZE, AHCI_CL_SIZE, 48 command_list); 49 assert(err_is_ok(r)); 50 // CLB must be aligned to 1024 bytes 51 assert(((*command_list)->paddr & 0x3FF) == 0); 52 ahci_port_clb_wr(port, (*command_list)->paddr); 53 54 // allocate frame for received FIS 55 r = ahci_dma_region_alloc_aligned(AHCI_RFIS_SIZE, AHCI_RFIS_SIZE, 56 receive_fis); 57 assert(err_is_ok(r)); 58 // FIS must be aligned to 256 bytes 59 assert(((*receive_fis)->paddr & 0xFF) == 0); 60 ahci_port_fb_wr(port, (*receive_fis)->paddr); 61 62 return 0; 63} 64 65void ahci_port_free_dma_structs(struct ahci_port_info *port) 66{ 67 errval_t err; 68 err = ahci_dma_region_free(port->command_list); 69 if (err_is_fail(err)) { 70 USER_PANIC_ERR(err, "ahci_dma_region_free failed for port command list"); 71 } 72 err = ahci_dma_region_free(port->receive_fis); 73 if (err_is_fail(err)) { 74 USER_PANIC_ERR(err, "ahci_dma_region_free failed for port receive fis"); 75 } 76} 77 78errval_t ahci_setup_command(int *command, struct ahci_port_info *port, 79 uint8_t *fis, size_t fis_length, size_t num_prds, bool is_write) 80{ 81 errval_t r; 82 AHCI_DEBUG("ahci_setup_command(%p, %p, %p, %zu, %zu, %d): entering\n", 83 command, port, fis, fis_length, num_prds, is_write); 84 85 int command_slot = ahci_find_free_command_slot(port); 86 assert(command_slot != -1); //TODO: what to do if we run out of slots? wait? 87 port->command_slots[command_slot].in_use = true; 88 89 // setup command table w/ enough entries for PRDs 90 size_t prdt_size = num_prds*ahci_port_prd_size; 91 size_t cmd_table_size = PRDT_OFFSET + prdt_size; 92 struct ahci_dma_region *ct; 93 r = ahci_dma_region_alloc(cmd_table_size, &ct); 94 if (err_is_fail(r)) { 95 DEBUG_ERR(r, "failed to allocate memory for command table"); 96 return r; 97 } 98 port->command_slots[command_slot].command_table = ct; 99 100 101 AHCI_DEBUG("Using command slot: %d\n", command_slot); 102 // insert command table into command header at pos `next_cmd' 103 uint8_t *command_header_entry = 104 ((uint8_t*)port->command_list->vaddr) + command_slot*ahci_port_chdr_size; 105 memset(command_header_entry, 0, ahci_port_chdr_size); 106 ahci_port_chdr_t chdr = (ahci_port_chdr_t) command_header_entry; 107 ahci_port_chdr_cfl_insert(chdr, fis_length / 4); 108 ahci_port_chdr_w_insert(chdr, is_write); 109 ahci_port_chdr_ctba_insert(chdr, ct->paddr); 110 111 // copy fis into command table 112 memset(ct->vaddr, 0, cmd_table_size); 113 ahci_dma_region_copy_in(ct, fis, 0, fis_length); 114 115 *command = command_slot; 116 117 AHCI_DEBUG("ahci_setup_command: exiting\n"); 118 return SYS_ERR_OK; 119} 120 121static void ahci_set_physical_region(ahci_port_prd_t prd, 122 genpaddr_t physical_base, size_t length) 123{ 124 //AHCI_DEBUG("ahci_set_physical_region: entering\n"); 125#ifdef AHCI_FIXED_PR_SIZE 126 assert(length == PR_SIZE); 127#else 128 assert(length <= MAX_PR_SIZE); 129 assert((length & 1) == 0); 130#endif 131 132 ahci_port_prd_dba_insert(prd, (uint32_t)physical_base); 133 ahci_port_prd_dbau_insert(prd, (uint32_t)(physical_base >> 32)); 134 ahci_port_prd_dbc_insert(prd, length-1); 135 //AHCI_DEBUG("ahci_set_physical_region: exiting\n"); 136} 137 138static inline genpaddr_t ahci_get_sector_paddr(struct ahci_dma_region *region, 139 size_t offset) 140{ 141 return region->paddr + offset; 142} 143 144errval_t ahci_add_physical_regions(struct ahci_port_info *port, 145 int command, struct ahci_dma_region *buf, size_t buflen) 146{ 147 AHCI_DEBUG("ahci_add_physical_regions: entering\n"); 148 uint8_t *command_header_base = 149 ((uint8_t*)port->command_list->vaddr) + command*ahci_port_chdr_size; 150 ahci_port_chdr_t chdr = (ahci_port_chdr_t) command_header_base; 151 152 // find next free prd entry in command header 153 size_t prd_count = ahci_port_chdr_prdtl_extract(chdr); 154 AHCI_DEBUG("prd_count = %zd\n", prd_count); 155 // position for next prd 156 size_t prdt_index = prd_count; 157 // get base address of prd table 158 uint8_t *prdt_base = 159 (uint8_t*)port->command_slots[command].command_table->vaddr + 160 PRDT_OFFSET; 161 162 // max 512 bytes per PR 163#ifdef AHCI_FIXED_PR_SIZE 164 size_t num_prds_needed = CEIL_DIV(buflen, PR_SIZE); 165#else 166 size_t num_prds_needed = CEIL_DIV(buflen, MAX_PR_SIZE); 167#endif 168 AHCI_DEBUG("#prds = %zd\n", num_prds_needed); 169 170 // check that remaining space is enough for requested length 171 if (prdt_index + num_prds_needed >= AHCI_MAX_PRD_COUNT) { 172 // TODO: error code 173 AHCI_DEBUG("not enough prd space left.\n"); 174 return -1; 175 } 176 177 AHCI_DEBUG("PRDT base: %p\n", prdt_base); 178 // initialize a prd for each region 179 ahci_port_prd_t prd; 180 size_t offset = 0; 181 do { 182 prd = (ahci_port_prd_t) prdt_base + (prdt_index * ahci_port_prd_size); 183 memset(prd, 0, ahci_port_prd_size); 184 genpaddr_t sector_addr = ahci_get_sector_paddr(buf, offset); 185 //AHCI_DEBUG("sector_addr = 0x%lx\n", sector_addr); 186#ifdef AHCI_FIXED_PR_SIZE 187 ahci_set_physical_region(prd, sector_addr, PR_SIZE); 188 buflen -= PR_SIZE; 189 offset += PR_SIZE; 190#else 191 size_t chunksize = buflen < MAX_PR_SIZE ? buflen : MAX_PR_SIZE; 192 ahci_set_physical_region(prd, sector_addr, chunksize); 193 buflen -= chunksize; 194 offset += chunksize; 195#endif 196 prdt_index += 1; 197 } while (buflen > 0); 198 199 // update number of PRDs for command 200 ahci_port_chdr_prdtl_insert(chdr, prdt_index); 201 202 AHCI_DEBUG("ahci_add_physical_regions: exiting\n"); 203 return 0; 204} 205 206#if defined(AHCI_LIB_DEBUG) || defined(GLOBAL_DEBUG) 207void ahci_dump_command(int command, struct ahci_port_info *port) 208{ 209 char buf_[4096]; 210 ahci_port_chdr_t chdr = 211 (ahci_port_chdr_t)((uint8_t*) port->command_list->vaddr) + 212 command*ahci_port_chdr_size; 213 ahci_port_chdr_prtval(buf_, 4096, chdr); 214 AHCI_DEBUG("\n%s\n", buf_); 215 int prd_count = ahci_port_chdr_prdtl_extract(chdr); 216 ahci_port_prd_t prd; 217 for (int k = 0; k < prd_count; k++) { 218 uint8_t *cmd_table_base = (uint8_t*) port->command_slots[command].command_table->vaddr; 219 prd = (ahci_port_prd_t) (cmd_table_base + PRDT_OFFSET + k*ahci_port_prd_size); 220 ahci_port_prd_prtval(buf_, 4096, prd); 221 AHCI_DEBUG("\nPRD %d:\n%s\n", k, buf_); 222 } 223} 224#else 225void ahci_dump_command(int command, struct ahci_port_info *port) {} 226#endif 227 228#if defined(AHCI_LIB_DEBUG) || defined(GLOBAL_DEBUG) 229void ahci_dump_rfis(struct ahci_port_info *port) 230{ 231 printf("rfis:\n"); 232 for (int i = 0; i < 256; i += 4) { 233 printf("%3x: 0x%02x 0x%02x 0x%02x 0x%02x\n", i, 234 *(((uint8_t*)port->receive_fis->vaddr) + i), 235 *(((uint8_t*)port->receive_fis->vaddr) + i + 1), 236 *(((uint8_t*)port->receive_fis->vaddr) + i + 2), 237 *(((uint8_t*)port->receive_fis->vaddr) + i + 3) 238 ); 239 } 240} 241#else 242void ahci_dump_rfis(struct ahci_port_info *port) {} 243#endif 244