- Refactors existing AHCI code to use device queue interface.
- Adds harness test for disks.
- Adds some minor improvements for scalebench harness.
- Disable vtd due to some bug with the identity mapping on babybel machines.
Signed-off-by: Gerd Zellweger <mail@gerdzellweger.com>
Conflicts:
usr/acpi/acpi_main.c
* ahci_port.dev
*
* DESCRIPTION: AHCI (SATA) Host bus adaptor, per-port registers
- *
+ *
* Section numbers refer to the Serial ATA Advanced Host Controller
* Interface (AHCI) specification 1.3, June 2008
*/
device ahci_port msbfirst (addr b) "AHCI port" {
- // 3.3.1-2
+ // 3.3.1-2
// Low 10 bits mbz
register clb rw addr(b,0x00) "Command list base address" type(uint64);
-
+
// 3.3.3-4
// Low 10 bits mbz
register fb rw addr(b,0x08) "FIS base address" type(uint64);
-
+
// 3.3.5
register is addr(b,0x10) "Interrupt status" {
cpds 1 rw1c "Cold port detect";
pss 1 rw1c "PIO setup FIS interrupt";
dhrs 1 rw1c "Device to host register FIS interrupt";
};
-
+
// 3.3.6
register ie addr(b,0x14) "Interrupt enable" {
cpde 1 rw "Cold port detect";
pse 1 rw "PIO setup FIS interrupt";
dhre 1 rw "Device to host register FIS interrupt";
};
-
- // 3.3.7
+
+ // 3.3.7
constants icct "Interface communication control" {
slumber = 0x6 "Slumber";
partial = 0x2 "Partial";
st 1 rw "Start";
};
- // 3.3.8
+ // 3.3.8
register tfd ro addr(b,0x20) "Task file data" {
_ 16 rsvd;
err 8 "Error";
- sts 8 "Status (task file status)";
+ bsy 1 ro "Indicates the interface is busy";
+ cs2 3 ro "Command specific";
+ drq 1 ro "Indicates a data transfer is requested";
+ cs1 2 ro "Command specific";
+ serr 1 ro "Indicates an error during the transfer.";
};
-
+
// 3.3.9
register sig ro addr(b,0x24) "Signature" {
lbah 8 "LBA high";
lbal 8 "LBA low";
sectors 8 "Sector count";
};
-
+
// 3.3.10
constants speed "Interface speed" {
gen1 = 0b0001 "Gen 1 (1.5 Gbps)";
spd 4 type(speed) "Current interface speed";
det 4 type(dets) "Device detection";
};
-
+
// 3.3.11
constants ipmall "Interface transitions" {
noipm = 0x0 "No interface restrictions";
// 3.3.14
register ci rw addr(b,0x38) "Command issue" type(uint32);
-
- // 3.3.15
+
+ // 3.3.15
register sntf addr(b,0x3c) "Serial ATA notification" {
_ 16 mbz;
pmn 16 rw1c "PMN notify";
};
-
+
// 3.3.16
register fbs addr(b,0x40) "FIS-based switching control" {
_ 12 mbz;
w 1 "Write";
a 1 "ATAPI";
cfl 5 "Command FIS length";
-
+
prdbc 32 "Physical region descriptor byte count";
-
- ctba 32 "Command table descriptor base address";
-
- ctbau 32 "Command table descriptor base address upper";
+
+ ctba 64 "Command table descriptor base address";
+
_ 32;
_ 32;
_ 32;
_ 9;
dbc 22 "Data byte count";
};
-
+
// 12.2
constants emtype "Enclosure message type" {
led = 0x0 "LED";
msize 8 "Message size";
_ 8;
};
-
+
// 12.2.1
constants emledstate "Enclosure LED state" {
off = 0b000 "LED shall be off";
failure VMKIT_CTRL_INVALID "Invalid frame capability passed for control structure",
failure VMKIT_ENDPOINT "Error setting monitor endpoint for dispatcher",
failure VMKIT_ENDPOINT_INVALID "Invalid monitor endpoint capability passed",
- failure VMKIT_VMX_VMFAIL_INVALID "The VMCS pointer is invalid",
+ failure VMKIT_VMX_VMFAIL_INVALID "The VMCS pointer is invalid",
failure VMKIT_VMX_VMFAIL_VALID "VMX instruction failed (VM-instruction error field = ErrorNumber)",
// Serial port errors
failure BIOS_CALL_FAILED "Unknown error returned from VBE BIOS call",
};
-// errors generated by ahcid and libahci
+// errors generated by lib/blk/ahci
errors ahcid AHCI_ERR_ {
+ failure PORT_INIT "Port initialization failed",
failure PORT_INVALID "Provided port id is not valid",
failure PORT_BUSY "Port has been opened elsewhere",
failure PORT_MISMATCH "Port is not opened by client",
failure ILLEGAL_ARGUMENT "Illegal argument in call",
};
+// errors generated by devif
+errors ahcid DEV_ERR_ {
+ failure NOT_INITIALIZED "Queue exists but could not be initialized.",
+ failure NOT_FOUND "Invalid queue requested, not found?",
+ failure ALREADY_CREATED "The queue specified has already been created.",
+ failure REGISTER_BUFFER "Unable to register the buffer with the driver.",
+ failure INVALID_BUFFER_ARGS "Invalid arguments for specified buffer.",
+ failure QUEUE_EMPTY "Nothing to dequeue.",
+ failure QUEUE_FULL "The queue is full.",
+};
+
errors sata SATA_ERR_ {
failure INVALID_TYPE "Unknown FIS type or invalid/unimplemented field for type",
};
failure SM_EXCLUSIVE_WS "BULK_SM: Exclusive waitset required per channel.",
failure NET_MAX_QUEUES "The number of maximum queues is reached",
failure NET_POOL_USED "The pool is already used over a no-copy channel.",
-
-
+
+
};
errors virtio VIRTIO_ERR_ {
failure QUEUE_INVALID "The selected queue does not exist",
failure QUEUE_BUSY "The queue is busy.",
failure BUFFER_SIZE "The buffer size is invalid.",
- failure BUFFER_STATE "The state of the buffer / buffer list is invalid",
+ failure BUFFER_STATE "The state of the buffer / buffer list is invalid",
failure ARG_INVALID "The given argument is invalid.",
failure NO_BUFFER "No buffer given, number of buffers is 0",
failure ALLOC_FULL "The allocator is already full",
failure BUFFER_USED "The buffer is already enqueued and used",
failure NO_DESC_AVAIL "There is no descriptor availabe",
failure DEQ_CHAIN "Not the entire chain could be dequeued",
- failure INVALID_RING_INDEX "The supplied index is not valid",
+ failure INVALID_RING_INDEX "The supplied index is not valid",
failure BLK_REQ_IOERR "The request ended in an IO error",
failure BLK_REQ_UNSUP "The request type was not supported",
};
+++ /dev/null
-/*
- * Copyright (c) 2011 ETH Zurich.
- * All rights reserved.
- *
- * This file is distributed under the terms in the attached LICENSE file.
- * If you do not find this file, copies can be found by writing to:
- * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
- */
-
-#ifndef _AHCI_H
-#define _AHCI_H
-
-#include <barrelfish/waitset.h>
-#include <barrelfish/idc.h>
-#include <barrelfish/event_mutex.h>
-#include <flounder/flounder.h>
-#include <ahci/ahci_util.h>
-#include <ahci/ahci_dma_pool.h>
-#include <dev/ata_identify_dev.h>
-
-struct ahci_binding;
-
-typedef void ahci_bind_continuation_fn(void *st, errval_t err,
- struct ahci_binding *_binding);
-typedef bool ahci_can_send_fn(struct ahci_binding *_binding);
-typedef errval_t ahci_register_send_fn(struct ahci_binding *_binding,
- struct waitset *ws, struct event_closure _continuation);
-typedef errval_t ahci_change_waitset_fn(struct ahci_binding *_binding,
- struct waitset *ws);
-typedef errval_t ahci_control_fn(struct ahci_binding *_binding,
- idc_control_t control);
-typedef void ahci_error_handler_fn(struct ahci_binding *_binding, errval_t err);
-
-/*
- * Message type signatures (receive)
- */
-typedef void ahci_command_completed_method_fn(struct ahci_binding *_binding,
- void *tag);
-
-/*
- * Receive VTable
- */
-struct ahci_rx_vtbl {
- ahci_command_completed_method_fn *command_completed;
-};
-
-/*
- * The binding structure
- */
-struct ahci_binding {
- /* user state */
- void *st;
-
- /* waitset for receive handlers and send continuations */
- struct waitset *waitset;
-
- /* Mutex for the use of user code. */
- /* Must be held before any operation where there is a possibility of */
- /* concurrent access to the same binding (eg. multiple threads, or */
- /* asynchronous event handlers that use the same binding object). */
- struct event_mutex mutex;
-
- /* returns true iff a message could currently be accepted by the binding */
- ahci_can_send_fn *can_send;
-
- /* register an event for when a message is likely to be able to be sent */
- ahci_register_send_fn *register_send;
-
- /* change the waitset used by a binding */
- ahci_change_waitset_fn *change_waitset;
-
- /* perform control operations */
- ahci_control_fn *control;
-
- /* error handler for async errors */
- ahci_error_handler_fn *error_handler;
-
- /* Message receive functions (filled in by user) */
- struct ahci_rx_vtbl rx_vtbl;
-
- /* Private state belonging to the binding implementation */
- uint8_t port_id;
- struct ahci_port_info port_info;
- struct waitset_chanstate register_chanstate;
- struct waitset_chanstate tx_cont_chanstate;
-
- uint8_t *identify_data;
- size_t identify_length;
- ata_identify_t identify;
-};
-
-errval_t ahci_issue_command(struct ahci_binding *_binding,
- struct event_closure _continuation, void *tag, uint8_t *fis,
- size_t fis_length, bool is_write, struct ahci_dma_region *buf,
- size_t buflen);
-
-errval_t ahci_close(struct ahci_binding *_binding,
- struct event_closure _continuation);
-
-errval_t ahci_init(uint8_t port, ahci_bind_continuation_fn *_continuation,
- void *st, struct waitset *waitset);
-
-#endif // _AHCI_H
+++ /dev/null
-/*
- * Copyright (c) 2011 ETH Zurich.
- * All rights reserved.
- *
- * This file is distributed under the terms in the attached LICENSE file.
- * If you do not find this file, copies can be found by writing to:
- * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
- */
-
-#ifndef __AHCI_DEFS_H
-#define __AHCI_DEFS_H
-
-#include <ahci/ahci.h>
-
-#endif
+++ /dev/null
-/*
- * Copyright (c) 2011 ETH Zurich.
- * All rights reserved.
- *
- * This file is distributed under the terms in the attached LICENSE file.
- * If you do not find this file, copies can be found by writing to:
- * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
- */
-
-#ifndef _AHCI_DMA_POOL_H
-#define _AHCI_DMA_POOL_H
-#include <string.h>
-
-struct ahci_dma_region {
- void *vaddr;
- genpaddr_t paddr;
- size_t size;
- size_t backing_region;
-};
-
-errval_t ahci_dma_pool_init(size_t pool_size);
-errval_t ahci_dma_region_alloc(size_t size, struct ahci_dma_region **retregion);
-errval_t ahci_dma_region_alloc_aligned(size_t size, size_t alignment_requirement, struct ahci_dma_region **retregion);
-errval_t ahci_dma_region_free(struct ahci_dma_region *region);
-
-static inline void *ahci_dma_region_copy_in(struct ahci_dma_region *region, const void *buf, genvaddr_t offset, size_t size) {
- void *dest = (char *)region->vaddr + offset;
- return memcpy(dest, buf, size);
-}
-static inline void *ahci_dma_region_copy_out(struct ahci_dma_region *region, void *buf, genvaddr_t offset, size_t size) {
- void *src_ = (char *)region->vaddr + offset;
- return memcpy(buf, src_, size);
-}
-
-#endif // _AHCI_DMA_POOL_H
+++ /dev/null
-/*
- * Copyright (c) 2011 ETH Zurich.
- * All rights reserved.
- *
- * This file is distributed under the terms in the attached LICENSE file.
- * If you do not find this file, copies can be found by writing to:
- * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
- */
-
-#ifndef _AHCI_UTIL_H
-#define _AHCI_UTIL_H
-
-#include <barrelfish/barrelfish.h>
-#include <ahci/ahci_dma_pool.h>
-#include <dev/ahci_port_dev.h>
-
-#define PORT_SIZE 0x80
-
-#define BLOCK_SIZE 512
-
-// The QEMU AHCI emulation only supports PRDs of size 512 bytes
-// PR_SIZE can be used together with AHCI_FIXED_PR_SIZE to enforce
-// PRs of fixed size specified in PR_SIZE
-// If not forced, PRs will be of arbitrary size with max length 4MB as
-// specified in the AHCI spec
-#define PR_SIZE 512
-#define MAX_PR_SIZE (128 * 1024)
-
-#define PRDT_OFFSET 0x80
-
-#ifndef CEIL_DIV
-#define CEIL_DIV(x, d) (((x) + ((d)-1)) / (d))
-#endif
-
-struct ahci_command_slot {
- struct ahci_dma_region *command_table;
- bool in_use;
- void *tag;
-};
-
-struct ahci_port_info {
- ahci_port_t port;
- void *mapped_vaddr;
- void *port_base;
- struct ahci_dma_region *command_list;
- struct ahci_dma_region *receive_fis;
- struct ahci_command_slot command_slots[32];
- uint32_t hba_capabilities;
- struct capref hba_cap;
-};
-
-errval_t ahci_port_alloc_dma_structs(ahci_port_t *port,
- struct ahci_dma_region **command_list,
- struct ahci_dma_region **receive_fis);
-
-#endif // _AHCI_UTIL_H
--- /dev/null
+/*
+ * Copyright (c) 2016, ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#ifndef _AHCI_
+#define _AHCI_
+
+#include <barrelfish/barrelfish.h>
+#include <pci/mem.h>
+
+struct ahci_disk;
+
+errval_t blk_ahci_init(struct device_mem* bar5, struct ahci_disk** out);
+errval_t blk_ahci_stop(struct ahci_disk* ad);
+
+#endif // _AHCI_
+++ /dev/null
-/*
- * Copyright (c) 2016 ETH Zurich.
- * All rights reserved.
- *
- * This file is distributed under the terms in the attached LICENSE file.
- * If you do not find this file, copies can be found by writing to:
- * ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
- */
-#ifndef DEVICE_QUEUE_INTERFACE_H_
-#define DEVICE_QUEUE_INTERFACE_H_ 1
-
-
-#include <barrelfish/barrelfish.h>
-
-typedef uint32_t regionid_t;
-typedef uint32_t bufferid_t;
-
-/**
- * Represent the device queue itself
- */
-struct device_queue {
-
- uint32_t queue_id;
-
- // name of the device
- char* device_name;
- // pointer to device queue state
- void* q_state;
-
- //TODO Other state needed ...
-
-};
-
-
-struct device_queue_buffer {
- // region id to which the buffer belongs
- regionid_t region_id;
- // id within the region
- bufferid_t buffer_id;
- // physical base address of the buffer
- lpaddr_t base;
- // length of the buffer
- size_t len;
-}
-
-/*
- * ===========================================================================
- * Device queue creation and destruction
- * ===========================================================================
- */
-
-
- /**
- * @brief creates a queue
- *
- * @param q Return pointer to the device_queue (handle)
- * @param device_name Device name of the device to which this queue belongs
- * (Driver itself is running in a separate process)
- * @param misc Anything you can think of that makes sense for the device
- * and its driver?
- *
- * @returns error on failure or SYS_ERR_OK on success
- */
-
-errval_t device_queue_create(struct device_queue **q,
- char* device_name,
- char* misc);
-
-
- /**
- * @brief destroys the device queue
- *
- * @param q The queue state to free (and the device queue to be
- shut down in the driver)
- *
- * @returns error on failure or SYS_ERR_OK on success
- */
-errval_t device_queue_destroy(struct device_queue *qp);
-
-
-/*
- * ===========================================================================
- * Datapath functions
- * ===========================================================================
- */
-
-/**
- * @brief enqueue a buffer into the device queue
- *
- * @param q The device queue to call the operation on
- * @param region_id Id of the memory region the buffer belongs to
- * @param base Physical address of the start of the enqueued buffer
- * @param lenght Lenght of the enqueued buffer
- * @param buffer_id The buffer id of the enqueue buffer (TODO only for
- * fixed size buffers?)
- * @param misc_flags Any other argument that makes sense to the device queue
- *
- * @returns error on failure or SYS_ERR_OK on success
- *
- */
-errval_t device_queue_enqueue(struct device_queue *q,
- regionid_t region_id,
- lpaddr_t base,
- size_t length,
- bufferid_t buffer_id,
- char* misc_flags);
-
-/**
- * @brief enqueue some memory into the device queue
- *
- * @param q The device queue to call the operation on
- * @param buf Buffer to enqueue (includes physical address, lenght,
- * region id and buffer id)
- * @param misc_flags Any other argument that makes sense to the device queue
- *
- * @returns error on failure or SYS_ERR_OK on success
- *
- */
-errval_t device_queue_enqueue(struct device_queue *q,
- struct device_queue_buffer* buf,
- char* misc_flags);
-
-
-/**
- * @brief dequeue a buffer from the device queue
- *
- * @param q The device queue to call the operation on
- * @param region_id Return pointer to the id of the memory
- * region the buffer belongs to
- * @param base Return pointer to the physical address of
- * the of the buffer
- * @param lenght Return pointer to the lenght of the dequeue buffer
- * @param buffer_id Return pointer to thehe buffer id of the dequeued buffer
- *
- * @returns error on failure or SYS_ERR_OK on success
- *
- */
-errval_t device_queue_dequeue(struct device_queue *q,
- regionid_t* region_id,
- lpaddr_t* base,
- size_t* length,
- bufferid_t* buffer_id);
-
-/**
- * @brief dequeue a buffer from the device queue
- *
- * @param q The device queue to call the operation on
- * @param buf Return pointer to the dequeued buffer
- *
- * @returns error on failure or SYS_ERR_OK on success
- *
- */
-errval_t device_queue_dequeue(struct device_queue *q,
- struct device_queue_buf** buf);
-/*
- * ===========================================================================
- * Control Path
- * ===========================================================================
- */
-
-/**
-* @brief Add a memory region that can be used as buffers to
-* the device queue
-*
-* @param q The device queue to call the operation on
-* @param cap A Capability for some memory
-* @param region_id Return pointer to a region id that is assigned
-* to the memory
-*
-* @returns error on failure or SYS_ERR_OK on success
-*
-*/
-errval_t device_queue_register(struct device_queue *q,
- struct capref cap,
- regionid_t* region_id);
-
-/**
-* @brief Remove a memory region
-*
-* @param q The device queue to call the operation on
-* @param region_id The region id to remove from the device
-* queues memory
-* @param cap The capability to the removed memory
-*
-* @returns error on failure or SYS_ERR_OK on success
-*
-*/
-errval_t device_queue_register(struct device_queue *q,
- regionid_t region_id,
- struct capref* cap);
-
-/**
-* @brief Send a notification about new buffers on the queue
-*
-* @param q The device queue to call the operation on
-*
-* @returns error on failure or SYS_ERR_OK on success
-*
-*/
-errval_t device_queue_sync(struct device_queue *q);
-
-/**
-* @brief Send a control message to the device queue
-*
-* @param q The device queue to call the operation on
-* @param ctrl A sting encoding the control message
-*
-* @returns error on failure or SYS_ERR_OK on success
-*
-*/
-errval_t device_queue_control(struct device_queue *q,
- char* ctrl);
-
-#endif /* DEVICE_QUEUE_INTERFACE_H_ */
--- /dev/null
+/*
+ * Copyright (c) 2016 ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+#ifndef DEVICE_QUEUE_INTERFACE_H_
+#define DEVICE_QUEUE_INTERFACE_H_ 1
+
+#include <barrelfish/barrelfish.h>
+
+typedef uint64_t regionid_t;
+typedef uint64_t bufferid_t;
+
+/*
+ * ===========================================================================
+ * Device queue creation and destruction
+ * ===========================================================================
+ */
+
+/**
+ * @brief creates a queue
+ * @returns error on failure or SYS_ERR_OK on success
+ */
+
+errval_t devq_create(void* st, char *device_name, uint64_t flags, void **queue);
+
+/**
+ * @brief destroys the device queue
+ *
+ * @param q The queue state to free (and the device queue to be
+ shut down in the driver)
+ *
+ * @returns error on failure or SYS_ERR_OK on success
+ */
+errval_t devq_destroy(void *queue);
+
+/*
+ * ===========================================================================
+ * Datapath functions
+ * ===========================================================================
+ */
+
+/**
+ * @brief enqueue a buffer into the device queue
+ *
+ * @param q The device queue to call the operation on
+ * @param region_id Id of the memory region the buffer belongs to
+ * @param base Physical address of the start of the enqueued buffer
+ * @param length Length of the enqueued buffer
+ * @param buffer_id The buffer id of the enqueue buffer (TODO only for
+ * fixed size buffers?)
+ * @param misc_flags Any other argument that makes sense to the device queue
+ *
+ * @returns error on failure or SYS_ERR_OK on success
+ *
+ */
+errval_t devq_enqueue(void *q, regionid_t region_id, lpaddr_t base,
+ size_t length, bufferid_t buffer_id, uint64_t flags);
+
+/**
+ * @brief dequeue a buffer from the device queue
+ *
+ * @param q The device queue to call the operation on
+ * @param region_id Return pointer to the id of the memory
+ * region the buffer belongs to
+ * @param base Return pointer to the physical address of
+ * the of the buffer
+ * @param length Return pointer to the length of the dequeue buffer
+ * @param buffer_id Return pointer to the buffer id of the dequeued buffer
+ *
+ * @returns error on failure or SYS_ERR_OK on success
+ *
+ */
+errval_t devq_dequeue(void *q, regionid_t *region_id, lpaddr_t *base,
+ size_t *length, bufferid_t *buffer_id);
+/*
+ * ===========================================================================
+ * Control Path
+ * ===========================================================================
+ */
+
+/**
+* @brief Add a memory region that can be used as buffers to
+* the device queue
+*
+* @param q The device queue to call the operation on
+* @param cap A Capability for some memory
+* @param region_id Return pointer to a region id that is assigned
+* to the memory
+*
+* @returns error on failure or SYS_ERR_OK on success
+*
+*/
+errval_t devq_register(void *q, struct capref cap, regionid_t *region_id);
+
+/**
+* @brief Remove a memory region
+*
+* @param q The device queue to call the operation on
+* @param region_id The region id to remove from the device
+* queues memory
+* @param cap The capability to the removed memory
+*
+* @returns error on failure or SYS_ERR_OK on success
+*
+*/
+errval_t devq_remove(void *q, regionid_t region_id);
+
+/**
+* @brief Send a notification about new buffers on the queue
+*
+* @param q The device queue to call the operation on
+*
+* @returns error on failure or SYS_ERR_OK on success
+*
+*/
+errval_t devq_sync(void *q);
+
+/**
+* @brief Send a control message to the device queue
+*
+* @param q The device queue to call the operation on
+* @param request A sting encoding the control message
+* @param value A value for the request.
+*
+* @returns error on failure or SYS_ERR_OK on success
+*
+*/
+errval_t devq_control(void *q, uint64_t request, uint64_t value);
+
+#endif /* DEVICE_QUEUE_INTERFACE_H_ */
#ifndef PCI_CLIENT_DEBUG_H
#define PCI_CLIENT_DEBUG_H
+#define PCI_LIB_DEBUG 1
+
#if defined(PCI_LIB_DEBUG) || defined(GLOBAL_DEBUG)
#define PCI_CLIENT_DEBUG(x...) printf("pci_client: " x)
#else
+++ /dev/null
-
---------------------------------------------------------------------------
--- Copyright (c) 2007-2012, ETH Zurich.
--- All rights reserved.
---
--- This file is distributed under the terms in the attached LICENSE file.
--- If you do not find this file, copies can be found by writing to:
--- ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
---
--- Hakefile for lib/ahci
---
---------------------------------------------------------------------------
-
-[ build library { target = "ahci",
- cFiles = [ "ahci.c", "ahci_util.c", "sata_fis.c", "ahci_dma_pool.c" ],
- flounderDefs = [ "ata_rw28" ],
- flounderBindings = [ "ahci_mgmt", "ata_rw28" ],
- flounderExtraBindings = [ ("ahci_mgmt", ["rpcclient"]),
- ("ata_rw28", ["ahci", "rpcclient"]) ],
- mackerelDevices = [ "ata_identify", "ahci_port", "ahci_hba" ],
- addLibraries = [ ]
- },
- build library { target = "ahci_vsic",
- cFiles = [ "ahci.c", "ahci_util.c", "sata_fis.c", "ahci_dma_pool.c", "storage_vsic.c" ],
- flounderDefs = [ "ata_rw28" ],
- flounderBindings = [ "ahci_mgmt", "ata_rw28" ],
- flounderExtraBindings = [ ("ahci_mgmt", ["rpcclient"]),
- ("ata_rw28", ["ahci", "rpcclient"]) ],
- mackerelDevices = [ "ata_identify", "ahci_port", "ahci_hba" ],
- addLibraries = [ ]
- }
-]
+++ /dev/null
-/*
- * Copyright (c) 2011, 2012, ETH Zurich.
- * All rights reserved.
- *
- * This file is distributed under the terms in the attached LICENSE file.
- * If you do not find this file, copies can be found by writing to:
- * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
- */
-
-#include <barrelfish/barrelfish.h>
-#include <barrelfish/waitset.h>
-#include <barrelfish/waitset_chan.h>
-#include <barrelfish/nameservice_client.h>
-#include <if/ahci_mgmt_defs.h>
-#include <dev/ahci_hba_dev.h> // this is used to parse HBA capabilities
-#include <dev/ahci_port_dev.h>
-#include <ahci/ahci.h>
-#include <ahci/ahci_dma_pool.h>
-#include <ahci/ahci_util.h>
-#include <string.h>
-#include "ahci_debug.h"
-#include "ahci_internal.h"
-
-static struct ahci_mgmt_binding *mgmt_binding;
-
-struct bind_st { // this is to be able to pass 3 params as user state
- size_t num_ports;
- struct ahci_binding **ahci_binding;
- ahci_bind_continuation_fn *cont;
- void *st;
- uint8_t port; // only valid in open and identify cb
-};
-
-struct mgmt_close_call_st
-{
- struct ahci_binding *binding;
- struct event_closure continuation;
-};
-
-static void ahci_mgmt_identify_response_cb(struct ahci_mgmt_binding *b,
- uint8_t *identify_data, size_t data_len)
-{
- struct bind_st *bst = b->st;
- struct ahci_binding *ahci_binding = bst->ahci_binding[bst->port];
-
- if (!ahci_binding) {
- AHCI_DEBUG("got identify data for unbound port\n");
- return;
- }
-
- bool has_identify = ahci_binding->identify_data;
- if (has_identify) {
- free(ahci_binding->identify_data);
- }
- ahci_binding->identify_data = identify_data;
- ahci_binding->identify_length = data_len;
- ata_identify_initialize(&ahci_binding->identify,
- (void*)ahci_binding->identify_data);
-
- if (!has_identify) {
- struct ahci_port_info *port = &ahci_binding->port_info;
-
- // enable rFIS area and start running commands
- ahci_port_cmd_t cmd = ahci_port_cmd_rd(&port->port);
- cmd = ahci_port_cmd_fre_insert(cmd, 1);
- cmd = ahci_port_cmd_st_insert(cmd, 1);
- ahci_port_cmd_wr(&port->port, cmd);
-
- // enable all interrupts of this port
- AHCI_DEBUG("enabling all port interrupts\n");
- ahci_port_ie_wr(&port->port, -1);
-
- // fire off the original binding callback
- if (bst->cont) {
- bst->cont(bst->st, SYS_ERR_OK, ahci_binding);
- }
- }
-
- AHCI_DEBUG("ahci_mgmt_identify_response_cb: exiting\n");
-}
-
-static void ahci_mgmt_open_cb(struct ahci_mgmt_binding *b, errval_t status,
- struct capref controller_mem, uint64_t offset, uint32_t capabilities)
-{
- AHCI_DEBUG("open cb\n");
- struct bind_st *bst = b->st;
- errval_t err = status, cleanup_err;
- struct ahci_binding *ahci_binding;
-
- if (bst->num_ports == 0) {
- // init bind_st properly
- ahci_binding = bst->ahci_binding[0];
- bst->num_ports = ahci_hba_cap_np_extract(capabilities) + 1;
- void *tmp = calloc(bst->num_ports, sizeof(struct ahci_binding *));
- if (tmp == NULL) {
- err = LIB_ERR_MALLOC_FAIL;
- goto error_status;
- }
- bst->ahci_binding = tmp;
- bst->ahci_binding[bst->port] = ahci_binding;
- }
- else {
- // get correct ahci binding
- ahci_binding = bst->ahci_binding[bst->port];
- }
-
- struct ahci_port_info *port = &ahci_binding->port_info;
-
- // check status code from management daemon
- if (err_is_fail(status)) {
- DEBUG_ERR(status, "failed to open port: '%s'", err_getstring(status));
- goto error_status;
- }
-
- // HBA caps
- port->hba_capabilities = capabilities;
-
- // init port
- AHCI_DEBUG("mapping port in our address space\n");
- // map device in our address space
- err = vspace_map_one_frame_attr(&port->mapped_vaddr, offset + PORT_SIZE,
- controller_mem, VREGION_FLAGS_READ_WRITE_NOCACHE, NULL, NULL);
- if (err_is_fail(err)) {
- DEBUG_ERR(err, "vspace_map_one_frame failed: '%s'", err_getstring(err));
- goto error_map_controller;
- }
- port->port_base = (char *)port->mapped_vaddr + offset;
-
- // store controller mem cap in port
- port->hba_cap = controller_mem;
-
- // setup mackerel struct for port
- ahci_port_initialize(&port->port, port->port_base);
- err = ahci_port_alloc_dma_structs(&port->port,
- &port->command_list, &port->receive_fis);
- if (err_is_fail(err)) {
- DEBUG_ERR(err, "ahci_port_alloc_dma_structs failed");
- goto error_dma_allocate;
- }
- AHCI_DEBUG("enabling rFIS area + start running commands\n");
-
- // fetch identify data from management daemon
- err = ahci_mgmt_identify_call__tx(b, NOP_CONT, ahci_binding->port_id);
- if (err_is_fail(err)) {
- DEBUG_ERR(err, "failed to send identify_call to ahci_mgmt");
- goto error_identify_send;
- }
-
- goto end;
-
-error_identify_send:
- // free port dma region
- ahci_port_free_dma_structs(port);
-
-error_dma_allocate:
- // cleanup mapping of controller mem
- cleanup_err = vspace_unmap(port->mapped_vaddr);
- if (err_is_fail(cleanup_err)) {
- USER_PANIC_ERR(cleanup_err, "vspace_unmap failed");
- }
-
-error_map_controller:
- // destroy controller mem cap
- cleanup_err = cap_destroy(controller_mem);
- if (err_is_fail(cleanup_err)) {
- USER_PANIC_ERR(cleanup_err, "cap_destroy failed");
- }
-
-error_status:
- // cleanup semi-initialized libahci binding
- waitset_chanstate_destroy(&ahci_binding->register_chanstate);
- waitset_chanstate_destroy(&ahci_binding->tx_cont_chanstate);
-
- free(ahci_binding);
- bst->ahci_binding[bst->port] = NULL;
-
- if (bst->cont) {
- bst->cont(bst->st, err, NULL);
- }
-
- // free user state
- free(bst);
-
-end:
- AHCI_DEBUG("ahci_mgmt_open_cb: exiting\n");
-}
-
-static void ahci_mgmt_command_completed_cb(struct ahci_mgmt_binding *b,
- uint8_t port_id, uint32_t interrupt_status)
-{
- AHCI_DEBUG("got command completed message\n");
- struct bind_st *bst = b->st;
- struct ahci_port_info *port_info = &bst->ahci_binding[port_id]->port_info;
-
- if (interrupt_status == 0) {
- // ghost irq, ignore. (although this should not even get to here)
- return;
- }
-
- AHCI_DEBUG("interrupt status = 0x%"PRIx32"\n", interrupt_status);
-
- uint8_t tfes, ifs, hbfs, hbds;
- if ((tfes = ahci_port_is_tfes_extract(interrupt_status)) ||
- (ifs = ahci_port_is_ifs_extract(interrupt_status)) ||
- (hbfs = ahci_port_is_hbfs_extract(interrupt_status)) ||
- (hbds = ahci_port_is_hbds_extract(interrupt_status)))
- {
- char buf[4096];
- ahci_port_serr_t errstate = ahci_port_serr_rd(&port_info->port);
- ahci_port_tfd_t tfd = ahci_port_tfd_rd(&port_info->port);
- ahci_dump_rfis(port_info);
- ahci_port_serr_prtval(buf, 4096, errstate);
- puts(buf);
- printf("task file data:\n");
- ahci_port_tfd_prtval(buf, 4096, tfd);
- puts(buf);
- assert(!"error");
- }
- else if (ahci_port_is_infs_extract(interrupt_status)) {
- char buf[4096];
- ahci_port_serr_t errstate = ahci_port_serr_rd(&port_info->port);
- ahci_port_serr_prtval(buf, 4096, errstate);
- puts(buf);
- assert(!"non-fatal error");
- }
-
- uint32_t ci = ahci_port_ci_rd(&port_info->port);
-
- // which commands have been completed?
- for (int i = 0; i < 32; i++) {
- if (!port_info->command_slots[i].in_use || (ci & (1<<i))) {
- continue; // skip slots we didn't use or that have not been processed yet
- }
-
- // free command table
- AHCI_DEBUG("freeing command table for slot %d (in_use=%d): %p\n",
- i, port_info->command_slots[i].in_use,
- port_info->command_slots[i].command_table);
- ahci_dma_region_free(port_info->command_slots[i].command_table);
- port_info->command_slots[i].command_table = 0;
-
- void *tag = port_info->command_slots[i].tag;
- port_info->command_slots[i].tag = 0;
- port_info->command_slots[i].in_use = false;
-
- AHCI_DEBUG("dispatching to user level\n");
- // FIXME: does this make sense? will we even return to here in the near future?
- bst->ahci_binding[port_id]->rx_vtbl.command_completed(bst->ahci_binding[port_id], tag);
- AHCI_DEBUG("return from user level\n");
- }
-
- AHCI_DEBUG("cc_cb exiting\n");
-}
-
-static void ahci_mgmt_close_cb(struct ahci_mgmt_binding *_binding, errval_t status)
-{
- AHCI_DEBUG("mgmt close cb\n");
- if (err_is_fail(status)) {
- DEBUG_ERR(status, "close callback");
- }
- struct bind_st *bst = _binding->st;
- struct mgmt_close_call_st *st = bst->st;
- if (st->continuation.handler != NULL) {
- waitset_chan_trigger_closure(st->binding->waitset,
- &st->binding->tx_cont_chanstate, st->continuation);
- }
- free(st->binding->identify_data);
- free(st->binding);
- free(st);
-}
-
-static void ahci_mgmt_bind_cb(void *st, errval_t err, struct ahci_mgmt_binding *b)
-{
- if (err_is_fail(err)) {
- USER_PANIC_ERR(err, "ahci_mgmt bind failed in callback");
- }
-
- struct bind_st *bst = st;
-
- // set up our binding to the mgmt daemon
- mgmt_binding = b;
- b->rx_vtbl.open_response = ahci_mgmt_open_cb;
- b->rx_vtbl.command_completed = ahci_mgmt_command_completed_cb;
- b->rx_vtbl.close_response = ahci_mgmt_close_cb;
- b->rx_vtbl.identify_response = ahci_mgmt_identify_response_cb;
-
- b->st = bst;
-
- // try to open the port
- ahci_mgmt_open_call__tx(b, NOP_CONT, bst->port);
-}
-
-static bool ahci_can_send(struct ahci_binding *_binding)
-{
- return ahci_find_free_command_slot(&_binding->port_info) != -1;
-}
-
-static errval_t ahci_register_send(struct ahci_binding *_binding,
- struct waitset *ws, struct event_closure _continuation)
-{
- if (ahci_can_send(_binding)) {
- return waitset_chan_trigger_closure(ws,
- &(_binding->register_chanstate), _continuation);
- }
- else {
- return waitset_chan_register(ws,
- &(_binding->register_chanstate), _continuation);
- }
-}
-
-static errval_t ahci_change_waitset(struct ahci_binding *_binding, struct waitset *ws)
-{
- AHCI_DEBUG("change waitset, ws = %p\n", ws);
- // TODO: re-register somewhere?
- _binding->waitset = ws;
- mgmt_binding->change_waitset(mgmt_binding, ws);
-
- return SYS_ERR_OK;
-}
-
-static errval_t ahci_control(struct ahci_binding *_binding, idc_control_t control)
-{
- // TODO: implement
- return SYS_ERR_OK;
-}
-
-static void ahci_error_handler(struct ahci_binding *_binding, errval_t err)
-{
- DEBUG_ERR(err, "Asynchronous in ahci messaging. Aborting.");
- abort();
-}
-
-errval_t ahci_init(uint8_t port, ahci_bind_continuation_fn *_continuation,
- void *st, struct waitset *waitset)
-{
- AHCI_DEBUG("ahci_init: waitset = %p\n", waitset);
- errval_t err;
- iref_t iref;
-
- // setup binding struct
- struct ahci_binding *binding = calloc(1, sizeof(struct ahci_binding));
- binding->st = NULL;
- binding->waitset = waitset;
- event_mutex_init(&binding->mutex, waitset);
- /* set up management function pointers */
- binding->can_send = ahci_can_send;
- binding->register_send = ahci_register_send;
- binding->change_waitset = ahci_change_waitset;
- binding->control = ahci_control;
- binding->error_handler = ahci_error_handler;
- memset(&binding->rx_vtbl, 0, sizeof(binding->rx_vtbl));
- waitset_chanstate_init(&binding->register_chanstate, CHANTYPE_AHCI);
- waitset_chanstate_init(&binding->tx_cont_chanstate, CHANTYPE_AHCI);
-
- //binding->st = st;
- binding->port_id = port;
-
- // setup binding to ahcid and init dma pool
- if (mgmt_binding == NULL) {
- err = nameservice_blocking_lookup("ahcid", &iref);
- if (err_is_fail(err)) {
- DEBUG_ERR(err, "nameservice_blocking_lookup for ahcid");
- return err; // FIXME
- }
-
- // couple continuation and binding
- struct bind_st *bst = malloc(sizeof(struct bind_st));
- assert(bst != NULL);
- bst->cont = _continuation;
- // temp; used as flag in open_cb
- bst->num_ports = 0;
- // temp; realloc as soon as we know num_ports
- bst->ahci_binding = malloc(sizeof(struct ahci_binding*));
- bst->ahci_binding[0] = binding;
- bst->port = port;
- bst->st = st;
-
- // init dma pool to 1M
- ahci_dma_pool_init(1024 * 1024);
-
- err = ahci_mgmt_bind(iref, ahci_mgmt_bind_cb, bst, waitset,
- IDC_BIND_FLAG_RPC_CAP_TRANSFER);
-
- if (err_is_fail(err)) {
- DEBUG_ERR(err, "ahci_mgmt bind failed");
- return err; // FIXME
- }
- }
- else {
- // change mgmt binding waitset to supplied waitset
- struct bind_st *bst = mgmt_binding->st;
- bst->cont = _continuation;
- bst->ahci_binding[port] = binding;
- bst->port = port;
- bst->st = st;
-
- // try to open the port
- mgmt_binding->change_waitset(mgmt_binding, waitset);
- ahci_mgmt_open_call__tx(mgmt_binding, NOP_CONT, binding->port_id);
- }
-
- AHCI_DEBUG("ahci_init: exiting\n");
- return SYS_ERR_OK;
-}
-
-errval_t ahci_issue_command(struct ahci_binding *_binding,
- struct event_closure _continuation, void *tag, uint8_t *fis,
- size_t fis_length, bool is_write, struct ahci_dma_region *buf, size_t buflen)
-{
- errval_t err = 0;
-
- AHCI_DEBUG("entering ahci_issue_command: tag = %p; buf = %p; buflen = %zd\n",
- tag, buf, buflen);
-
- struct ahci_port_info *port = &_binding->port_info;
- int command;
- AHCI_DEBUG("ahci_issue_command: fis_length = %zd\n", fis_length);
- size_t num_prds = 0;
- if (buf) {
- assert((buflen & 1) == 0); // force even byte count
-#ifdef AHCI_FIXED_PR_SIZE
- num_prds = CEIL_DIV(buflen, PR_SIZE);
-#else
- num_prds = CEIL_DIV(buflen, MAX_PR_SIZE);
-#endif
- }
- err = ahci_setup_command(&command, port, fis, fis_length, num_prds, is_write);
- if (err_is_fail(err)) {
- DEBUG_ERR(err, "ahci_setup_command failed");
- return err;
- }
-
- // save tag
- port->command_slots[command].tag = tag;
-
- if (buf) {
- err = ahci_add_physical_regions(port, command, buf, buflen);
- if (err_is_fail(err)) {
- DEBUG_ERR(err, "ahci_add_physical_regions failed");
- return err;
- }
- }
-
- // issue command
- ahci_port_ci_wr(&port->port, (1<<command));
-
- AHCI_DEBUG("ahci_load_fis: calling user continuation\n");
- if (_continuation.handler != NULL) {
- waitset_chan_trigger_closure(_binding->waitset,
- &_binding->tx_cont_chanstate, _continuation);
- }
- AHCI_DEBUG("ahci_load_fis: exiting\n");
- return SYS_ERR_OK;
-}
-
-errval_t ahci_close(struct ahci_binding *_binding, struct event_closure _continuation)
-{
- AHCI_DEBUG("ahci_close: callback = %p\n", _continuation.handler);
- struct mgmt_close_call_st *st = calloc(1, sizeof(struct mgmt_close_call_st));
- if (!st) {
- return LIB_ERR_MALLOC_FAIL;
- }
- st->binding = _binding;
- st->continuation = _continuation;
- struct bind_st *bst = mgmt_binding->st;
- bst->st = st;
- // unmap controller memory
- errval_t err = cap_destroy(_binding->port_info.hba_cap);
- if (err_is_fail(err)) {
- printf("cap_destroy: %s (%"PRIuERRV")\n", err_getstring(err), err);
- }
- ahci_mgmt_close_call__tx(mgmt_binding, NOP_CONT, _binding->port_id);
-
- return SYS_ERR_OK;
-}
+++ /dev/null
-/*
- * Copyright (c) 2011 ETH Zurich.
- * All rights reserved.
- *
- * This file is distributed under the terms in the attached LICENSE file.
- * If you do not find this file, copies can be found by writing to:
- * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
- */
-
-#ifndef AHCI_DEBUG_H_
-#define AHCI_DEBUG_H_
-
-#include <stdio.h>
-
-/*****************************************************************
- * Debug printer:
- *****************************************************************/
-
-#if defined(AHCI_LIB_DEBUG) || defined(GLOBAL_DEBUG)
-#define AHCI_DEBUG(x...) printf("ahci: " x)
-#else
-#define AHCI_DEBUG(x...) ((void)0)
-#endif
-#if defined(AHCI_LIB_DEBUG) || defined(GLOBAL_DEBUG)
-#define AHCI_TRACE_ENTER0() AHCI_TRACE_ENTER(" ")
-#define AHCI_TRACE_ENTER(x...) do { \
- printf("ahci: entering %s. ", __FUNCTION__); \
- printf(x); \
- printf("\n"); \
-} while(0)
-#else
-#define AHCI_TRACE_ENTER0() ((void)0)
-#define AHCI_TRACE_ENTER(x...) ((void)0)
-#endif
-
-#endif // AHCI_DEBUG_H_
+++ /dev/null
-/*
- * Copyright (c) 2011 ETH Zurich.
- * All rights reserved.
- *
- * This file is distributed under the terms in the attached LICENSE file.
- * If you do not find this file, copies can be found by writing to:
- * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
- */
-
-#include <barrelfish/barrelfish.h>
-#include <ahci/ahci_dma_pool.h>
-#include <errors/errno.h>
-#include <string.h>
-#include <sys/types.h> /* for ssize_t */
-#include "ahci_debug.h"
-
-#define EIGHT_MEG 8388608
-
-// meta-data structure for the dma pool
-struct dma_pool {
- // size of the pool in bytes
- size_t size;
-
- // number of allocated backing memory regions
- size_t addr_count;
-
- // number of allocated slots for backing memory regions
- size_t addr_entries;
-
- // virtual addresses of backing memory regions
- // (valid for indices 0 .. addr_count - 1)
- void **virt_addrs;
-
- // physical addresses of backing memory regions
- // (valid for indices 0 .. addr_count - 1)
- genpaddr_t *phys_addrs;
-
- // frame caprefs for backing memory regions
- // (valid for indices 0 .. addr_count - 1)
- struct capref *frame_caps;
-
- // void* pointing to the first free chunk in backing
- // memory regions (only valid for indices 0 .. addr_count - 1)
- struct free **first_free;
-};
-
-// free list element (contained in the first bytes of a free chunk itself)
-struct free {
- // pointers to previous and next free chunks
- struct free *next, *prev;
-
- // size of this chunk
- size_t size;
-
- // the index of the backing region of memory
- size_t backing_region;
-};
-
-
-static struct dma_pool pool;
-static struct free *first_free = NULL, *last_free = NULL;
-
-/**
- * This function grows the address arrays in the pool metadata
- * \return LIB_ERR_MALLOC_FAIL when a realloc fails, SYS_ERR_OK else.
- */
-static errval_t
-grow_vp_arrays(void)
-{
- AHCI_TRACE_ENTER0();
- if (pool.addr_entries == 0) { // initial alloc
- pool.virt_addrs = calloc(8, sizeof(void *));
- if (pool.virt_addrs == NULL) {
- return LIB_ERR_MALLOC_FAIL;
- }
- pool.phys_addrs = calloc(8, sizeof(genpaddr_t));
- if (pool.phys_addrs == NULL) {
- return LIB_ERR_MALLOC_FAIL;
- }
- pool.frame_caps = calloc(8, sizeof(struct capref));
- if (pool.frame_caps == NULL) {
- return LIB_ERR_MALLOC_FAIL;
- }
- pool.first_free = calloc(8, sizeof(struct free *));
- if (pool.first_free == NULL) {
- return LIB_ERR_MALLOC_FAIL;
- }
- pool.addr_entries = 8;
- }
- else { // double size
- size_t new_size = pool.addr_entries * 2;
- void **newv;
- struct capref *newc;
- genpaddr_t *newp;
- struct free **newf;
- newv = realloc(pool.virt_addrs, new_size*sizeof(void *));
- if (newv == NULL) {
- AHCI_DEBUG("realloc failed for pool.virt_addrs");
- return LIB_ERR_MALLOC_FAIL;
- }
- memset(newv+pool.addr_entries, 0, pool.addr_entries);
- newp = realloc(pool.phys_addrs, new_size*sizeof(genpaddr_t));
- if (newp == NULL) {
- AHCI_DEBUG("realloc failed for pool.phys_addrs");
- return LIB_ERR_MALLOC_FAIL;
- }
- memset(newp+pool.addr_entries, 0, pool.addr_entries);
- newc = realloc(pool.frame_caps, new_size*sizeof(struct capref));
- if (newc == NULL) {
- AHCI_DEBUG("realloc failed for pool.frame_caps\n");
- return LIB_ERR_MALLOC_FAIL;
- }
- memset(newc+pool.addr_entries, 0, pool.addr_entries);
- newf = realloc(pool.first_free, new_size*sizeof(struct free *));
- if (newf == NULL) {
- AHCI_DEBUG("realloc failed for pool.first_free");
- return LIB_ERR_MALLOC_FAIL;
- }
- memset(newf+pool.addr_entries, 0, pool.addr_entries);
- pool.virt_addrs = newv;
- pool.phys_addrs = newp;
- pool.frame_caps = newc;
- pool.first_free = newf;
- pool.addr_entries = new_size;
- }
- return SYS_ERR_OK;
-}
-
-/**
- * Increase dma pool size to new_pool_size, growing
- * address arrays if necessary.
- */
-static errval_t
-grow_dma_pool(size_t new_pool_size)
-{
- AHCI_TRACE_ENTER("new pool size = %zd", new_pool_size);
- // determine additional pool size (using ssize_t temp variable in order to
- // detect requests that would shrink the pool).
- ssize_t temp_pool_size = new_pool_size;
- if (pool.size != 0) {
- temp_pool_size -= pool.size;
- }
-
- // return if pool already big enough
- if (temp_pool_size <= 0) {
- return SYS_ERR_OK;
- }
-
- // from here: temp_pool_size > 0
- if (temp_pool_size <= BASE_PAGE_SIZE) {
- temp_pool_size = BASE_PAGE_SIZE;
- }
-
- // allocate and map memory
- struct capref frame;
- struct frame_identity frameid;
- errval_t err = SYS_ERR_OK;
- void *va;
- size_t retsize;
- // allocate, map and store frame(s)
- do {
- // allocate frame
- err = frame_alloc(&frame, temp_pool_size, &retsize);
- if (err_is_fail(err)) {
- break;
- }
- // map frame
- err = vspace_map_one_frame_attr(&va, retsize, frame,
- VREGION_FLAGS_READ_WRITE_NOCACHE, NULL, NULL);
- if (err_is_fail(err)) {
- break;
- }
- // get frame phys addr
- err = invoke_frame_identify(frame, &frameid);
- if (err_is_fail(err)) {
- break;
- }
-
- // grow arrays if needed
- if (pool.addr_count == pool.addr_entries) {
- err = grow_vp_arrays();
- if (err_is_fail(err)) {
- break;
- }
- }
-
- // store new frame in pool
- pool.size += retsize;
- pool.virt_addrs[pool.addr_count] = va;
- pool.phys_addrs[pool.addr_count] = frameid.base;
- pool.frame_caps[pool.addr_count] = frame;
- // update free list
- struct free *f = (struct free *)va;
- pool.first_free[pool.addr_count] = f;
- f->size = retsize;
- f->backing_region = pool.addr_count;
- f->prev = last_free;
- if (first_free == NULL) { // first region
- first_free = last_free = f;
- } else {
- last_free->next = f;
- }
- f->next = NULL;
- pool.addr_count += 1;
- temp_pool_size -= retsize;
- } while(temp_pool_size > 0);
-
- return err;
-}
-
-/**
- * Init DMA pool
- * \param pool_size Initial pool size in bytes
- */
-errval_t
-ahci_dma_pool_init(size_t pool_size)
-{
- AHCI_TRACE_ENTER("pool size = %zd", pool_size);
- // round pool_size up to page size
- pool_size = ROUND_UP(pool_size, BASE_PAGE_SIZE);
- return grow_dma_pool(pool_size);
-}
-
-static void
-remove_from_free_list(struct free *f)
-{
- if (f == first_free && f == last_free) {
- // only remaining free chunk, clear free list
- first_free = last_free = NULL;
- pool.first_free[f->backing_region] = NULL;
- } else if (f == first_free) {
- // we were first free, f->next != NULL, make f->next first_free
- first_free = f->next;
- first_free->prev = NULL;
- } else if (f == last_free) {
- // we were last free, f->prev != NULL, make f->prev last free
- last_free = f->prev;
- last_free->next = NULL;
- } else {
- // we are somewhere in the middle, remove
- f->prev->next = f->next;
- f->next->prev = f->prev;
- }
-
- if (f == pool.first_free[f->backing_region]) {
- if (f->next && f->next->backing_region == f->backing_region) {
- // we were the first free in our backing region, and there is
- // another free chunk in the same region: make that chunk first_free
- // in our backing region
- pool.first_free[f->backing_region] = f->next;
- }
- else {
- // no other free chunks in our backing region, clear first_free
- pool.first_free[f->backing_region] = NULL;
- }
- }
-}
-
-/**
- * Build a struct ahci_dma_region using the end of the free chunk pointed to
- * by f ensuring that the resulting address for the return region is aligned to
- * alignment_requirement.
- * Note: All free chunk addresses should be aligned to at least 512 bytes.
- */
-static struct ahci_dma_region*
-get_region(struct free *f, size_t size, size_t alignment_requirement)
-{
- AHCI_TRACE_ENTER("f = %p; size = %zd; alignment = 0x%zx",
- f, size, alignment_requirement);
-
- struct ahci_dma_region *r = calloc(1, sizeof(struct ahci_dma_region));
- if (r == NULL) {
- return NULL;
- }
-
- // set the backing region in the new ahci_dma_region
- r->backing_region = f->backing_region;
- AHCI_DEBUG("backing region = %zd\n", f->backing_region);
-
- // check that remaining free chunk is at least 512 bytes large.
- // If that is not the case just use the whole chunk.
- ssize_t rem = 0;
- if (f->size > size) {
- rem = f->size - size;
- if (rem < 512) {
- rem = 0;
- size = f->size;
- }
- }
- AHCI_DEBUG("rem: %zd; size: %zd; f->size: %zd\n", rem, size, f->size);
-
- // calculate the aligned virtual address of the new ahci_dma_region
- uintptr_t vaddr_unaligned = ((uintptr_t) f) + rem;
- AHCI_DEBUG("vaddr_unaligned = 0x%zx\n", vaddr_unaligned);
- uintptr_t vaddr = vaddr_unaligned & ~(alignment_requirement-1);
-
- // sanity check the aligned virtual address, return error if it is
- // smaller than the address of the free chunk.
- //
- // This can happen when you have a merged free chunk which was created from
- // the following two (constructed) chunks: f1=0x600, f1->size=0x200 and
- // f2=0x800, f2->size=0x200 --(merge)--> f=0x600, f->size=0x400
- // Now suppose we have a request for size=0x400 and alignment_requirement=0x400:
- // on first glance `f' seems to match that request, but after aligning the address
- // vaddr will be smaller than f and thus f cannot fulfil this request.
- if (vaddr < (uintptr_t)f) {
- AHCI_DEBUG("too small\n");
- free(r);
- return (void*)-1;
- }
- AHCI_DEBUG("vaddr = 0x%zx\n", vaddr);
-
- // recalculate the size of the ahci_dma_region and the remaining chunk size
- ptrdiff_t addr_diff = vaddr_unaligned - vaddr;
- size += addr_diff;
- // here rem should either remain >= 512 or become 0
- rem -= addr_diff;
-
- AHCI_DEBUG("rem: %zd; size: %zd; f->size: %zd\n", rem, size, f->size);
-
- // calculate the offset into the backing region
- ptrdiff_t offset = vaddr - ((uintptr_t) pool.virt_addrs[f->backing_region]);
- AHCI_DEBUG("offset = 0x%zx\n", offset);
- // set the remaining fields in the ahci_dma_region
- r->vaddr = (void *) vaddr;
- r->paddr = pool.phys_addrs[f->backing_region] + offset;
- AHCI_DEBUG("paddr = 0x%zx\n", r->paddr);
- r->size = size;
-
- if (rem == 0) {
- // no remaining space in this free chunk, need to delete from free list
- remove_from_free_list(f);
- }
- else {
- // adjust size of free chunk
- f->size = rem;
- }
-
- return r;
-}
-
-/**
- * Allocate a dma region aligned to a multiple of alignment_requirement.
- * \param size the minimum size of the requested region
- * \param alignment_requirement align address and size of the chunk to a
- * multiple of this number
- * \param retregion the newly allocated ahci_dma_region
- */
-errval_t
-ahci_dma_region_alloc_aligned(size_t size, size_t alignment_requirement,
- struct ahci_dma_region **retregion)
-{
- AHCI_DEBUG("size = %zd; alignment_requirement = 0x%zx; retregion = %p\n",
- size, alignment_requirement, retregion);
-
- if (retregion == NULL) {
- return AHCI_ERR_ILLEGAL_ARGUMENT;
- }
-
- // set alignment_requirement (and therefore size) of request
- // to at least 512
- if (alignment_requirement < 512) {
- alignment_requirement = 512;
- }
-
- // round alloc request to alignment_requirement bytes
- size = ROUND_UP(size, alignment_requirement);
-
- // iterate over free list
- struct free *f;
- f = first_free;
-restart:
- for (; f; f = f->next) {
- AHCI_DEBUG("f = %p; f->size = %zd\n", f, f->size);
- if (f->size >= size) { // found sufficiently big free chunk
- AHCI_DEBUG("found free chunk\n");
- *retregion = get_region(f, size, alignment_requirement);
- if (retregion == (void*)-1) {
- // when aligning the address the address became smaller than
- // the address of the free region. Search more.
- continue;
- }
- AHCI_DEBUG("retregion = %p\n", *retregion);
- // if *retregion is NULL here, get_region() failed.
- if (*retregion == NULL) {
- return LIB_ERR_MALLOC_FAIL;
- }
- return SYS_ERR_OK;
- }
- }
- AHCI_DEBUG("did not find sufficiently large free region, trying to grow pool.\n");
- // save index of first free slot in pool
- size_t first_new = pool.addr_count;
-
- // grow pool by at least the size of the request.
- // This prevents that we reach this code again, because in the next search
- // we will find the newly allocated memory with size >= request size
- errval_t err = grow_dma_pool(pool.size + size);
- if (err_is_fail(err)) {
- return err;
- }
-
- // continue searching for sufficiently large regions starting where we
- // allocated new memory just now
- f = pool.first_free[first_new];
- goto restart;
-}
-
-/**
- * Allocate a new dma region with alignment requirement 512
- * \param size the minimum size of the requested region
- * \param alignment_requirement align address and size of the chunk to a
- * multiple of this number
- * \param retregion the newly allocated ahci_dma_region
- */
-errval_t
-ahci_dma_region_alloc(size_t size, struct ahci_dma_region **retregion)
-{
- return ahci_dma_region_alloc_aligned(size, 512, retregion);
-}
-
-/**
- * Insert dma region region into free list.
- * \param region region to insert into free list
- */
-static errval_t
-return_region(struct ahci_dma_region *region)
-{
- AHCI_TRACE_ENTER("region = %p; region->vaddr = %p", region, region->vaddr);
- if (first_free == NULL) {
- // no free chunks. init free list with current region
- AHCI_DEBUG("no free chunks. init free list with current region\n");
- first_free = last_free = region->vaddr;
- first_free->size = region->size;
- first_free->backing_region = region->backing_region;
- pool.first_free[first_free->backing_region] = first_free;
- }
- else {
- // insert into free list
- AHCI_DEBUG("insert into free list\n");
- struct free *n = pool.first_free[region->backing_region];
- struct free *f;
- f = region->vaddr;
- f->size = region->size;
- f->backing_region = region->backing_region;
- AHCI_DEBUG("free: vaddr = %p; size = %zd; backing_region = %zd\n",
- f, f->size, f->backing_region);
-
- if (n == NULL) {
- // current region only free in backing block
- AHCI_DEBUG("current region only free in backing block\n");
- pool.first_free[region->backing_region] = f;
- if (region->backing_region >= pool.addr_count) {
- // we are in the last backing block && only free region in block
- // --> we are the new last_free
- AHCI_DEBUG("new last_free\n");
- f->next = NULL;
- f->prev = last_free;
- last_free->next = f;
- last_free = f;
- }
- else {
- // find next backing region with free chunks
- AHCI_DEBUG("find next backing_region w/ free chunks\n");
- size_t br = region->backing_region + 1;
- while (br < pool.addr_count) {
- if ((n = pool.first_free[br]) != NULL) {
- // found next free, insert before
- AHCI_DEBUG("found next br w/ free: %zd\n", br);
- n->prev->next = f;
- f->prev = n->prev;
- n->prev = f;
- f->next = n;
- break;
- }
- br++;
- }
-
- // we didn't find any backing region after ours with free
- // chunks --> we are last free
- if (br >= pool.addr_count) {
- AHCI_DEBUG("last_free");
- f->next = NULL;
- f->prev = last_free;
- last_free->next = f;
- last_free = f;
- }
-
- // we've inserted ourselves before the first_free,
- // becoming first_free
- if (f->prev == NULL) {
- AHCI_DEBUG("first_free");
- first_free = f;
- }
- }
- } // end current region only free in backing block
- else {
- // insert into backing block's free list
- AHCI_DEBUG("insert into backing block's free list\n");
-
- // find our next
- while (n && (void *)n < region->vaddr) {
- n = n->next;
- }
-
- // insert before n
- AHCI_DEBUG("insert before: %p\n", n);
- if (n == NULL) {
- // we are new last_free
- AHCI_DEBUG("last free\n");
- f->next = NULL;
- f->prev = last_free;
- last_free->next = f;
- last_free = f;
- }
- else if (n == first_free) {
- // we are new first_free
- AHCI_DEBUG("first free\n");
- f->prev = NULL;
- f->next = first_free;
- first_free->prev = f;
- first_free = f;
- // we're first_free; this implies that we are
- // first_free of our backing region
- pool.first_free[f->backing_region] = f;
- }
- else {
- if (n == pool.first_free[f->backing_region]) {
- // we are first free in our region
- AHCI_DEBUG("first free in region\n");
- pool.first_free[f->backing_region] = f;
- }
-
- // insert before n
- n->prev->next = f;
- f->prev = n->prev;
- n->prev = f;
- f->next = n;
- }
- } // end insert into backing block's free list
-
- // merge with next, if possible
- if (f->next && f->next->backing_region == f->backing_region) {
- n = f->next;
- // check if f and f->next can be merged
- uintptr_t fp = (uintptr_t) f;
- uintptr_t np = (uintptr_t) n;
- if (fp + f->size == np) {
- AHCI_DEBUG("merging with next free (%p)\n", f->next);
- // remove f->next from free list and add its size to f
- struct free *new_next = n->next;
- f->size += n->size;
- f->next = new_next;
-
- if (last_free == n) {
- // we are new last free
- last_free = f;
- } else {
- new_next->prev = f;
- }
- }
- } // end merge with next
-
- // merge with previous, if possible
- if (f->prev && f->prev->backing_region == f->backing_region) {
- struct free *p = f->prev;
- // check if f->prev and f can be merged
- uintptr_t fp = (uintptr_t) f;
- uintptr_t pp = (uintptr_t) p;
- if (pp + p->size == fp) {
- AHCI_DEBUG("merging with prev free (%p)\n", f->prev);
- // add f's size to f->prev's size and remove f from free list
- p->size += f->size;
- p->next = f->next;
-
- if (last_free == f) {
- // we are new last free
- last_free = p;
- }
- else {
- f->next->prev = p;
- }
- }
- } // end merge with previous
- } // end insert into free list
-
- return SYS_ERR_OK;
-}
-
-/**
- * Free ahci_dma_region region
- * \param region the dma region to free
- */
-errval_t
-ahci_dma_region_free(struct ahci_dma_region *region)
-{
- AHCI_TRACE_ENTER("region = %p", region);
- // insert memory into free list
- errval_t err = return_region(region);
- if (err_is_fail(err)) {
- return err;
- }
- // free struct ahci_dma_region
- free(region);
-
- return SYS_ERR_OK;
-}
+++ /dev/null
-/*
- * Copyright (c) 2011 ETH Zurich.
- * All rights reserved.
- *
- * This file is distributed under the terms in the attached LICENSE file.
- * If you do not find this file, copies can be found by writing to:
- * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
- */
-
-#ifndef _AHCI_INTERNAL_H
-#define _AHCI_INTERNAL_H
-
-void ahci_dump_command(int command, struct ahci_port_info *port);
-
-void ahci_dump_rfis(struct ahci_port_info *port);
-
-int ahci_find_free_command_slot(struct ahci_port_info *port);
-
-void ahci_port_free_dma_structs(struct ahci_port_info *port);
-
-errval_t ahci_setup_command(int *command, struct ahci_port_info *port,
- uint8_t *fis, size_t fis_length, size_t num_prds, bool is_write);
-
-errval_t ahci_add_physical_regions(struct ahci_port_info *port, int command,
- struct ahci_dma_region *buf, size_t buflen);
-
-
-#endif
+++ /dev/null
-/*
- * Copyright (c) 2011 ETH Zurich.
- * All rights reserved.
- *
- * This file is distributed under the terms in the attached LICENSE file.
- * If you do not find this file, copies can be found by writing to:
- * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
- */
-
-#include <barrelfish/barrelfish.h>
-#include <barrelfish/memobj.h>
-#include <barrelfish/vregion.h>
-#include <ahci/ahci.h>
-#include <ahci/ahci_dma_pool.h>
-#include <dev/ahci_hba_dev.h>
-#include <string.h>
-#include "ahci_debug.h"
-#include "ahci_internal.h"
-
-#define AHCI_MAX_PRD_COUNT 65535
-
-// CL is 1K, see AHCI Spec 1.3 Section 3.3.1
-#define AHCI_CL_SIZE 1024
-
-// rFIS is 256 bytes, see AHCI Spec 1.3 Section 3.3.3
-#define AHCI_RFIS_SIZE 256
-
-
-int ahci_find_free_command_slot(struct ahci_port_info *port)
-{
- for (int i = 0; i < ahci_hba_cap_ncs_extract(port->hba_capabilities) + 1; i++) {
- if (!port->command_slots[i].in_use) {
- return i;
- }
- }
-
- return -1;
-}
-
-errval_t ahci_port_alloc_dma_structs(ahci_port_t *port,
- struct ahci_dma_region **command_list,
- struct ahci_dma_region **receive_fis)
-{
- errval_t r;
-
- // allocate frame for command header list
- r = ahci_dma_region_alloc_aligned(AHCI_CL_SIZE, AHCI_CL_SIZE,
- command_list);
- assert(err_is_ok(r));
- // CLB must be aligned to 1024 bytes
- assert(((*command_list)->paddr & 0x3FF) == 0);
- ahci_port_clb_wr(port, (*command_list)->paddr);
-
- // allocate frame for received FIS
- r = ahci_dma_region_alloc_aligned(AHCI_RFIS_SIZE, AHCI_RFIS_SIZE,
- receive_fis);
- assert(err_is_ok(r));
- // FIS must be aligned to 256 bytes
- assert(((*receive_fis)->paddr & 0xFF) == 0);
- ahci_port_fb_wr(port, (*receive_fis)->paddr);
-
- return 0;
-}
-
-void ahci_port_free_dma_structs(struct ahci_port_info *port)
-{
- errval_t err;
- err = ahci_dma_region_free(port->command_list);
- if (err_is_fail(err)) {
- USER_PANIC_ERR(err, "ahci_dma_region_free failed for port command list");
- }
- err = ahci_dma_region_free(port->receive_fis);
- if (err_is_fail(err)) {
- USER_PANIC_ERR(err, "ahci_dma_region_free failed for port receive fis");
- }
-}
-
-errval_t ahci_setup_command(int *command, struct ahci_port_info *port,
- uint8_t *fis, size_t fis_length, size_t num_prds, bool is_write)
-{
- errval_t r;
- AHCI_DEBUG("ahci_setup_command(%p, %p, %p, %zu, %zu, %d): entering\n",
- command, port, fis, fis_length, num_prds, is_write);
-
- int command_slot = ahci_find_free_command_slot(port);
- assert(command_slot != -1); //TODO: what to do if we run out of slots? wait?
- port->command_slots[command_slot].in_use = true;
-
- // setup command table w/ enough entries for PRDs
- size_t prdt_size = num_prds*ahci_port_prd_size;
- size_t cmd_table_size = PRDT_OFFSET + prdt_size;
- struct ahci_dma_region *ct;
- r = ahci_dma_region_alloc(cmd_table_size, &ct);
- if (err_is_fail(r)) {
- DEBUG_ERR(r, "failed to allocate memory for command table");
- return r;
- }
- port->command_slots[command_slot].command_table = ct;
-
-
- AHCI_DEBUG("Using command slot: %d\n", command_slot);
- // insert command table into command header at pos `next_cmd'
- uint8_t *command_header_entry =
- ((uint8_t*)port->command_list->vaddr) + command_slot*ahci_port_chdr_size;
- memset(command_header_entry, 0, ahci_port_chdr_size);
- ahci_port_chdr_t chdr = (ahci_port_chdr_t) command_header_entry;
- ahci_port_chdr_cfl_insert(chdr, fis_length / 4);
- ahci_port_chdr_w_insert(chdr, is_write);
- ahci_port_chdr_ctba_insert(chdr, (uint32_t)ct->paddr);
- ahci_port_chdr_ctbau_insert(chdr, (uint32_t)(ct->paddr >> 32));
-
- // copy fis into command table
- memset(ct->vaddr, 0, cmd_table_size);
- ahci_dma_region_copy_in(ct, fis, 0, fis_length);
-
- *command = command_slot;
-
- AHCI_DEBUG("ahci_setup_command: exiting\n");
- return SYS_ERR_OK;
-}
-
-static void ahci_set_physical_region(ahci_port_prd_t prd,
- genpaddr_t physical_base, size_t length)
-{
- //AHCI_DEBUG("ahci_set_physical_region: entering\n");
-#ifdef AHCI_FIXED_PR_SIZE
- assert(length == PR_SIZE);
-#else
- assert(length <= MAX_PR_SIZE);
- assert((length & 1) == 0);
-#endif
-
- ahci_port_prd_dba_insert(prd, (uint32_t)physical_base);
- ahci_port_prd_dbau_insert(prd, (uint32_t)(physical_base >> 32));
- ahci_port_prd_dbc_insert(prd, length-1);
- //AHCI_DEBUG("ahci_set_physical_region: exiting\n");
-}
-
-static inline genpaddr_t ahci_get_sector_paddr(struct ahci_dma_region *region,
- size_t offset)
-{
- return region->paddr + offset;
-}
-
-errval_t ahci_add_physical_regions(struct ahci_port_info *port,
- int command, struct ahci_dma_region *buf, size_t buflen)
-{
- AHCI_DEBUG("ahci_add_physical_regions: entering\n");
- uint8_t *command_header_base =
- ((uint8_t*)port->command_list->vaddr) + command*ahci_port_chdr_size;
- ahci_port_chdr_t chdr = (ahci_port_chdr_t) command_header_base;
-
- // find next free prd entry in command header
- size_t prd_count = ahci_port_chdr_prdtl_extract(chdr);
- AHCI_DEBUG("prd_count = %zd\n", prd_count);
- // position for next prd
- size_t prdt_index = prd_count;
- // get base address of prd table
- uint8_t *prdt_base =
- (uint8_t*)port->command_slots[command].command_table->vaddr +
- PRDT_OFFSET;
-
- // max 512 bytes per PR
-#ifdef AHCI_FIXED_PR_SIZE
- size_t num_prds_needed = CEIL_DIV(buflen, PR_SIZE);
-#else
- size_t num_prds_needed = CEIL_DIV(buflen, MAX_PR_SIZE);
-#endif
- AHCI_DEBUG("#prds = %zd\n", num_prds_needed);
-
- // check that remaining space is enough for requested length
- if (prdt_index + num_prds_needed >= AHCI_MAX_PRD_COUNT) {
- // TODO: error code
- AHCI_DEBUG("not enough prd space left.\n");
- return -1;
- }
-
- AHCI_DEBUG("PRDT base: %p\n", prdt_base);
- // initialize a prd for each region
- ahci_port_prd_t prd;
- size_t offset = 0;
- do {
- prd = (ahci_port_prd_t) prdt_base + (prdt_index * ahci_port_prd_size);
- memset(prd, 0, ahci_port_prd_size);
- genpaddr_t sector_addr = ahci_get_sector_paddr(buf, offset);
- //AHCI_DEBUG("sector_addr = 0x%lx\n", sector_addr);
-#ifdef AHCI_FIXED_PR_SIZE
- ahci_set_physical_region(prd, sector_addr, PR_SIZE);
- buflen -= PR_SIZE;
- offset += PR_SIZE;
-#else
- size_t chunksize = buflen < MAX_PR_SIZE ? buflen : MAX_PR_SIZE;
- ahci_set_physical_region(prd, sector_addr, chunksize);
- buflen -= chunksize;
- offset += chunksize;
-#endif
- prdt_index += 1;
- } while (buflen > 0);
-
- // update number of PRDs for command
- ahci_port_chdr_prdtl_insert(chdr, prdt_index);
-
- AHCI_DEBUG("ahci_add_physical_regions: exiting\n");
- return 0;
-}
-
-#if defined(AHCI_LIB_DEBUG) || defined(GLOBAL_DEBUG)
-void ahci_dump_command(int command, struct ahci_port_info *port)
-{
- char buf_[4096];
- ahci_port_chdr_t chdr =
- (ahci_port_chdr_t)((uint8_t*) port->command_list->vaddr) +
- command*ahci_port_chdr_size;
- ahci_port_chdr_prtval(buf_, 4096, chdr);
- AHCI_DEBUG("\n%s\n", buf_);
- int prd_count = ahci_port_chdr_prdtl_extract(chdr);
- ahci_port_prd_t prd;
- for (int k = 0; k < prd_count; k++) {
- uint8_t *cmd_table_base = (uint8_t*) port->command_slots[command].command_table->vaddr;
- prd = (ahci_port_prd_t) (cmd_table_base + PRDT_OFFSET + k*ahci_port_prd_size);
- ahci_port_prd_prtval(buf_, 4096, prd);
- AHCI_DEBUG("\nPRD %d:\n%s\n", k, buf_);
- }
-}
-#else
-void ahci_dump_command(int command, struct ahci_port_info *port) {}
-#endif
-
-#if defined(AHCI_LIB_DEBUG) || defined(GLOBAL_DEBUG)
-void ahci_dump_rfis(struct ahci_port_info *port)
-{
- printf("rfis:\n");
- for (int i = 0; i < 256; i += 4) {
- printf("%3x: 0x%02x 0x%02x 0x%02x 0x%02x\n", i,
- *(((uint8_t*)port->receive_fis->vaddr) + i),
- *(((uint8_t*)port->receive_fis->vaddr) + i + 1),
- *(((uint8_t*)port->receive_fis->vaddr) + i + 2),
- *(((uint8_t*)port->receive_fis->vaddr) + i + 3)
- );
- }
-}
-#else
-void ahci_dump_rfis(struct ahci_port_info *port) {}
-#endif
+++ /dev/null
-/*
- * Copyright (c) 2011 ETH Zurich.
- * All rights reserved.
- *
- * This file is distributed under the terms in the attached LICENSE file.
- * If you do not find this file, copies can be found by writing to:
- * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
- */
-
-#include <stdlib.h>
-#include <errors/errno.h>
-#include <ahci/sata_fis.h>
-
-errval_t sata_alloc_h2d_register_fis(void **fis_p, size_t *fis_size_p)
-{
- struct sata_fis_reg_h2d *fis;
-
- fis = calloc(1, sizeof(*fis));
- if (!fis) {
- return LIB_ERR_MALLOC_FAIL;
- }
- fis->type = SATA_FIS_TYPE_H2D;
-
- /* Device Shadow Register layout (see: [1])
- * [ 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 ]
- * [ - | L | - | DEV | HS3 | HS2 | HS1 | HS0 ]
- *
- * L is the address mode, cleared implies CHS addressing, set, LBA addressing
- * DEV device select, cleared and set imply Device 0 and 1 resp.
- * for SATA this should always be cleared (see: [2])
- * HS3-HS0 are bits 28-25 of the LBA 28 (not used for LBA 48, see [3])
- *
- * [1] Serial ATA NSSD Rev. 1.0 (Sept 2008), section 6.3.1
- * [2] Serial ATA Rev. 2.6 (15-February-2007), section 13.1, paragraph 2
- * [3] ATA8-ACS Rev. 3f (December 11, 2006), section 7.1.5.2
- */
- fis->device |= (1 << 6);
-
- *fis_p = fis;
- *fis_size_p = sizeof(*fis);
-
- return SYS_ERR_OK;
-}
-
-errval_t sata_set_command(void *fis, uint8_t command)
-{
- uint8_t fis_type = *(uint8_t*)fis;
-
- if (fis_type == SATA_FIS_TYPE_H2D) {
- struct sata_fis_reg_h2d *fis_reg_h2d = fis;
- fis_reg_h2d->command = command;
-
- /* set bit to indicate update of command register (see [1])
- *
- * [1]: SATA Rev. 2.6 (15-February-2007), section 10.3.4
- */
- fis_reg_h2d->specialstuff |= (1 << 7);
-
- return SYS_ERR_OK;
- }
- else {
- return SATA_ERR_INVALID_TYPE;
- }
-}
-
-errval_t sata_set_feature(void *fis, uint8_t feature)
-{
- uint8_t fis_type = *(uint8_t*)fis;
-
- if (fis_type == SATA_FIS_TYPE_H2D) {
- struct sata_fis_reg_h2d *fis_reg_h2d = fis;
- fis_reg_h2d->feature = feature;
-
- return SYS_ERR_OK;
- }
- else {
- return SATA_ERR_INVALID_TYPE;
- }
-}
-
-errval_t sata_set_lba28(void *fis, uint32_t lba)
-{
- uint8_t fis_type = *(uint8_t*)fis;
-
- if (fis_type == SATA_FIS_TYPE_H2D) {
- struct sata_fis_reg_h2d *fis_reg_h2d = fis;
- fis_reg_h2d->lba0 = lba & 0xFF;
- fis_reg_h2d->lba1 = (lba >> 8) & 0xFF;
- fis_reg_h2d->lba2 = (lba >> 16) & 0xFF;
- fis_reg_h2d->device = (fis_reg_h2d->device & ~0x0F) | ((lba >> 24) & 0x0F);
-
- return SYS_ERR_OK;
- }
- else {
- return SATA_ERR_INVALID_TYPE;
- }
-}
-
-errval_t sata_set_lba48(void *fis, uint64_t lba)
-{
- uint8_t fis_type = *(uint8_t*)fis;
-
- if (fis_type == SATA_FIS_TYPE_H2D) {
- struct sata_fis_reg_h2d *fis_reg_h2d = fis;
- fis_reg_h2d->lba0 = lba & 0xFF;
- fis_reg_h2d->lba1 = (lba >> 8) & 0xFF;
- fis_reg_h2d->lba2 = (lba >> 16) & 0xFF;
- fis_reg_h2d->device &= 0xF0; // clear bits otherwise used by lba28
-
- fis_reg_h2d->lba3 = (lba >> 24) & 0xFF;
- fis_reg_h2d->lba4 = (lba >> 32) & 0xFF;
- fis_reg_h2d->lba5 = (lba >> 40) & 0xFF;
-
- return SYS_ERR_OK;
- }
- else {
- return SATA_ERR_INVALID_TYPE;
- }
-}
-
-errval_t sata_set_count(void *fis, uint16_t count)
-{
- uint8_t fis_type = *(uint8_t*)fis;
-
- if (fis_type == SATA_FIS_TYPE_H2D) {
- struct sata_fis_reg_h2d *fis_reg_h2d = fis;
- fis_reg_h2d->countl = count & 0xFF;
- fis_reg_h2d->counth = (count >> 8) & 0xFF;
-
- return SYS_ERR_OK;
- }
- else {
- return SATA_ERR_INVALID_TYPE;
- }
-}
+++ /dev/null
-/*
- * Copyright (c) 2014, University of Washington.
- * All rights reserved.
- *
- * This file is distributed under the terms in the attached LICENSE file.
- * If you do not find this file, copies can be found by writing to:
- * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
- */
-
-#include <barrelfish/barrelfish.h>
-#include <barrelfish/waitset.h>
-#include <if/ata_rw28_defs.h>
-#include <if/ata_rw28_ahci_defs.h>
-#include <if/ata_rw28_rpcclient_defs.h>
-#include <storage/vsic.h>
-
-struct ahci_vsic {
- struct ahci_ata_rw28_binding ahci_ata_rw28_binding;
- struct ata_rw28_rpc_client ata_rw28_rpc;
- struct ata_rw28_binding *ata_rw28_binding;
- struct ahci_binding *ahci_binding;
- errval_t bind_err;
-};
-
-static errval_t vsic_write(struct storage_vsic *vsic, struct storage_vsa *vsa,
- off_t offset, size_t size, void *buffer)
-{
- assert(vsic != NULL);
- assert(vsa != NULL);
- assert(buffer != NULL);
- struct ahci_vsic *mydata = vsic->data;
- errval_t status;
-
- errval_t err = mydata->ata_rw28_rpc.vtbl.
- write_dma(&mydata->ata_rw28_rpc, buffer, STORAGE_VSIC_ROUND(vsic, size),
- offset, &status);
- if (err_is_fail(err)) {
- USER_PANIC_ERR(err, "write_dma rpc");
- }
- if (err_is_fail(status)) {
- USER_PANIC_ERR(status, "write_dma status");
- }
-
- return SYS_ERR_OK;
-}
-
-static errval_t vsic_read(struct storage_vsic *vsic, struct storage_vsa *vsa,
- off_t offset, size_t size, void *buffer)
-{
- assert(vsic != NULL);
- assert(vsa != NULL);
- assert(buffer != NULL);
- struct ahci_vsic *mydata = vsic->data;
- uint8_t *buf = NULL;
- size_t bytes_read, toread = STORAGE_VSIC_ROUND(vsic, size);
-
- errval_t err = mydata->ata_rw28_rpc.vtbl.
- read_dma(&mydata->ata_rw28_rpc, toread, offset, &buf, &bytes_read);
- if (err_is_fail(err))
- USER_PANIC_ERR(err, "read_dma rpc");
- if (!buf)
- USER_PANIC("read_dma -> !buf");
- if (bytes_read != toread)
- USER_PANIC("read_dma -> read_size != size");
-
- // XXX: Copy from DMA buffer to user buffer
- memcpy(buffer, buf, size);
- free(buf);
-
- return SYS_ERR_OK;
-}
-
-static errval_t vsic_flush(struct storage_vsic *vsic, struct storage_vsa *vsa)
-{
- assert(vsic != NULL);
- assert(vsa != NULL);
- struct ahci_vsic *mydata = vsic->data;
- errval_t outerr;
-
- errval_t err = mydata->ata_rw28_rpc.vtbl.
- flush_cache(&mydata->ata_rw28_rpc, &outerr);
- assert(err_is_ok(err));
-
- return outerr;
-}
-
-static errval_t vsic_wait(struct storage_vsic *vsic)
-{
- // XXX: Interface currently synchronous
- return SYS_ERR_OK;
-}
-
-static struct storage_vsic_ops ahci_vsic_ops = {
- .write = vsic_write,
- .read = vsic_read,
- .flush = vsic_flush,
- .wait = vsic_wait,
-};
-
-static void ahci_bind_cb(void *st, errval_t err, struct ahci_binding *_binding)
-{
- assert(err_is_ok(err));
- struct ahci_vsic *mydata = st;
- mydata->ahci_binding = _binding;
-}
-
-// XXX: This could be made public and controlled by the programmer instead of
-// the commandline
-static errval_t ahci_vsic_alloc(struct storage_vsic *vsic, uint8_t port)
-{
- assert(vsic != NULL);
- errval_t err;
- struct ahci_vsic *mydata = malloc(sizeof(struct ahci_vsic));
- assert(mydata != NULL);
- memset(mydata, 0, sizeof(struct ahci_vsic));
-
- // init ahci management binding
- err = ahci_init(port, ahci_bind_cb, mydata, get_default_waitset());
- assert(err_is_ok(err));
-
- while(!mydata->ahci_binding) {
- event_dispatch(get_default_waitset());
- }
-
- // init ata flounder binding
- err = ahci_ata_rw28_init(&mydata->ahci_ata_rw28_binding,
- get_default_waitset(), mydata->ahci_binding);
- if (err_is_fail(err)) {
- USER_PANIC_ERR(err, "ahci_ata_rw28_init");
- }
- mydata->ata_rw28_binding =
- (struct ata_rw28_binding*)&mydata->ahci_ata_rw28_binding;
-
- // init RPC client
- err = ata_rw28_rpc_client_init(&mydata->ata_rw28_rpc,
- mydata->ata_rw28_binding);
- if (err_is_fail(err)) {
- USER_PANIC_ERR(err, "ata_rw28_rpc_client_init");
- }
-
- // Init VSIC data structure
- vsic->ops = ahci_vsic_ops;
- vsic->data = mydata;
- vsic->blocksize = 512; // XXX: Determine from drive?
-
- return SYS_ERR_OK;
-}
-
-errval_t storage_vsic_driver_init(int argc, const char **argv,
- struct storage_vsic *vsic)
-{
- // init dma pool
- ahci_dma_pool_init(1024*1024);
-
- // Allocate port 0
- return ahci_vsic_alloc(vsic, 0);
-}
USER_PANIC("Network polling not available without Arranet!\n");
}
+void poll_ahci(struct waitset_chanstate *) __attribute__((weak));
+void poll_ahci(struct waitset_chanstate *chan)
+{
+ errval_t err = waitset_chan_trigger(chan);
+ assert(err_is_ok(err)); // should not be able to fail
+}
+
/// Helper function that knows how to poll the given channel, based on its type
static void poll_channel(struct waitset_chanstate *chan)
{
arranet_polling_loop_proxy();
break;
+ case CHANTYPE_AHCI:
+ poll_ahci(chan);
+ break;
+
default:
assert(!"invalid channel type to poll!");
}
--- /dev/null
+[
+ build library {
+ target = "blk",
+ mackerelDevices = [ "ata_identify", "ahci_port", "ahci_hba" ],
+ cFiles = [ "blk.c", "blk_ahci/ahci_init.c", "blk_ahci/ahci_port.c", "blk_ahci/ahci_dev.c", "blk_ahci/sata_fis.c", "blk_ahci/device_impl.c", "dma_mem/dma_mem.c" ]
+ }
+]
--- /dev/null
+/**
+ * \file
+ * \brief
+ */
+/*
+ * Copyright (c) 2016, ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+
+#include <barrelfish/barrelfish.h>
--- /dev/null
+///! AHCI Device helper functions
+#include <barrelfish/barrelfish.h>
+#include <barrelfish/deferred.h>
+
+#include "blk_ahci.h"
+#include "../blk_debug.h"
+
+#include "ahci_dev.h"
+
+size_t ahci_port_offset(uint32_t port)
+{
+ return 0x100 + port * 0x80;
+}
+
+bool ahci_port_is_idle(ahci_port_t* port)
+{
+ ahci_port_cmd_t cmd = ahci_port_cmd_rd(port);
+ uint8_t st = ahci_port_cmd_st_extract(cmd);
+ uint8_t cr = ahci_port_cmd_cr_extract(cmd);
+ uint8_t fre = ahci_port_cmd_fre_extract(cmd);
+
+ return st == 0 && cr == 0 && fre == 0;
+}
+
+bool ahci_port_is_ready(ahci_port_t* port)
+{
+ uint8_t busy = ahci_port_tfd_bsy_rdf(port);
+ uint8_t drq = ahci_port_tfd_drq_rdf(port);
+ return busy == 0 && drq == 0;
+}
+
+bool ahci_port_is_functional(ahci_port_t* port)
+{
+ uint8_t det = ahci_port_ssts_det_rdf(port);
+ return ahci_port_is_ready(port) && det == 0x3;
+}
+
+bool ahci_port_slot_free(ahci_port_t* port, uint8_t slot)
+{
+ bool ci_free = (ahci_port_ci_rd(port) & (1<<slot)) == 0;
+ bool sact_free = (ahci_port_sact_rd(port) & (1<<slot)) == 0;
+ return ci_free && sact_free;
+}
+
+void ahci_port_start(ahci_port_t* port)
+{
+ assert(ahci_port_is_functional(port));
+ ahci_port_cmd_st_wrf(port, 0x1);
+}
+
+void ahci_port_stop(ahci_port_t* port)
+{
+ ahci_port_cmd_st_wrf(port, 0);
+
+ ahci_port_cmd_t cmd = ahci_port_cmd_rd(port);
+ while (ahci_port_cmd_cr_extract(cmd) != 0) {
+ // TODO should clear in 500ms
+ }
+
+ ahci_port_cmd_fre_wrf(port, 0);
+ while (ahci_port_cmd_fr_rdf(port) != 0) {
+ // TODO should clear in 500ms
+ }
+}
+
+bool ahci_port_is_running(ahci_port_t* port)
+{
+ return ahci_port_cmd_st_rdf(port) > 0;
+}
+
+uint32_t ahci_port_probe(ahci_port_t* port)
+{
+ if (ahci_port_ssts_det_rdf(port) == 0x03) {
+ return ahci_port_sig_rd(port);
+ }
+
+ return 0;
+}
+
+void ahci_hba_reset(ahci_hba_t* controller)
+{
+ ahci_hba_ghc_t ghc = ahci_hba_ghc_rd(controller);
+ BLK_DEBUG("Resetting HBA (setting ghc = %x)...\n", ghc);
+ ghc = ahci_hba_ghc_hr_insert(ghc, 1);
+ ahci_hba_ghc_wr(controller, ghc);
+ barrelfish_usleep(200000);
+ while (1) {
+ ghc = ahci_hba_ghc_rd(controller);
+ if (ahci_hba_ghc_hr_extract(ghc) == 0) {
+ BLK_DEBUG("reset done\n");
+ break;
+ }
+ }
+}
+
+void ahci_hba_irq_enable(ahci_hba_t* controller)
+{
+ BLK_DEBUG("Enabling HBA Interrupts\n");
+ ahci_hba_ghc_t ghc = ahci_hba_ghc_rd(controller);
+ ghc = ahci_hba_ghc_ie_insert(ghc, 1);
+ ahci_hba_ghc_wr(controller, ghc);
+}
+
+void ahci_hba_irq_disable(ahci_hba_t* controller)
+{
+ BLK_DEBUG("Disable HBA Interrupts\n");
+ ahci_hba_ghc_t ghc = ahci_hba_ghc_rd(controller);
+ ghc = ahci_hba_ghc_ie_insert(ghc, 1);
+ ahci_hba_ghc_wr(controller, ghc);
+}
+
+uint32_t ahci_hba_get_num_ports(ahci_hba_t* controller)
+{
+ return ahci_hba_cap_np_extract(ahci_hba_cap_rd(controller)) + 1;
+}
+
+uint32_t ahci_hba_get_command_slots(ahci_hba_t* controller)
+{
+ return ahci_hba_cap_ncs_extract(ahci_hba_cap_rd(controller));
+}
+
+bool ahci_port_is_implemented(ahci_hba_t* controller, size_t port)
+{
+ return (ahci_hba_pi_rd(controller) & (1 << port)) > 0;
+}
+
+void ahci_port_print_identification(ata_identify_t *id)
+{
+ char* buf2 = malloc(10*4096);
+ assert(buf2 != NULL);
+ ata_identify_pr(buf2, 10*4096, id);
+ puts(buf2);
+ free(buf2);
+}
+
+errval_t ahci_hba_init(ahci_hba_t* controller)
+{
+ ahci_hba_reset(controller);
+
+ // Make sure AHCI is enabled:
+ BLK_DEBUG("Setting controller into AHCI mode\n");
+ ahci_hba_ghc_t ghc = ahci_hba_ghc_rd(controller);
+ ghc = ahci_hba_ghc_ae_insert(ghc, 1);
+ ahci_hba_ghc_wr(controller, ghc);
+
+ ahci_hba_irq_disable(controller);
+
+ return SYS_ERR_OK;
+}
--- /dev/null
+/*
+ * Copyright (c) 2016, ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#ifndef AHCI_DEV_H_
+#define AHCI_DEV_H_
+
+#include <stdbool.h>
+#include <errors/errno.h>
+#include <dev/ahci_hba_dev.h>
+#include <dev/ahci_port_dev.h>
+#include <dev/ata_identify_dev.h>
+
+void ahci_hba_reset(ahci_hba_t* controller);
+void ahci_hba_irq_enable(ahci_hba_t* controller);
+void ahci_hba_irq_disable(ahci_hba_t* controller);
+uint32_t ahci_hba_get_num_ports(ahci_hba_t* controller);
+errval_t ahci_hba_init(ahci_hba_t* controller);
+uint32_t ahci_hba_get_command_slots(ahci_hba_t* controller);
+bool ahci_port_is_implemented(ahci_hba_t* controller, size_t port);
+uint32_t ahci_port_probe(ahci_port_t* port);
+
+void ahci_port_start(ahci_port_t* port);
+void ahci_port_stop(ahci_port_t* port);
+bool ahci_port_is_running(ahci_port_t* port);
+bool ahci_port_is_idle(ahci_port_t* port);
+bool ahci_port_is_functional(ahci_port_t* port);
+bool ahci_port_is_ready(ahci_port_t* port);
+size_t ahci_port_offset(uint32_t port);
+bool ahci_port_slot_free(ahci_port_t* port, uint8_t slot);
+
+void ahci_port_print_identification(ata_identify_t *id);
+
+#endif // AHCI_DEV_H_
--- /dev/null
+#include <barrelfish/barrelfish.h>
+#include <pci/pci.h>
+
+#include "blk_ahci.h"
+#include "../blk_debug.h"
+#include "ahci_dev.h"
+
+lvaddr_t blk_ahci_get_bar5_vaddr(struct ahci_disk* ad)
+{
+ assert(ad->bar5->vaddr != NULL);
+ return (lvaddr_t)ad->bar5->vaddr;
+}
+
+errval_t blk_ahci_init(struct device_mem* bar5, struct ahci_disk** out)
+{
+ errval_t err;
+
+ err = map_device(bar5);
+ if (err_is_fail(err)) {
+ USER_PANIC_ERR(err, "Map BAR5 failed.");
+ }
+
+ struct ahci_disk* ad = calloc(sizeof(struct ahci_disk), 1);
+ assert (ad != NULL);
+
+ ad->bar5 = bar5;
+
+ ahci_hba_initialize(&ad->controller, (void *)(bar5->vaddr));
+ BLK_DEBUG("Accessing conf regs starting at %p\n", (void *)(bar5->vaddr));
+ BLK_DEBUG("Physical address of conf regs: %p\n", (void *)(bar5->paddr));
+
+ err = ahci_hba_init(&ad->controller);
+ if (err_is_fail(err)) {
+ USER_PANIC_ERR(err, "Init HBA failed.");
+ }
+
+ ahci_hba_irq_enable(&ad->controller);
+
+ err = blk_ahci_ports_init(ad);
+ if (err_is_fail(err)) {
+ USER_PANIC_ERR(err, "Port init failed.");
+ }
+
+ *out = ad;
+ return err;
+}
+
+
+errval_t blk_ahci_stop(struct ahci_disk* ad)
+{
+ // stop device
+ // devmem free (ad->bar5)
+ // free (ad)
+
+ return SYS_ERR_OK;
+}
--- /dev/null
+#include <barrelfish/barrelfish.h>
+#include <barrelfish/deferred.h>
+
+#include <pci/pci.h>
+
+#include "blk_ahci.h"
+#include "ahci_dev.h"
+#include "sata_fis.h"
+
+#include "../blk_debug.h"
+#include "../dma_mem/dma_mem.h"
+
+static ahci_port_chdr_t get_command_header(struct ahci_port* port, uint8_t slot)
+{
+ return (ahci_port_chdr_t) ((uint8_t*)port->clb.vaddr) + (ahci_port_chdr_size * slot);
+}
+
+static struct region_descriptor region_descriptor_new(uint64_t dba, uint32_t dbc, bool irq)
+{
+ struct region_descriptor d;
+ d.dba = dba;
+ d.dbc = dbc;
+ d.dbc |= irq << 31;
+
+ return d;
+}
+
+static void command_table_set_cfis(struct command_table* ct, struct sata_fis_reg_h2d* fis)
+{
+ assert(sizeof(ct->cfis) >= sizeof(*fis));
+ struct sata_fis_reg_h2d* fis_ptr = (struct sata_fis_reg_h2d*) ct->cfis;
+ *fis_ptr = *fis;
+}
+
+static errval_t port_init_fb(struct ahci_port* port)
+{
+ assert(port != NULL);
+
+ errval_t err = dma_mem_alloc(1024, &port->fb);
+ if (err_is_fail(err)) {
+ DEBUG_ERR(err, "DMA alloc failed");
+ return err_push(err, AHCI_ERR_PORT_INIT);
+ }
+ memset((void*)port->fb.vaddr, 0x0, 1024);
+
+ assert(port->fb.paddr % 4096 == 0);
+ ahci_port_fb_wr(&port->port, port->fb.paddr);
+ return err;
+}
+
+static errval_t port_init_clb(struct ahci_port* port)
+{
+ assert (port != NULL);
+ assert (ahci_port_chdr_size*32 == 0x400);
+
+ errval_t err = dma_mem_alloc(ahci_port_chdr_size*32, &port->clb);
+ if (err_is_fail(err)) {
+ DEBUG_ERR(err, "DMA alloc failed");
+ return err_push(err, AHCI_ERR_PORT_INIT);
+ }
+ memset((void*)port->clb.vaddr, 0x0, ahci_port_chdr_size*32);
+
+ assert(port->clb.paddr % 1024 == 0);
+ ahci_port_clb_wr(&port->port, port->clb.paddr);
+ return err;
+}
+
+static errval_t port_init_ctba(struct ahci_port* port, size_t command_slot)
+{
+ assert(port != NULL);
+ assert(command_slot < MAX_CTBA_SLOTS);
+
+ errval_t err = dma_mem_alloc(4096, &port->ctba_mem[command_slot]);
+ if (err_is_fail(err)) {
+ DEBUG_ERR(err, "DMA alloc failed");
+ return err_push(err, AHCI_ERR_PORT_INIT);
+ }
+ memset((void*)port->ctba_mem[command_slot].vaddr, 0x0, 4096);
+ port->command_table[command_slot] = (struct command_table*) port->ctba_mem[command_slot].vaddr;
+
+ return err;
+}
+
+static void blk_ahci_interrupt(struct ahci_port* port, struct dev_queue *queue)
+{
+ ahci_port_is_t status = ahci_port_is_rd(&port->port);
+
+ // A request was handled:
+ //if (ahci_port_is_dhrs_extract(status) > 0) {
+ BLK_DEBUG("Done with DMA command.\n");
+
+ for (size_t slot = 0; slot < queue->port->ncs; slot++) {
+ struct dev_queue_request *dqr = &queue->requests[slot];
+
+ bool slot_has_request = dqr->status == RequestStatus_InProgress;
+ bool slot_done = ahci_port_slot_free(&port->port, slot);
+ if (slot_has_request && !slot_done) {
+ //printf("waiting for slot %zu.\n", slot);
+ }
+ if (slot_has_request && slot_done) {
+ //printf("AHCI slot %zu is done now.\n", slot);
+ dqr->status = RequestStatus_Done;
+ }
+ }
+
+ if (ahci_port_is_dhrs_extract(status)) {
+ ahci_port_is_dhrs_wrf(&port->port, 0x1);
+ }
+ //}
+
+ // Did something happen we can't yet handle?
+ if (status > 0x1) {
+#if defined(BLK_DEBUG_ENABLE)
+ char buf[4096];
+ ahci_port_is_pr(buf, 4096, &port->port);
+ BLK_DEBUG("GOT unhandled IRQ: %u\n", ahci_port_is_rd(&port->port));
+ puts(buf);
+#else
+ printf("[BLK] unhandled irq: %u", ahci_port_is_rd(&port->port));
+#endif
+ }
+ else {
+ //printf("[BLK] IRQ handled\n");
+ }
+}
+
+errval_t blk_ahci_port_dma_async(struct ahci_port *port, size_t slot, uint64_t block, lpaddr_t base, size_t length, bool write)
+{
+ assert(length % 512 == 0);
+ assert(ahci_port_slot_free(&port->port, 1 << slot));
+
+ errval_t err = SYS_ERR_OK;
+
+ ahci_port_chdr_t header = get_command_header(port, slot);
+ memset(header, 0, ahci_port_chdr_size);
+ ahci_port_chdr_a_insert(header, 0);
+ ahci_port_chdr_w_insert(header, write);
+ ahci_port_chdr_cfl_insert(header, sizeof(struct sata_fis_reg_h2d) / sizeof(uint32_t));
+ ahci_port_chdr_prdtl_insert(header, 1);
+ ahci_port_chdr_ctba_insert(header, port->ctba_mem[slot].paddr);
+
+ uint8_t command = write ? ATA_CMD_WRITE_DMA_EXT : ATA_CMD_READ_DMA_EXT;
+ uint16_t sectors = length / 512;
+
+ struct command_table* ct = port->command_table[slot];
+ struct sata_fis_reg_h2d fis;
+ sata_h2d_fis_new(&fis, command, block, sectors);
+ command_table_set_cfis(ct, &fis);
+ ct->prdt[0] = region_descriptor_new(base, (length - 1) | 0x1, false);
+
+ while (!ahci_port_is_ready(&port->port)) {
+ // TODO: Abort return error on timeout
+ }
+
+ // Issue command in slot 0
+ ahci_port_ci_rawwr(&port->port, 1 << slot);
+ BLK_DEBUG("Issued async DMA command.\n");
+
+#if 0
+ while ((ahci_port_ci_rd(&port->port) & (1<<slot)) > 0) {
+ // TODO: abort on timeout
+ //barrelfish_usleep(10000);
+ }
+ BLK_DEBUG("Done with DMA command.\n");
+#endif
+
+ return err;
+}
+
+errval_t blk_ahci_port_dma(struct ahci_port *port, uint64_t block, struct dma_mem *buffer, bool write)
+{
+ errval_t err = SYS_ERR_OK;
+ size_t slot = 0;
+
+ // TODO: use only slot 0
+ assert(ahci_port_slot_free(&port->port, 1 << slot));
+
+ ahci_port_chdr_t header = get_command_header(port, slot);
+ memset(header, 0, ahci_port_chdr_size);
+ ahci_port_chdr_a_insert(header, 0);
+ ahci_port_chdr_w_insert(header, write);
+ ahci_port_chdr_cfl_insert(header, sizeof(struct sata_fis_reg_h2d) / sizeof(uint32_t));
+ ahci_port_chdr_prdtl_insert(header, 1);
+ ahci_port_chdr_ctba_insert(header, port->ctba_mem[slot].paddr);
+
+ uint8_t command = write ? ATA_CMD_WRITE_DMA_EXT : ATA_CMD_READ_DMA_EXT;
+ assert(buffer->bytes % 512 == 0);
+ uint16_t sectors = buffer->bytes / 512;
+
+ struct command_table* ct = port->command_table[slot];
+ struct sata_fis_reg_h2d fis;
+ sata_h2d_fis_new(&fis, command, block, sectors);
+ command_table_set_cfis(ct, &fis);
+ ct->prdt[0] = region_descriptor_new(buffer->paddr, (buffer->bytes - 1) | 0x1, false);
+
+ while (!ahci_port_is_ready(&port->port)) {
+ // TODO: Abort return error on timeout
+ }
+
+ // Issue command in slot 0
+ ahci_port_ci_wr(&port->port, 1 << slot);
+
+ BLK_DEBUG("Issued DMA command (w=%d) block=%zu.\n", write, block);
+ while ((ahci_port_ci_rd(&port->port) & (1<<slot)) > 0) {
+ // TODO: abort on timeout
+ barrelfish_usleep(10000);
+ }
+
+ BLK_DEBUG("Done with DMA command.\n");
+ // Clear expected interrupt:
+ ahci_port_is_dhrs_wrf(&port->port, 0x1);
+
+ if (ahci_port_is_rd(&port->port) != 0x0) {
+#if defined(BLK_DEBUG_ENABLE)
+ char buf[4096];
+ ahci_port_is_pr(buf, 4096, &port->port);
+ BLK_DEBUG("GOT unhandled IRQ: %u\n", ahci_port_is_rd(&port->port));
+ puts(buf);
+#endif
+ }
+
+ printf("%s:%s:%d: write? %d %zu\n", __FILE__, __FUNCTION__, __LINE__, write, ((uint64_t*)buffer->vaddr)[0]);
+ return err;
+}
+
+static errval_t port_identify(struct ahci_port *port)
+{
+ // TODO: this function needs proper error handling
+ errval_t err = SYS_ERR_OK;
+ assert(ahci_port_slot_free(&port->port, 1));
+
+ err = dma_mem_alloc(512, &port->identify_mem);
+ if (err_is_fail(err)) {
+ USER_PANIC_ERR(err, "dma memory allocation failed.");
+ }
+ memset((void*)port->identify_mem.vaddr, 0, 512);
+
+ // Write command into command list
+ ahci_port_chdr_t header = get_command_header(port, 0);
+ memset(header, 0, ahci_port_chdr_size);
+ ahci_port_chdr_w_insert(header, 0);
+ ahci_port_chdr_a_insert(header, 0);
+ ahci_port_chdr_cfl_insert(header, sizeof(struct sata_fis_reg_h2d) / sizeof(uint32_t));
+ ahci_port_chdr_prdtl_insert(header, 1); // we use 1 PRD
+ ahci_port_chdr_ctba_insert(header, port->ctba_mem[0].paddr);
+
+ struct command_table* ct = port->command_table[0];
+
+ struct sata_fis_reg_h2d fis;
+ memset(&fis, 0, sizeof(struct sata_fis_reg_h2d));
+ fis.type = SATA_FIS_TYPE_H2D;
+ fis.command = 0xEC; // ATA_CMD_IDENTIFY
+ fis.device = 0;
+ fis.specialstuff = 0x80; // Command
+ command_table_set_cfis(ct, &fis);
+
+ ct->prdt[0] = region_descriptor_new(port->identify_mem.paddr, 511, false);
+
+ while (!ahci_port_is_ready(&port->port)) {}
+
+ // Ensure command processing is on
+ ahci_port_cmd_t cmd = ahci_port_cmd_rd(&port->port);
+ assert(ahci_port_cmd_cr_extract(cmd) == 1);
+
+ // Issue command in slot 0
+ ahci_port_ci_wr(&port->port, 0x1);
+
+ BLK_DEBUG("Issued IDENTIFY command.\n");
+ while ((ahci_port_ci_rd(&port->port) & 0x1) > 0) {
+ // TODO: abort on timeout
+ barrelfish_usleep(10000);
+ }
+
+ // Clear the interrupts
+ ahci_port_is_pss_wrf(&port->port, 0x1);
+
+ // Nothing else should've happened:
+ if (ahci_port_is_rd(&port->port) != 0x0) {
+#if defined(BLK_DEBUG_ENABLE)
+ char buf[4096];
+ ahci_port_is_pr(buf, 4096, &port->port);
+ BLK_DEBUG("GOT unhandled IRQ: %u\n", ahci_port_is_rd(&port->port));
+ puts(buf);
+#endif
+ }
+ assert(ahci_port_is_rd(&port->port) == 0x0);
+
+ BLK_DEBUG("IDENTIFY command processed.\n");
+ ata_identify_initialize(&port->identify, (mackerel_addr_t)port->identify_mem.vaddr);
+
+#if defined(BLK_DEBUG_ENABLE)
+ ahci_port_print_identification(&port->identify);
+#endif
+ return err;
+}
+
+errval_t blk_ahci_ports_init(struct ahci_disk *ad)
+{
+ errval_t err = SYS_ERR_OK;
+ size_t ports = ahci_hba_get_num_ports(&ad->controller);
+ size_t ncs = ahci_hba_cap_ncs_rdf(&ad->controller);
+ BLK_DEBUG("Implemented ports: %zu\n", ports);
+ BLK_DEBUG("Implemented command slots: %zu\n", ncs);
+
+ lvaddr_t base_address = blk_ahci_get_bar5_vaddr(ad);
+
+ // Determine which ports are implemented by the HBA, by reading the PI register. This bit
+ // map value will aid software in determining how many ports are available and which port
+ // registers need to be initialized.
+ for (size_t i = 0; i < ports; i++) {
+ struct ahci_port *port = &ad->ports[i];
+ if (!ahci_port_is_implemented(&ad->controller, i)) {
+ continue;
+ }
+ ahci_port_initialize(&port->port, (mackerel_addr_t)base_address + ahci_port_offset(i));
+
+ BLK_DEBUG("Initializing port: %zu\n", i);
+ uint32_t probe = ahci_port_probe(&port->port);
+ if (probe == 0) {
+ BLK_DEBUG("Port %zu no connection\n", i);
+ continue;
+ }
+ else if (probe == 0x00000101) {
+ BLK_DEBUG("Port %zu found SATA device, initializing.\n", i);
+ }
+ else {
+ BLK_DEBUG("Port %zu unkown signature: %d\n", i, ahci_port_sig_rd(&port->port));
+ continue;
+ }
+
+ // Ensure that the controller is not in the running state by reading and examining each
+ // implemented port’s PxCMD register. If PxCMD.ST, PxCMD.CR, PxCMD.FRE and
+ // PxCMD.FR are all cleared, the port is in an idle state.
+ if (!ahci_port_is_idle(&port->port)) {
+ BLK_DEBUG("Port %zu is running, stopping it.", i);
+ ahci_port_stop(&port->port);
+ }
+ assert(ahci_port_is_idle(&port->port)); // if this triggers, try a port reset...
+
+ // For each implemented port, system software shall allocate memory for and program:
+ // - PxCLB and PxCLBU
+ // - PxFB and PxFBU
+ port_init_clb(port);
+ port_init_fb(port);
+
+ // After setting PxFB and PxFBU to the physical address of the FIS receive area,
+ // system software shall set PxCMD.FRE to ‘1’.
+ ahci_port_cmd_fre_wrf(&port->port, 0x1);
+
+ // For each implemented port, clear the PxSERR register, by writing ‘1s’ to each bit location.
+ uint32_t serr = ahci_port_serr_rd(&port->port);
+ ahci_port_serr_wr(&port->port, serr);
+
+ // Determine which events should cause an interrupt, and set each implemented port’s PxIE
+ ahci_port_ie_wr(&port->port, 0xffffffff);
+
+ assert(ncs < MAX_CTBA_SLOTS);
+ for (size_t slot = 0; slot < ncs; slot++) {
+ port_init_ctba(port, slot);
+ }
+
+ BLK_DEBUG("Waiting for port %zu to become ready\n", i);
+ while(!ahci_port_is_functional(&port->port)) {
+ barrelfish_usleep(5000);
+ }
+ ahci_port_start(&port->port);
+
+ BLK_DEBUG("Initialized port %zu\n", i);
+ err = port_identify(port);
+ assert(err_is_ok(err));
+
+ port->ncs = ahci_hba_get_command_slots(&ad->controller);
+ port->interrupt = blk_ahci_interrupt;
+ port->is_initialized = true;
+ }
+
+ return err;
+}
--- /dev/null
+
+/*
+ * Copyright (c) 2016, ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#ifndef _BLK_AHCI_
+#define _BLK_AHCI_
+
+#include <barrelfish/barrelfish.h>
+#include <pci/mem.h>
+#include <blk/ahci.h>
+#include <devif/queue.h>
+
+#include <dev/ahci_hba_dev.h>
+#include <dev/ahci_port_dev.h>
+#include <dev/ata_identify_dev.h>
+
+#include "../dma_mem/dma_mem.h"
+
+#define MAX_AHCI_PORTS 32
+#define MAX_CTBA_SLOTS 32
+
+#define MAX_HBA 32
+#define MAX_BUFFERS 256
+#define MAX_REQUESTS MAX_CTBA_SLOTS
+
+struct __attribute__((__packed__)) region_descriptor {
+ /// Data base address
+ uint64_t dba;
+ uint32_t reserved;
+ /// Byte count and Interrupt bit (31)
+ uint32_t dbc;
+};
+
+struct __attribute__((__packed__)) command_table {
+ /// Command FIS
+ uint8_t cfis[64];
+ /// ATAPI command
+ uint8_t acmd[16];
+ uint8_t reserved[48];
+ /// Physical Descriptior Region Table entries
+ /// Can be up to 65536 entries, we limit this to 248 for now
+ /// (which limits the size of CommandTable to a single page).
+ struct region_descriptor prdt[248];
+};
+
+
+struct ahci_port;
+struct dev_queue;
+typedef void (*ahci_port_interrupt_handler_fn)(struct ahci_port*, struct dev_queue*);
+
+struct ahci_port {
+ bool is_initialized; //< Port is up and running, ready to read/write.
+ ahci_port_t port;
+ struct dma_mem fb;
+ struct dma_mem clb;
+ struct dma_mem ctba_mem[MAX_CTBA_SLOTS];
+ struct command_table* command_table[MAX_CTBA_SLOTS]; //< Points to ctba_mem[i].vaddr
+ size_t ncs; //< Number of command slots actually implemented
+ ahci_port_interrupt_handler_fn interrupt;
+ struct ahci_mgmt_binding *binding;
+ struct dma_mem identify_mem;
+ ata_identify_t identify; //< Points to identify_mem.vaddr, valid after port_identify() is done.
+};
+
+struct ahci_disk {
+ struct device_mem* bar5;
+ ahci_hba_t controller;
+ struct ahci_port ports[MAX_AHCI_PORTS];
+};
+
+
+enum RequestStatus {
+ RequestStatus_Unused = 0,
+ RequestStatus_InProgress = 1,
+ RequestStatus_Done = 2
+};
+
+struct dev_queue_request {
+ regionid_t region_id;
+ struct dma_mem region;
+ uint64_t command_slot;
+
+ lpaddr_t base;
+ size_t length;
+ bufferid_t buffer_id;
+
+ errval_t error;
+ enum RequestStatus status;
+};
+
+struct dev_queue {
+ struct ahci_port* port;
+ struct dma_mem buffers[MAX_BUFFERS];
+ struct dev_queue_request requests[MAX_REQUESTS];
+};
+
+/* ahci_port_init.h */
+errval_t blk_ahci_ports_init(struct ahci_disk *ad);
+errval_t blk_ahci_port_dma(struct ahci_port *port, uint64_t block, struct dma_mem *buffer, bool write);
+errval_t blk_ahci_port_dma_async(struct ahci_port *port, size_t slot, uint64_t block, lpaddr_t base, size_t length, bool write);
+
+lvaddr_t blk_ahci_get_bar5_vaddr(struct ahci_disk* ad);
+
+#endif // _BLK_AHCI_
--- /dev/null
+#include <barrelfish/barrelfish.h>
+#include <assert.h>
+#include <devif/queue.h>
+
+#include "blk_ahci.h"
+#include "ahci_dev.h" // TODO: get rid of this include
+#include "../dma_mem/dma_mem.h"
+#include "../blk_debug.h"
+
+static bool is_valid_buffer(struct dev_queue* dq, size_t slot)
+{
+ return !capref_is_null(dq->buffers[slot].frame);
+}
+
+static errval_t request_slot_alloc(struct dev_queue* dq, size_t* slot)
+{
+ assert(dq->port->ncs <= MAX_REQUESTS);
+
+ for (size_t i=0; i < dq->port->ncs; i++) {
+ struct dev_queue_request *dqr = &dq->requests[i];
+ if (dqr->status == RequestStatus_Unused) {
+ dqr->status = RequestStatus_InProgress;
+ *slot = i;
+ return SYS_ERR_OK;
+ }
+ }
+
+ return DEV_ERR_QUEUE_FULL;
+}
+
+static errval_t get_port(struct ahci_disk* hba, size_t port_num, struct ahci_port** p) {
+ assert(hba != NULL);
+ assert(port_num < MAX_AHCI_PORTS);
+ errval_t err = SYS_ERR_OK;
+
+ struct ahci_port* port = &hba->ports[port_num];
+ if (!port->is_initialized) {
+ return err_push(err, DEV_ERR_NOT_INITIALIZED);
+ }
+
+ *p = port;
+ return SYS_ERR_OK;
+}
+
+static errval_t init_queue(struct dev_queue** dq, struct ahci_port *port) {
+ struct dev_queue* queue = calloc(1, sizeof(struct dev_queue));
+ if (dq == NULL) {
+ return LIB_ERR_MALLOC_FAIL;
+ }
+
+ queue->port = port;
+ for (size_t i = 0; i< MAX_BUFFERS; i++) {
+ queue->buffers[i].frame = NULL_CAP;
+ }
+
+ *dq = queue;
+ return SYS_ERR_OK;
+}
+
+static bool slice_is_in_range(struct dma_mem *mem, lpaddr_t base, size_t length)
+{
+ bool lower_bound = mem->paddr <= base;
+ bool upper_bound = mem->paddr+mem->bytes >= base+length;
+
+ return lower_bound && upper_bound;
+}
+
+static uint64_t flags_get_block(uint64_t flags)
+{
+ return flags & ((1ULL<<49) - 1);
+}
+
+static bool flags_is_write(uint64_t flags) {
+ return (flags & (1ULL << 63)) > 0;
+}
+
+void devq_interrupt_handler(void* q);
+void devq_interrupt_handler(void* q)
+{
+ if (q == NULL) {
+ BLK_DEBUG("Ignored interrupt, device not yet initialized?\n");
+ return;
+ }
+ struct dev_queue *queue = q;
+ struct ahci_port *port = queue->port;
+
+ assert(port->interrupt != NULL);
+ port->interrupt(port, queue);
+}
+
+errval_t devq_create(void* st, char *device_name, uint64_t flags, void **queue)
+{
+ errval_t err = SYS_ERR_OK;
+
+ struct ahci_port* port;
+ err = get_port(st, flags, &port);
+ if (err_is_fail(err)) {
+ return err;
+ }
+
+ struct dev_queue *dq;
+ err = init_queue(&dq, port);
+ if (err_is_fail(err)) {
+ return err;
+ }
+
+ *queue = dq;
+
+ return err;
+}
+
+errval_t devq_destroy(void *qp)
+{
+ struct dev_queue *queue = qp;
+
+ // TODO: Wait for stuff to finish...!
+
+ // Clean-up memory:
+ for (size_t i = 0; i< MAX_BUFFERS; i++) {
+ dma_mem_free(&queue->buffers[i]);
+ }
+ free(qp);
+ return SYS_ERR_OK;
+}
+
+errval_t devq_enqueue(void *q, regionid_t region_id, lpaddr_t base, size_t length, bufferid_t buffer_id, uint64_t flags)
+{
+ struct dev_queue *queue = q;
+ assert(region_id < MAX_BUFFERS);
+ assert(is_valid_buffer(queue, region_id));
+ assert(base != 0);
+ assert(length >= 512);
+
+ struct dma_mem* mem = &queue->buffers[region_id];
+
+ if (!slice_is_in_range(mem, base, length)) {
+ return DEV_ERR_INVALID_BUFFER_ARGS;
+ }
+
+ size_t slot;
+ errval_t err = request_slot_alloc(queue, &slot);
+ if (err_is_fail(err)) {
+ return err;
+ }
+
+ struct dev_queue_request *dqr = &queue->requests[slot];
+ dqr->status = RequestStatus_InProgress;
+ dqr->buffer_id = buffer_id;
+ dqr->base = base;
+ dqr->length = length;
+ dqr->region_id = region_id;
+ dqr->command_slot = slot;
+
+ uint64_t block = flags_get_block(flags);
+ bool write = flags_is_write(flags);
+ return blk_ahci_port_dma_async(queue->port, slot, block, base, length, write);
+}
+
+errval_t devq_dequeue(void *q,
+ regionid_t* region_id,
+ lpaddr_t* base,
+ size_t* length,
+ bufferid_t* buffer_id)
+{
+ assert(q != NULL);
+ assert(region_id != NULL);
+ assert(base != NULL);
+ assert(length != NULL);
+ assert(length != NULL);
+
+ struct dev_queue *queue = q;
+
+ for (size_t i=0; i<queue->port->ncs; i++) {
+ struct dev_queue_request *dqr = &queue->requests[i];
+ if (dqr->status == RequestStatus_Done) {
+ *base = dqr->base;
+ *length = dqr->length;
+ *buffer_id = dqr->buffer_id;
+ *region_id = dqr->region_id;
+
+ dqr->status = RequestStatus_Unused;
+ return dqr->error;
+ }
+ }
+
+ return DEV_ERR_QUEUE_EMPTY;
+}
+
+errval_t devq_register(void *q,
+ struct capref cap,
+ regionid_t* region_id)
+{
+ errval_t err = DEV_ERR_REGISTER_BUFFER;
+ assert(!capref_is_null(cap));
+ struct dev_queue *queue = q;
+
+ for (size_t i=0; i<MAX_BUFFERS; i++) {
+ if (is_valid_buffer(q, i)) {
+ printf("Don't overwrite existing buffer\n");
+ continue;
+ }
+
+ struct dma_mem* mem = &queue->buffers[i];
+ err = dma_mem_from_capref(cap, mem);
+ if (err_is_fail(err)) {
+ DEBUG_ERR(err, "call failed");
+ return err_push(err, DEV_ERR_REGISTER_BUFFER);
+ }
+
+ *region_id = i;
+ return SYS_ERR_OK;
+ }
+
+ return err;
+}
+
+errval_t devq_remove(void *q, regionid_t region_id)
+{
+ assert(region_id < MAX_BUFFERS);
+ assert(q != NULL);
+ struct dev_queue *queue = q;
+
+ struct dma_mem* mem = &queue->buffers[region_id];
+ assert(!capref_is_null(mem->frame));
+
+ return dma_mem_free(mem);
+}
+
+errval_t devq_sync(void *q)
+{
+ return SYS_ERR_OK;
+}
+
+errval_t devq_control(void *q, uint64_t request, uint64_t value)
+{
+ return SYS_ERR_OK;
+}
--- /dev/null
+/*
+ * Copyright (c) 2011 ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#include <stdlib.h>
+#include <errors/errno.h>
+#include "sata_fis.h"
+
+void sata_h2d_fis_new(struct sata_fis_reg_h2d* fis, uint8_t command, uint64_t lba, uint16_t sectors)
+{
+ sata_h2d_fis_init(fis);
+ sata_h2d_set_command(fis, command);
+ sata_h2d_set_lba48(fis, lba);
+ sata_h2d_set_count(fis, sectors);
+}
+
+void sata_h2d_fis_init(struct sata_fis_reg_h2d* fis)
+{
+ fis->type = SATA_FIS_TYPE_H2D;
+
+ /* Device Shadow Register layout (see: [1])
+ * [ 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 ]
+ * [ - | L | - | DEV | HS3 | HS2 | HS1 | HS0 ]
+ *
+ * L is the address mode, cleared implies CHS addressing, set, LBA addressing
+ * DEV device select, cleared and set imply Device 0 and 1 resp.
+ * for SATA this should always be cleared (see: [2])
+ * HS3-HS0 are bits 28-25 of the LBA 28 (not used for LBA 48, see [3])
+ *
+ * [1] Serial ATA NSSD Rev. 1.0 (Sept 2008), section 6.3.1
+ * [2] Serial ATA Rev. 2.6 (15-February-2007), section 13.1, paragraph 2
+ * [3] ATA8-ACS Rev. 3f (December 11, 2006), section 7.1.5.2
+ */
+ fis->device |= (1 << 6);
+}
+
+void sata_h2d_set_command(struct sata_fis_reg_h2d* fis, uint8_t command)
+{
+ fis->command = command;
+ /* set bit to indicate update of command register (see [1])
+ *
+ * [1]: SATA Rev. 2.6 (15-February-2007), section 10.3.4
+ */
+ fis->specialstuff |= (1 << 7);
+}
+
+void sata_h2d_set_feature(struct sata_fis_reg_h2d* fis, uint8_t feature)
+{
+ fis->feature = feature;
+}
+
+void sata_h2d_set_lba28(struct sata_fis_reg_h2d* fis, uint32_t lba)
+{
+ fis->lba0 = lba & 0xFF;
+ fis->lba1 = (lba >> 8) & 0xFF;
+ fis->lba2 = (lba >> 16) & 0xFF;
+ fis->device = (fis->device & ~0x0F) | ((lba >> 24) & 0x0F);
+}
+
+void sata_h2d_set_lba48(struct sata_fis_reg_h2d* fis, uint64_t lba)
+{
+ fis->lba0 = lba & 0xFF;
+ fis->lba1 = (lba >> 8) & 0xFF;
+ fis->lba2 = (lba >> 16) & 0xFF;
+ fis->device &= 0xF0; // clear bits otherwise used by lba28
+
+ fis->lba3 = (lba >> 24) & 0xFF;
+ fis->lba4 = (lba >> 32) & 0xFF;
+ fis->lba5 = (lba >> 40) & 0xFF;
+}
+
+void sata_h2d_set_count(struct sata_fis_reg_h2d* fis, uint16_t count)
+{
+ fis->countl = count & 0xFF;
+ fis->counth = (count >> 8) & 0xFF;
+}
#ifndef _AHCI_SATA_FIS_H
#define _AHCI_SATA_FIS_H
+#include <stdint.h>
+
#define SATA_FIS_TYPE_H2D 0x27 // Register FIS - Host to Device
#define SATA_FIS_TYPE_D2H 0x34 // Register FIS - Device to Host
#define SATA_FIS_TYPE_DMAA 0x39 // DMA Activate FIS - Device to Host
#define SATA_FIS_TYPE_PIO 0x5F // PIO Setup FIS - Device to Host
#define SATA_FIS_TYPE_SDB 0xA1 // Set Device Bits FIS - Device to Host
+#define ATA_CMD_READ_PIO 0x20
+#define ATA_CMD_READ_PIO_EXT 0x24
+#define ATA_CMD_READ_DMA 0xC8
+#define ATA_CMD_READ_DMA_EXT 0x25
+#define ATA_CMD_WRITE_PIO 0x30
+#define ATA_CMD_WRITE_PIO_EXT 0x34
+#define ATA_CMD_WRITE_DMA 0xCA
+#define ATA_CMD_WRITE_DMA_EXT 0x35
+#define ATA_CMD_CACHE_FLUSH 0xE7
+#define ATA_CMD_CACHE_FLUSH_EXT 0xEA
+#define ATA_CMD_PACKET 0xA0
+#define ATA_CMD_IDENTIFY_PACKET 0xA1
+#define ATA_CMD_IDENTIFY 0xEC
+
struct sata_fis_reg_h2d {
unsigned char type;
unsigned char specialstuff;
unsigned char reserved3[4];
};
-errval_t sata_alloc_h2d_register_fis(void **fis, size_t *fis_size);
-errval_t sata_set_command(void *fis, uint8_t command);
-errval_t sata_set_feature(void *fis, uint8_t feature);
-errval_t sata_set_lba28(void *fis, uint32_t lba);
-errval_t sata_set_lba48(void *fis, uint64_t lba);
-errval_t sata_set_count(void *fis, uint16_t count);
+void sata_h2d_fis_new(struct sata_fis_reg_h2d* fis, uint8_t command, uint64_t lba, uint16_t sectors);
+void sata_h2d_fis_init(struct sata_fis_reg_h2d* fis);
+void sata_h2d_set_command(struct sata_fis_reg_h2d* fis, uint8_t command);
+void sata_h2d_set_feature(struct sata_fis_reg_h2d* fis, uint8_t feature);
+void sata_h2d_set_lba28(struct sata_fis_reg_h2d* fis, uint32_t lba);
+void sata_h2d_set_lba48(struct sata_fis_reg_h2d* fis, uint64_t lba);
+void sata_h2d_set_count(struct sata_fis_reg_h2d* fis, uint16_t count);
+
#endif // _AHCI_SATA_FIS_H
--- /dev/null
+#ifndef BLK_DEBUG_H_
+#define BLK_DEBUG_H_
+
+//#define BLK_DEBUG_ENABLE 1
+
+#if defined(BLK_DEBUG_ENABLE) || defined(GLOBAL_DEBUG)
+#define BLK_DEBUG(x...) printf("BLK: " x)
+#else
+#define BLK_DEBUG(x...) ((void)0)
+#endif
+
+#endif
--- /dev/null
+/*
+ * Copyright (c) 2014 ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#include <string.h>
+#include <barrelfish/barrelfish.h>
+#include "dma_mem.h"
+
+errval_t dma_mem_from_capref(struct capref frame, struct dma_mem *mem)
+{
+ errval_t err = SYS_ERR_OK;
+ if (mem == NULL) {
+ return DMA_ERR_ARG_INVALID;
+ }
+
+ struct frame_identity id;
+ err = invoke_frame_identify(frame, &id);
+ if (err_is_fail(err)) {
+ DEBUG_ERR(err, "invoke frame id");
+ return err;
+ }
+
+ mem->paddr = id.base;
+ mem->bytes = id.bytes;
+ mem->requested = id.bytes;
+ mem->frame = frame;
+
+ void *addr;
+ err = vspace_map_one_frame_attr(&addr, mem->bytes, mem->frame, VREGION_FLAGS_READ_WRITE,
+ NULL, NULL);
+ if (err_is_fail(err)) {
+ dma_mem_free(mem);
+ return err;
+ }
+
+ mem->vaddr = (lvaddr_t)addr;
+
+ return SYS_ERR_OK;
+}
+
+/**
+ * \brief allocates and maps a memory region to be used for DMA purposes
+ *
+ * \param bytes minimum size of the memory region in bytes
+ * \param flags VREGION flags how the region gets mapped
+ * \param mem returns the mapping information
+ *
+ * \returns SYS_ERR_OK on success
+ * errval on error
+ */
+errval_t dma_mem_alloc(size_t bytes, struct dma_mem *mem)
+{
+ errval_t err = SYS_ERR_OK;
+ mem->requested = bytes;
+ bytes = ROUND_UP(BASE_PAGE_SIZE, bytes);
+
+ if (mem == NULL) {
+ return DMA_ERR_ARG_INVALID;
+ }
+
+ err = frame_alloc(&mem->frame, bytes, &mem->bytes);
+ if (err_is_fail(err)) {
+ return err;
+ }
+
+ struct frame_identity id;
+ err = invoke_frame_identify(mem->frame, &id);
+ if (err_is_fail(err)) {
+ dma_mem_free(mem);
+ return err;
+ }
+
+ mem->paddr = id.base;
+
+ void *addr;
+ err = vspace_map_one_frame_attr(&addr, mem->bytes, mem->frame, VREGION_FLAGS_READ_WRITE,
+ NULL, NULL);
+ if (err_is_fail(err)) {
+ dma_mem_free(mem);
+ return err;
+ }
+
+ mem->vaddr = (lvaddr_t)addr;
+
+ return SYS_ERR_OK;
+}
+
+/**
+ * \brief tries to free the allocated memory region
+ *
+ * \returns SYS_ERR_OK on success
+ * errval on error
+ */
+errval_t dma_mem_free(struct dma_mem *mem)
+{
+ errval_t err;
+
+ if (mem->vaddr) {
+ err = vspace_unmap((void*)mem->vaddr);
+ if (err_is_fail(err)) {
+ USER_PANIC_ERR(err, "call failed.");
+ }
+ }
+
+ if (!capref_is_null(mem->frame)) {
+ err = cap_destroy(mem->frame);
+ if (err_is_fail(err)) {
+ if (err_is_fail(err)) {
+ USER_PANIC_ERR(err, "call failed.");
+ }
+ }
+ }
+
+ memset(mem, 0, sizeof(*mem));
+
+ return SYS_ERR_OK;
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#ifndef LIB_DMA_MEM_UTILS_H
+#define LIB_DMA_MEM_UTILS_H
+
+#include <barrelfish/types.h>
+#include <barrelfish/capabilities.h>
+
+struct dma_mem
+{
+ lvaddr_t vaddr; ///< virtual address of the mapped region
+ lpaddr_t paddr; ///< physical address of the underlying frame
+ uint64_t bytes; ///< size of the region in bytes
+ uint64_t requested; ///< requested size of the region in bytes (<= bytes)
+ struct capref frame; ///< frame capability backing this region
+};
+
+/**
+ * \brief allocates and maps a memory region to be used for DMA purposes
+ *
+ * \param bytes minimum size of the memory region in bytes
+ * \param mem returns the mapping information
+ *
+ * \returns SYS_ERR_OK on success
+ * errval on error
+ */
+errval_t dma_mem_alloc(size_t bytes, struct dma_mem *mem);
+
+/**
+ * \brief Initializes a dma_mem struct for a given capref
+ *
+ * \param frame The frame with a reference to memory
+ *
+ * \returns SYS_ERR_OK on success
+ * errval on error
+ */
+errval_t dma_mem_from_capref(struct capref frame, struct dma_mem *mem);
+
+/**
+ * \brief tries to free the allocated memory region
+ *
+ * \returns SYS_ERR_OK on success
+ * errval on error
+ */
+errval_t dma_mem_free(struct dma_mem *mem);
+
+
+
+#endif /* LIB_DMA_MEM_UTILS_H */
bar->frame_cap[nc] = cap;
if (nc == 0) {
struct frame_identity id = { .base = 0, .bytes = 0 };
- invoke_frame_identify(cap, &id);
+ err = invoke_frame_identify(cap, &id);
+ if (err_is_fail(err)) {
+ USER_PANIC_ERR(err, "frame identify failed.");
+ }
bar->paddr = id.base;
bar->bits = log2ceil(id.bytes);
bar->bytes = id.bytes * ncaps;
}
// initialize the device. We have all the caps now
+ PCI_CLIENT_DEBUG("Succesfully done with pci init.\n");
init_func(bars, nbars);
err = SYS_ERR_OK;
if machine.name == "sbrinz1" or machine.name == "sbrinz2" \
or machine.name == "tomme1" or machine.name == "tomme2" \
- or machine.name == "appenzeller" or is_babybel == 1 :
+ or machine.name == "tilsiter1" or machine.name == "appenzeller" \
+ or is_babybel == 1:
# PCI allocation broken, use BIOS plan
m.add_module("%s/sbin/pci" % a, ["auto",
"skb_bridge_program=bridge_bios"] + machine.get_pci_args())
except NotImplementedError:
passed = None
if passed is False:
- debug.log('Test %s FAILED' % test.name)
+ debug.log('Test %s FAILED %s' % (test.name, '(' + result.reason() + ')') )
retval = False
elif passed:
debug.verbose('Test %s PASSED' % test.name)
'xphi_tickrate' : 1140,
'xphi_ram_gb' : 6},
+ 'tilsiter1': {'ncores' : 2,
+ 'machine_name' : 'tilsiter1',
+ 'bootarch' : 'x86_64',
+ 'buildarchs' : ['x86_64'],
+ 'cores_per_socket': 2,
+ 'perfcount_type' : 'intel',
+ 'tickrate' : 2500,
+ 'boot_timeout' : 120},
+
'nos4-32' : {'ncores' : 4,
'machine_name' : 'nos4',
'bootarch' : 'x86_32',
'perfcount_type': 'intel',
'tickrate' : 1870,
'boot_timeout': 360},
-
+
'vacherin-32': {'ncores' : 4,
'machine_name' : 'vacherin',
'bootarch' : 'x86_32',
'perfcount_type' : 'intel',
'tickrate' : 2500,
'boot_timeout' : 360},
-
'babybel4-32': {'ncores' : 20,
'machine_name' : 'babybel4',
'bootarch' : 'x86_32',
'perfcount_type' : 'intel',
'tickrate' : 1140,
'host_tickrate' : 2500,
- 'boot_timeout' : 360},
+ 'boot_timeout' : 360},
'xeon_phi_2': {'ncores' : 64,
'nphi' : 2,
'host_ncores' : 20,
'tickrate' : 1140,
'host_tickrate' : 2500,
'boot_timeout' : 360},
-
+
# SK: For Python 2.7
- # }.items() + {
+ # }.items() + {
# 'brie%s' % b: {
# 'ncores' : 4,
# 'machine_name' : ('brie%s' % b),
debug.current_level = options.debuglevel
return dirs
-
+
def main(dirs):
for dirname in dirs:
debug.verbose('reprocess results')
harness.process_results(test, dirname)
-
if __name__ == "__main__":
main(parse_args())
from stats import Stats
class ResultsBase(object):
- def __init__(self, name=None):
+ def __init__(self, name=None, reason=""):
self.name = name
+ self.fail_reason = reason
+
+ def reason(self):
+ return self.fail_reason
def passed(self):
"""Returns true iff the test is considered to have passed."""
class PassFailResult(ResultsBase):
"""Stores results of test that is purely pass/fail."""
- def __init__(self, passed):
- super(PassFailResult, self).__init__()
+ def __init__(self, passed, reason=""):
+ super(PassFailResult, self).__init__(reason=reason)
self.passfail = passed
def passed(self):
self.errors = errors
self.name = name
+ def reason(self):
+ return str(errors)
+
def passed(self):
return len(self.errors) == 0
for r in self.rows:
fh.write('\t'.join(map(str, r)) + '\n')
- def mark_failed(self):
+ def mark_failed(self, reason):
"""Mark this test as having failed."""
self.failed = True
+ self.fail_reason = reason
def add_row(self, row):
assert(len(row) == len(self.colnames))
--- /dev/null
+##########################################################################
+# Copyright (c) 2016, ETH Zurich.
+# All rights reserved.
+#
+# This file is distributed under the terms in the attached LICENSE file.
+# If you do not find this file, copies can be found by writing to:
+# ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
+##########################################################################
+
+import re, datetime
+import debug, tests
+from common import TestCommon, TimeoutError
+from results import RowResults
+
+BLK_TEST_TIMEOUT = datetime.timedelta(minutes=8) # XXX: tilsiter1 needs a bit longer (slow write speeds?)
+
+bandwidth = {
+ 'tilsiter1': {
+ 'read': {
+ 512: 32.67,
+ 1024: 50.08,
+ 2048: 90.15,
+ 4096: 154.21,
+ 8192: 236.92,
+ 16384: 318.05,
+ 32768: 379.68,
+ 65536: 416.99,
+ 131072: 428.64,
+ 262144: 439.88,
+ 524288: 495.27,
+ 1048576: 528.16,
+ 2097152: 546.71
+ },
+ 'write': {
+ 512: 5.41,
+ 1024: 1.70,
+ 2048: 3.32,
+ 4096: 6.68,
+ 8192: 26.50,
+ 16384: 49.92,
+ 32768: 88.74,
+ 65536: 143.63,
+ 131072: 212.54,
+ 262144: 281.08,
+ 524288: 377.55,
+ 1048576: 436.48,
+ 2097152: 49.25,
+ },
+ },
+
+ 'vacherin': {
+ 'read': {
+ 512: 15.24,
+ 1024: 15.35,
+ 2048: 30.32,
+ 4096: 57.29,
+ 8192: 102.01,
+ 16384: 167.28,
+ 32768: 237.24,
+ 65536: 316.74,
+ 131072: 361.41,
+ 262144: 405.19,
+ 524288: 490.97,
+ 1048576: 415.05,
+ 2097152: 424.57
+ },
+ 'write': {
+ 512: 15.20,
+ 1024: 12.55,
+ 2048: 26.87,
+ 4096: 51.89,
+ 8192: 93.21,
+ 16384: 152.43,
+ 32768: 213.55,
+ 65536: 296.29,
+ 131072: 365.72,
+ 262144: 412.98,
+ 524288: 440.78,
+ 1048576: 454.21,
+ 2097152: 462.02,
+ }
+ },
+
+ 'babybel1': {
+ 'read': {
+ 512: 43.42,
+ 1024: 25.56,
+ 2048: 31.16,
+ 4096: 55.63,
+ 8192: 85.02,
+ 16384: 114.35,
+ 32768: 105.40,
+ 65536: 147.15,
+ 131072: 414.57,
+ 262144: 482.58,
+ 524288: 515.73,
+ 1048576: 534.20,
+ 2097152: 544.49,
+ 4194304: 549.79,
+ },
+ 'write': {
+ 512: 42.52,
+ 1024: 38.18,
+ 2048: 41.29,
+ 4096: 75.26,
+ 8192: 221.12,
+ 16384: 267.90,
+ 32768: 426.09,
+ 65536: 494.36,
+ 131072: 516.22,
+ 262144: 526.34,
+ 524288: 533.67,
+ 1048576: 533.67,
+ 2097152: 522.25,
+ 4194304: 523.27
+ },
+ },
+
+ 'babybel2': {
+ 'read': {
+ 512: 43.32,
+ 1024: 14.08,
+ 2048: 31.10,
+ 4096: 55.14,
+ 8192: 83.53,
+ 16384: 108.78,
+ 32768: 95.90,
+ 65536: 115.02,
+ 131072: 159.26,
+ 262144: 427.62,
+ 524288: 514.74,
+ 1048576: 533.14,
+ 2097152: 503.16
+ },
+ 'write': {
+ 512: 43.09,
+ 1024: 22.52,
+ 2048: 41.00,
+ 4096: 75.28,
+ 8192: 220.75,
+ 16384: 268.17,
+ 32768: 416.18,
+ 65536: 497.10,
+ 131072: 519.22,
+ 262144: 529.46,
+ 524288: 534.73,
+ 1048576: 537.95,
+ 2097152: 518.22,
+ },
+ },
+
+ 'babybel3': {
+ 'read': {
+ 512: 43.53,
+ 1024: 25.71,
+ 2048: 31.09,
+ 4096: 55.30,
+ 8192: 83.35,
+ 16384: 107.88,
+ 32768: 95.66,
+ 65536: 115.43,
+ 131072: 161.73,
+ 262144: 428.30,
+ 524288: 514.24,
+ 1048576: 532.87,
+ 2097152: 504.82,
+ },
+ 'write': {
+ 512: 42.86,
+ 1024: 38.40,
+ 2048: 41.43,
+ 4096: 75.83,
+ 8192: 225.01,
+ 16384: 265.78,
+ 32768: 428.13,
+ 65536: 495.27,
+ 131072: 513.26,
+ 262144: 528.42,
+ 524288: 532.61,
+ 1048576: 532.61,
+ 2097152: 525.31,
+ }
+ },
+
+ 'babybel4': {
+ 'read': {
+ 512: 43.46,
+ 1024: 20.47,
+ 2048: 23.44,
+ 4096: 42.33,
+ 8192: 64.98,
+ 16384: 76.50,
+ 32768: 64.45,
+ 65536: 77.69,
+ 131072: 111.00,
+ 262144: 381.98,
+ 524288: 492.54,
+ 1048576: 512.53,
+ 2097152: 451.34
+ },
+ 'write': {
+ 512: 42.49,
+ 1024: 38.40,
+ 2048: 41.47,
+ 4096: 76.96,
+ 8192: 225.20,
+ 16384: 265.51,
+ 32768: 430.19,
+ 65536: 496.18,
+ 131072: 517.22,
+ 262144: 529.46,
+ 524288: 534.73,
+ 1048576: 534.73,
+ 2097152: 524.29
+ }
+ }
+}
+
+class BlkTests(TestCommon):
+
+ def __init__(self, options):
+ super(BlkTests, self).__init__(options)
+
+ def get_module_name(self):
+ return "ahci_test"
+
+ def boot(self, *args):
+ super(BlkTests, self).boot(*args)
+ self.set_timeout(BLK_TEST_TIMEOUT)
+
+ def get_modules(self, build, machine):
+ self.machine = machine.name
+ vendor_device = "0:0"
+ if self.machine == "tilsiter1":
+ vendor_device = "8086:a102"
+ if self.machine == "vacherin":
+ vendor_device = "8086:8c02"
+ elif self.machine.startswith("babybel"):
+ vendor_device = "8086:1d02"
+
+ modules = super(BlkTests, self).get_modules(build, machine)
+ modules.add_module(self.get_module_name(), ["manual", vendor_device, self.OP])
+
+ return modules
+
+ def is_finished(self, line):
+ return line.startswith("AHCI testing completed.")
+
+ def process_data(self, testdir, rawiter):
+ self.regex = re.compile(self.REGEX)
+ result = RowResults(['op', 'buffer', 'block', 'bandwidth'])
+ if not bandwidth.has_key(self.machine):
+ result.mark_failed('No data about this disk, please set the initial performance values.')
+ return result
+
+ matches = 0
+ for line in rawiter:
+ match = self.regex.match(line)
+ if match:
+ matches += 1
+
+ buffer_size, bs, bw = match.groups()
+ buffer_size = int(buffer_size)
+ bs = int(bs)
+ bw = float(bw)
+ operation = self.OP.lower()
+ if not bandwidth[self.machine].has_key(operation):
+ result.mark_failed('No data about this benchmark, please set the initial performance values.')
+ return result
+ if not bandwidth[self.machine][operation].has_key(bs):
+ result.mark_failed('No data for {} with bs {}.'.format(operation, bs))
+ return result
+
+ lower_bound = bandwidth[self.machine][operation][bs] * (1 - 0.15)
+ upper_bound = bandwidth[self.machine][operation][bs] * (1 + 0.20)
+
+ result.add_row((operation, buffer_size, bs, bw))
+ if bw <= lower_bound:
+ error = "{} for {} bytes blocks not within expected range (was {}, should be >= {}).".format(operation, bs, bw, lower_bound)
+ debug.log(error)
+ result.mark_failed(reason=error)
+ elif bw >= upper_bound:
+ error = "Achieved {} bandwidth for {} bytes blocks was better ({}) than expected ({}).".format(operation, bs, bw, upper_bound)
+ debug.log(error)
+ debug.log("This is good, if you can explain it! Adjust the bandwidth numbers in blk_tests.py and re-run the test.")
+ result.mark_failed(reason=error)
+ else:
+ pass
+
+ if line.startswith("AHCI testing completed.") and matches > 0:
+ return result
+
+ result.mark_failed('Did not see end of test or got no bandwidth numbers.')
+ return result
+
+@tests.add_test
+class BlkAhciWriteBWTest(BlkTests):
+ ''' AHCI Driver Write Bandwidth Test'''
+ name = "blk_read_test"
+ OP = "read"
+ REGEX = r"\[ahci_perf_sequential\] Read sequential size (\d+) bs (\d+): (\d+\.\d+) \[MB/s\]"
+
+@tests.add_test
+class BlkAhciReadBWTest(BlkTests):
+ ''' AHCI Driver Read Bandwidth Test'''
+ name = "blk_write_test"
+ OP = "write"
+ REGEX = r"\[ahci_perf_sequential\] Write sequential size (\d+) bs (\d+): (\d+\.\d+) \[MB/s\]"
+
+@tests.add_test
+class BlkAhciVerifyTest(BlkTests):
+ ''' AHCI Driver Correctness test '''
+ name = "blk_verify_test"
+ OP = "verify"
+ REGEX = r"\[ahci_verify_sequential\] SUCCESS \((\d+) (\d+)\)"
+ TESTS = 14
+
+ def process_data(self, testdir, rawiter):
+ self.regex = re.compile(self.REGEX)
+
+ matches = 0
+ for line in rawiter:
+ match = self.regex.match(line)
+ if match:
+ matches += 1
+
+ if matches == self.TESTS:
+ return PassFailResult(True)
+ elif matches < self.TESTS:
+ return PassFailResult(False, "Some block/buffer size checks did not report back with SUCCESS.")
+ elif matches > self.TESTS:
+ return PassFailResult(False, "Got more SUCCESS lines than expected. If you changed the test you may need to increase self.TESTS.")
+ else:
+ assert "Should not come here"
// Parse CMD Arguments
bool got_apic_id = false;
bool do_video_init = false;
- vtd_force_off = false;
+ vtd_force_off = true;
for (int i = 1; i < argc; i++) {
if(sscanf(argv[i], "apicid=%" PRIuPTR, &my_hw_id) == 1) {
got_apic_id = true;
+++ /dev/null
---------------------------------------------------------------------------
--- Copyright (c) 2007-2011, ETH Zurich.
--- All rights reserved.
---
--- This file is distributed under the terms in the attached LICENSE file.
--- If you do not find this file, copies can be found by writing to:
--- ETH Zurich D-INFK, Universitaetsstr. 6, CH-8092 Zurich. Attn: Systems Group.
---
--- Hakefile for /usr/drivers/ahci
---
---------------------------------------------------------------------------
-
-[ build application { target = "ahcid",
- cFiles = [ "ahcid.c", "ahcid_hwinit.c" ],
- flounderBindings = [ "ahci_mgmt" ],
- mackerelDevices = [ "ahci_hba", "ahci_port", "ata_identify" ],
- addLibraries = [ "pci", "skb", "ahci" ]
- }
-]
-
+++ /dev/null
-/*
- * Copyright (c) 2011 ETH Zurich.
- * All rights reserved.
- *
- * This file is distributed under the terms in the attached LICENSE file.
- * If you do not find this file, copies can be found by writing to:
- * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
- */
-
-#include <stdio.h>
-#include <string.h>
-#include "ahcid.h"
-#include <barrelfish/waitset.h>
-#include <barrelfish/syscalls.h>
-#include <barrelfish/nameservice_client.h>
-#include <pci/pci.h>
-#include <if/ahci_mgmt_defs.h>
-#include <skb/skb.h>
-#include <ahci/sata_fis.h>
-#include <ahci/ahci_dma_pool.h>
-#include <dev/ahci_hba_dev.h>
-#include <dev/ata_identify_dev.h>
-#include "ahcid_debug.h"
-
-#define PCI_DEVICE_ICH9R_82801IR 0x2922
-#define PCI_DEVICE_ICH10_82801JI 0x3a22
-#define PCI_DEVICE_SB7x0 0x4390 // also SB8x0 SB9x0
-
-static ahci_hba_t controller;
-static uint8_t num_ports = -1;
-static struct ahcid_port_info **ports;
-
-static struct ahci_mgmt_binding **port_bindings;
-
-static char *my_service_name = "ahcid";
-
-static struct device_mem hbabar;
-
-#if defined(AHCI_SERVICE_DEBUG) || defined(GLOBAL_DEBUG)
- static bool initialized = false;
-#endif
-
-struct device_id {
- uint32_t vendor;
- uint32_t device;
-};
-
-static void rx_list_call(struct ahci_mgmt_binding *b)
-{
- AHCID_DEBUG("got list call\n");
- uint8_t port;
- uint8_t current_num_ports = (num_ports == (uint8_t)-1 ? 0 : num_ports);
- uint8_t next_port = 0;
- uint8_t *port_ids = malloc(current_num_ports);
- for (port = 0; port < current_num_ports; port++) {
- if (ports[port] && ports[port]->status == AHCID_PORT_STATUS_IDLE) {
- port_ids[next_port++] = port;
- }
- else if (ports[port]) {
- AHCID_DEBUG("skipping port %u with status %d\n",
- (unsigned int)port, (int)ports[port]->status);
- }
- }
- AHCID_DEBUG("responding to list call with %u port_ids\n", (unsigned int)next_port);
- ahci_mgmt_list_response__tx(b, MKCLOSURE(free, port_ids), port_ids, next_port);
-}
-
-static void rx_identify_call(struct ahci_mgmt_binding *b, uint8_t port_id)
-{
- AHCID_DEBUG("got identify call\n");
- if (ports[port_id] && ports[port_id]->status == AHCID_PORT_STATUS_IDLE) {
- AHCID_DEBUG("responding...\n");
- ahci_mgmt_identify_response__tx(b, NOP_CONT,
- (uint8_t*)ports[port_id]->identify_data, 512);
- }
- else {
- AHCID_DEBUG("not responding...\n");
- }
-}
-
-static void rx_open_call(struct ahci_mgmt_binding *b, uint8_t port_id)
-{
- AHCID_DEBUG("got open call\n");
- if (ports[port_id] == NULL) {
- ahci_mgmt_open_response__tx(b, NOP_CONT, AHCI_ERR_PORT_INVALID,
- NULL_CAP, 0, 0);
- return;
- }
- if (ports[port_id]->binding != NULL) {
- ahci_mgmt_open_response__tx(b, NOP_CONT, AHCI_ERR_PORT_BUSY,
- NULL_CAP, 0, 0);
- return;
- }
- ports[port_id]->binding = b;
- // find correct frame cap for port. should be a single cap as long as FRAME_SIZE > 0x80b.
- uint32_t cap_size = hbabar.bytes / hbabar.nr_caps;
- uint32_t offset = ahcid_port_offset(port_id);
- int cap_index = 0;
- while (offset >= cap_size) { cap_index++; offset -= cap_size; }
-
- ahci_mgmt_open_response__tx(b, NOP_CONT, SYS_ERR_OK,
- hbabar.frame_cap[cap_index], offset, ahci_hba_cap_rd(&controller));
-}
-
-static void rx_close_call(struct ahci_mgmt_binding *b, uint8_t port_id)
-{
- AHCID_DEBUG("got close call\n");
- if (ports[port_id] == NULL) {
- ahci_mgmt_close_response__tx(b, NOP_CONT, AHCI_ERR_PORT_INVALID);
- return;
- }
- errval_t retval = AHCI_ERR_PORT_MISMATCH;
- if (ports[port_id]->binding == b) {
- ports[port_id]->binding = NULL;
- retval = SYS_ERR_OK;
- }
- ahci_mgmt_close_response__tx(b, NOP_CONT, retval);
- return;
-}
-
-static struct ahci_mgmt_rx_vtbl rx_vtbl = {
- .list_call = rx_list_call,
- .list_response = NULL,
- .identify_call = rx_identify_call,
- .identify_response = NULL,
- .open_call = rx_open_call,
- .open_response = NULL,
- .close_call = rx_close_call,
- .close_response = NULL,
- .command_completed = NULL,
-};
-
-static void export_cb(void *st, errval_t err, iref_t iref)
-{
- if (err_is_fail(err)) {
- USER_PANIC_ERR(err, "export failed");
- }
-
- AHCID_DEBUG("service exported at iref %u\n", iref);
-
- // register this iref with the name service
- err = nameservice_register(my_service_name, iref);
- if (err_is_fail(err)) {
- USER_PANIC_ERR(err, "nameservice_register failed");
- }
-}
-
-static errval_t connect_cb(void *st, struct ahci_mgmt_binding *b)
-{
- AHCID_DEBUG("got a connection!\n");
-
- // copy my message receive handler vtable to the binding
- b->rx_vtbl = rx_vtbl;
-
- // accept the connection (we could return an error to refuse it)
- return SYS_ERR_OK;
-}
-
-static void do_identify(struct ahcid_port_info *port)
-{
- errval_t r;
-
- // allocate frame for command table
- struct ahci_dma_region *command_table;
- r = ahci_dma_region_alloc(0x90, &command_table);
- assert(err_is_ok(r));
-
- // write command into command list
- ahci_port_chdr_t commandlist = (ahci_port_chdr_t)port->command_list->vaddr;
- memset(commandlist, 0, ahci_port_chdr_size);
-
-#if defined(AHCI_SERVICE_DEBUG) || defined(GLOBAL_DEBUG)
- char buf[4096];
- ahci_port_chdr_prtval(buf, 4096, commandlist);
- AHCID_DEBUG("Command Header: \n");
- puts(buf);
-#endif
-
- // allocate dma region for identify result
- struct ahci_dma_region **prd = calloc(1, sizeof(struct ahci_dma_region*));
- r = ahci_dma_region_alloc(512, prd);
- assert(err_is_ok(r));
-
- memset((*prd)->vaddr, 0, 512);
- port->prdts = prd;
- port->prdt_count = 1;
-
- ahci_port_chdr_w_insert(commandlist, 0); // dma on receive
- ahci_port_chdr_a_insert(commandlist, 0);
- ahci_port_chdr_cfl_insert(commandlist, 5);
-
- // set dma region for identify result
- ahci_port_chdr_prdtl_insert(commandlist, 1); // we use 1 PRD
- ahci_port_chdr_ctba_insert(commandlist, (uint32_t)command_table->paddr);
- ahci_port_chdr_ctbau_insert(commandlist, (uint32_t)(command_table->paddr>>32));
-
-
- //char buf[4096];
-#if defined(AHCI_SERVICE_DEBUG) || defined(GLOBAL_DEBUG)
- ahci_port_chdr_prtval(buf, 4096, commandlist);
- AHCID_DEBUG("Command Header: \n");
- puts(buf);
-#endif
-
- // fill command table
- memset(command_table->vaddr, 0, 0x90);
-
- // FIS
- struct sata_fis_reg_h2d *fis = command_table->vaddr;
- fis->type = SATA_FIS_TYPE_H2D;
- fis->command = 0xEC /* ATA_CMD_IDENTIFY */;
- fis->device = 0;
- fis->specialstuff = 0x80 /* command */;
-
- // place PRD into PRDT which is at offset 0x80
- uint32_t *prdt_entry = command_table->vaddr + 0x80;
- prdt_entry[0] = (*prd)->paddr;
- prdt_entry[3] = 511; // again this +1 thingy
-
- // ensure command processing is on
- ahci_port_cmd_t cmd = ahci_port_cmd_rd(&port->port);
- assert(ahci_port_cmd_cr_extract(cmd) == 1);
-
- // issue command in slot 0
- ahci_port_ci_wr(&port->port, 1);
-
- AHCID_DEBUG("Issued IDENTIFY command\n");
-
- port->status = AHCID_PORT_STATUS_IDENTIFY_PENDING;
-}
-
-
-static void ahci_init(struct device_mem *bar_info, int nr_allocated_bars)
-{
- // re-usable vars
- int i;
- char buf[4096];
- errval_t r;
-
- // registers
- ahci_hba_ghc_t ghc;
-
- // find BAR and map device data
- AHCID_DEBUG("nr_allocated_bars = %d\n", nr_allocated_bars);
- assert(nr_allocated_bars >= 1);
- // Although the AHCI specification requires the AHCI memory region to be in
- // BAR 5 (BAR 0 to 4 are used for legacy IDE mode) the QEMU AHCI emulation
- // incorrectly uses BAR 0. Because of this, ahcid consults both BAR 0 and
- // BAR 5 to find the HBA's memory mapped I/O region.
- if (nr_allocated_bars == 1) { // handle QEMU
- memcpy(&hbabar, &bar_info[0], sizeof(struct device_mem));
- } else if (nr_allocated_bars == 6) { // handle HW (AHCI in BAR 5)
- memcpy(&hbabar, &bar_info[5], sizeof(struct device_mem));
- } else {
- AHCID_DEBUG("strange device... not supported\n");
- abort();
- }
-
- map_device(&hbabar);
- ahci_hba_initialize(&controller, (void *)(hbabar.vaddr));
- AHCID_DEBUG("accessing conf regs starting at %p\n",
- (void *)(hbabar.vaddr));
- AHCID_DEBUG("physical address of conf regs: %p\n",
- (void *)(hbabar.paddr));
-
- // first of all, reset the device for fun and profit
- ghc = ahci_hba_ghc_rd(&controller);
- ghc = ahci_hba_ghc_hr_insert(ghc, 1);
- AHCID_DEBUG("Resetting HBA (setting ghc = %x)...\n", ghc);
- ahci_hba_ghc_wr(&controller, ghc);
-
- // spec: "the device shall reset this bit to '0' " so we will wait
- while (1) {
- ghc = ahci_hba_ghc_rd(&controller);
- if (ahci_hba_ghc_hr_extract(ghc) == 0) {
- AHCID_DEBUG("reset done\n");
- break;
- }
- sys_yield(CPTR_NULL);
- }
-
- // set HBA into AHCI mode
- AHCID_DEBUG("Setting controller into AHCI mode\n");
- ghc = ahci_hba_ghc_rd(&controller);
- ghc = ahci_hba_ghc_ae_insert(ghc, 1);
- ahci_hba_ghc_wr(&controller, ghc);
-
- // disable interrupts for the moment during setup
- ghc = ahci_hba_ghc_rd(&controller);
- if (ahci_hba_ghc_ie_extract(ghc) == 1) {
- AHCID_DEBUG("Interrupts are enabled. Disabling them during setup\n");
- ghc = ahci_hba_ghc_ie_insert(ghc, 1);
- ahci_hba_ghc_wr(&controller, ghc);
- } else {
- AHCID_DEBUG("Interrupts are disabled (as expected after a reset)\n");
- }
-
- // get number of ports the HBA supports
- // AHCI spec 1.3, section 3.1.1; bits 0-4 specify number of ports, 0 being 1 port
- num_ports = ahci_hba_cap_np_extract(ahci_hba_cap_rd(&controller)) + 1;
- AHCID_DEBUG("HBA supports %d ports\n", num_ports);
-
- ports = calloc(num_ports, sizeof(struct ahcid_port_info*));
- port_bindings = calloc(num_ports, sizeof(struct ahci_mgmt_binding*));
-
- // enable interrupts again
- ghc = ahci_hba_ghc_rd(&controller);
- ghc = ahci_hba_ghc_ie_insert(ghc, 1);
- AHCID_DEBUG("Enabling HBA Interrupts\n");
- ahci_hba_ghc_wr(&controller, ghc);
-
- // init dma pool
- r = ahci_dma_pool_init(1024 * 1024);
-
- // initialize ports
- r = ahcid_ports_init(ports, num_ports, ahci_hba_pi_rd(&controller),
- (void *)(hbabar.vaddr));
-
- for (i = 0; i < num_ports; i++) {
- if (ports[i] != NULL) {
- // read port state
- ahci_port_ssts_t ssts = ahci_port_ssts_rd(&ports[i]->port);
- if (ahci_port_ssts_det_extract(ssts) == ahci_port_detect) {
- // we have a device
- ahci_port_speed_prtval(buf, 4096, ahci_port_ssts_spd_extract(ssts));
- AHCID_DEBUG("Device detected! Port: %d Type: %s\n", i, buf);
-
- // enable all interrupts of this port
- ahci_port_ie_wr(&ports[i]->port, -1);
-
- // Clear PxSERR
- ahci_port_serr_wr(&ports[i]->port, -1);
-
- // wait until device is ready
- ahci_port_tfd_pr(buf, 4096, &ports[i]->port);
- AHCID_DEBUG("TFD: \n%s\n", buf);
- AHCID_DEBUG("Waiting for device to become ready\n");
- uint32_t taskfile;
- while (1) {
- taskfile = ahci_port_tfd_rd(&ports[i]->port);
- // 7: BSY, 3: DRQ, 0: ERR
- if ((ahci_port_tfd_sts_extract(taskfile) & 0x89) == 0) {
- AHCID_DEBUG("Device ready\n");
- break;
- }
- }
-
- // start running commands
- ahci_port_cmd_t cmd = ahci_port_cmd_rd(&ports[i]->port);
- cmd = ahci_port_cmd_st_insert(cmd, 1);
- ahci_port_cmd_wr(&ports[i]->port, cmd);
-
- // send identify command to device
- do_identify(ports[i]);
- }
- }
- }
-
- // export ahci management interface
- ahci_mgmt_export(NULL /* state pointer for connect/export callbacks */,
- export_cb, connect_cb, get_default_waitset(),
- IDC_EXPORT_FLAGS_DEFAULT);
-
-#if defined(AHCI_SERVICE_DEBUG) || defined(GLOBAL_DEBUG)
- initialized = true;
-#endif
-
-}
-
-static void ahci_interrupt_handler(void *arg)
-{
- uint32_t hba_irq_state = ahci_hba_is_rd(&controller);
-
- if (hba_irq_state == 0) {
-#ifdef AHCI_SERVICE_DEBUG
- static uint32_t verb_count = 0;
- verb_count++;
- if (verb_count % 1 == 0) {
- AHCID_DEBUG("Ignoring foreign interrupt\n");
- AHCID_DEBUG("Port 0 Interrupt State: %"PRIu32"\n",
- ahci_port_is_rd(&ports[0]->port));
- AHCID_DEBUG("Port 0 Command Issue state: %"PRIu32"\n",
- ahci_port_ci_rd(&ports[0]->port));
- }
-#endif
- // just ignore foreign interrupts
- return;
- }
-
- bool interrupted_ports[32] = {false};
- ahci_port_is_t interrupt_state[32] = {0};
-
- int i = 0;
- for (; i < 32; i++) { // check which ports have irq flag set
- if ((hba_irq_state & (1 << i)) && ports[i]) {
- AHCID_DEBUG("Interrupt for port %d\n", i);
-
- ahci_port_is_t port_state = ahci_port_is_rd(&ports[i]->port);
-
- // clear interrupts for port asap. (this is what FreeBSD also does)
- ahci_port_is_wr(&ports[i]->port, port_state);
-
- // process IDENTIFY response: QEMU sends D2H Register FIS, Spec.
- // says device should send a PIO Setup FIS. We handle both for now :)
- if ((ahci_port_is_dhrs_extract(port_state) == 1 ||
- ahci_port_is_pss_extract(port_state)) &&
- ports[i]->status == AHCID_PORT_STATUS_IDENTIFY_PENDING) {
- // we got a d2h register or pio setup fis
- uint8_t *d2hr_fis = ports[i]->receive_fis->vaddr +
- 0x40 /* D2H Register FIS offset in rFIS */;
- uint8_t *pss_fis = ports[i]->receive_fis->vaddr +
- 0x20 /* D2H Register FIS offset in rFIS */;
- uint8_t *fis;
- if (ahci_port_is_dhrs_extract(port_state)) {
- // QEMU case: QEMU sends a D2H Register FIS as answer to
- // IDENTIFY Device (0xEC).
- // This is *NOT* according to AT Attachement 8, Sect. 7.16
- // IDENTIFY DEVICE which specifies PIO Data-in.
- // This does not change anything in handling the actual
- // IDENTIFY data, which is still stored in the indicated
- // DMA region.
- fis = d2hr_fis;
- if (fis[0] != SATA_FIS_TYPE_D2H) {
- AHCID_DEBUG("Interrupt signalled D2H Reg FIS but the"
- " FIS has another type (0x%02x)\n", fis[0]);
- continue; // handle next port
- }
- }
- else if (ahci_port_is_pss_extract(port_state)) {
- // ATA-8 compliant case. Real hardware seems to do this.
- // PIO Setup FIS should mean that we need to receive data
- // afterwards.
- // However the AHCI HBA handles that for us and copies the
- // received data into the DMA region specified in the
- // IDENTIFY command.
- fis = pss_fis;
- if (fis[0] != SATA_FIS_TYPE_PIO) {
- AHCID_DEBUG("Interrupt signalled PIO Setup FIS but the"
- " FIS has another type (0x%02x)\n", fis[0]);
- continue; // handle next port
- }
- }
-
- int identify_received = 1;
- if (ports[i]->prdts != NULL && ports[i]->prdt_count == 1) {
- AHCID_DEBUG("coping data from IDENTIFY result on %d\n", i);
- memcpy(ports[i]->identify_data, ports[i]->prdts[0]->vaddr, 512);
- ata_identify_t identify;
- ata_identify_initialize(&identify,
- (char *)ports[i]->identify_data);
-#ifdef AHCI_SERVICE_DEBUG
- char buf[32768];
- ata_identify_pr(buf, 32768, &identify);
- puts(buf);
-#endif
- }
- else {
- AHCID_DEBUG("PRDTL structures not set up for port %d while"
- " processing IDENTIFY response.\n", i);
- identify_received = 0;
- }
-
- // uninitialize port
- // clear interrupts
- ahci_port_is_wr(&ports[i]->port, -1);
- // disable all interrupts of this port
- AHCID_DEBUG("disabling interrupts for %d\n", i);
- ahci_port_ie_wr(&ports[i]->port, 0);
- // stop running commands
- AHCID_DEBUG("stopping %d\n", i);
- ahci_port_cmd_t cmd = ahci_port_cmd_rd(&ports[i]->port);
- cmd = ahci_port_cmd_st_insert(cmd, 0);
- ahci_port_cmd_wr(&ports[i]->port, cmd);
- // free CL, rFIS
- AHCID_DEBUG("clearing clb and fb for %d\n", i);
- ahci_port_clb_wr(&ports[i]->port, 0);
- ahci_port_fb_wr(&ports[i]->port, 0);
- AHCID_DEBUG("freeing memory for %d\n", i);
- ahci_dma_region_free(ports[i]->command_list);
- ahci_dma_region_free(ports[i]->receive_fis);
- ahci_dma_region_free(ports[i]->prdts[0]);
- free(ports[i]->prdts);
- // add port to skb
- // TODO: true unique port_ids, more info?
- if (identify_received) {
- skb_add_fact("ahci_device(%d).", i);
- ports[i]->status = AHCID_PORT_STATUS_IDLE;
- AHCID_DEBUG("Processing IDENTIFY from port %d complete\n", i);
- }
- else {
- ports[i]->status = AHCID_PORT_STATUS_UNINITIALIZED;
- }
- continue;
- }
-#if defined(AHCI_SERVICE_DEBUG) || defined(GLOBAL_DEBUG)
- else if (ports[i]->status == AHCID_PORT_STATUS_IDENTIFY_PENDING) {
- AHCID_DEBUG("received unknown interrupt while waiting for"
- " IDENTIFY D2H Reg FIS\n");
- char buf[2048];
- ahci_port_is_prtval(buf, 2048, port_state);
- AHCID_DEBUG("Port Status:\n");
- puts(buf);
- }
-#endif
- if (ports[i]->binding != NULL) {
- interrupted_ports[i] = true;
- interrupt_state[i] = port_state;
- } else {
- AHCID_DEBUG("no client registered for port %d\n", i);
- }
- }
- }
-
- // clear interrupts for host controller
- ahci_hba_is_wr(&controller, (uint32_t)-1);
-
- // deliver messages to clients
- for (i = 0; i < 32; i++) {
- if (!interrupted_ports[i]) continue;
-
- AHCID_DEBUG("Port %d Interrupt State: 0x%"PRIx32"\n",
- i, interrupt_state[i]);
- AHCID_DEBUG("Port %d Command Issue state: 0x%"PRIx32"\n",
- i, ahci_port_ci_rd(&ports[i]->port));
-
- ahci_mgmt_command_completed__tx(ports[i]->binding, NOP_CONT,
- i, interrupt_state[i]);
- }
-}
-
-static void ahci_reregister_handler(void *arg)
-{
- errval_t err;
- struct device_id *dev_id = arg;
- err = pci_reregister_irq_for_device(PCI_CLASS_MASS_STORAGE, PCI_SUB_SATA,
- PCI_DONT_CARE, dev_id->vendor, dev_id->device, PCI_DONT_CARE, PCI_DONT_CARE,
- PCI_DONT_CARE, ahci_interrupt_handler, NULL,
- ahci_reregister_handler, dev_id);
- if (err_is_fail(err)) {
- DEBUG_ERR(err, "pci_reregister_irq_for_device");
- }
-
- return;
-}
-
-static void polling_loop(void)
-{
- errval_t err;
- struct waitset *ws = get_default_waitset();
- while (1) {
-#if defined(AHCI_SERVICE_DEBUG) || defined(GLOBAL_DEBUG)
- if (controller.b != NULL) {
- ahci_interrupt_handler(0);
- }
-#endif
- err = event_dispatch(ws);
- if (err_is_fail(err)) {
- DEBUG_ERR(err, "in event_dispatch");
- break;
- }
- }
-}
-
-int main(int argc, char **argv)
-{
- int r;
-
- AHCID_DEBUG("starting\n");
-
- //connect to the SKB
- AHCID_DEBUG("connecting to the SKB...\n");
- skb_client_connect();
- AHCID_DEBUG("connected.\n");
-
- r = pci_client_connect();
- assert(err_is_ok(r));
- AHCID_DEBUG("connected to pci\n");
-
- if (argc >= 3) {
- AHCID_DEBUG("got %s as vendor_id:device_id\n", argv[2]);
- uint64_t vendor_id, device_id;
- vendor_id = strtol(argv[2], NULL, 16);
- device_id = strtol(argv[2]+5, NULL, 16);
- struct device_id *dev_id = malloc(sizeof(*dev_id));
- dev_id->vendor = vendor_id;
- dev_id->device = device_id;
- r = pci_register_driver_movable_irq(ahci_init, PCI_CLASS_MASS_STORAGE,
- PCI_SUB_SATA, PCI_DONT_CARE, vendor_id,
- device_id,
- PCI_DONT_CARE, PCI_DONT_CARE, PCI_DONT_CARE,
- ahci_interrupt_handler, NULL,
- ahci_reregister_handler, dev_id);
- if (err_is_fail(r)) {
- printf("couldn't register device %04"PRIx64":%04"PRIx64": %s\n", vendor_id,
- device_id, err_getstring(r));
- return 1;
- }
- printf("ahcid: registered device %04"PRIx64":%04"PRIx64"\n", vendor_id, device_id);
- } else {
- // fall-back: try some known AHCI devices
- r = pci_register_driver_irq(ahci_init, PCI_CLASS_MASS_STORAGE,
- PCI_SUB_SATA, PCI_DONT_CARE, PCI_VENDOR_INTEL,
- PCI_DEVICE_ICH9R_82801IR /* QEMU ICH9R AHCI Controller */,
- PCI_DONT_CARE, PCI_DONT_CARE, PCI_DONT_CARE,
- ahci_interrupt_handler, NULL);
- if (err_is_ok(r)) {
- printf("ahcid: found QEMU ICH9R controller\n");
- goto finish;
- }
- printf("ahcid: did not find QEMU ICH9R controller\n");
-
- r = pci_register_driver_irq(ahci_init, PCI_CLASS_MASS_STORAGE,
- PCI_SUB_SATA, PCI_DONT_CARE,
- PCI_VENDOR_INTEL,
- PCI_DEVICE_ICH10_82801JI /* 82801JI (ICH10 Family) AHCI Controller */,
- PCI_DONT_CARE, PCI_DONT_CARE, PCI_DONT_CARE,
- ahci_interrupt_handler, NULL);
- if (err_is_ok(r)) {
- printf("ahcid: found Sun Microsystems ICH10 Family controller\n");
- goto finish;
- }
- printf("ahcid: did not find Sun Microsystems ICH10 Family controller\n");
-
- r = pci_register_driver_irq(ahci_init, PCI_CLASS_MASS_STORAGE,
- PCI_SUB_SATA, PCI_DONT_CARE,
- PCI_VENDOR_ATI,
- PCI_DEVICE_SB7x0 /* ATI SB7x0/SB8x0/SB9x0 SATA controller (IDE mode) */,
- PCI_DONT_CARE, PCI_DONT_CARE, PCI_DONT_CARE,
- ahci_interrupt_handler, NULL);
- if (err_is_ok(r)) {
- printf("ahcid: found ATI Technologies Inc. SB7x0/8x0/9x0 controller\n");
- goto finish;
- }
- printf("ahcid: did not find ATI Technologies Inc. SB7x0/8x0/9x0 controller\n");
- printf("ahcid: \ndid not find any supported AHCI controller\naborting...");
- return 1;
- }
-
-finish:
- AHCID_DEBUG("registered driver: retval=%d\n", r);
-
- polling_loop();
-}
+++ /dev/null
-/*
- * Copyright (c) 2011 ETH Zurich.
- * All rights reserved.
- *
- * This file is distributed under the terms in the attached LICENSE file.
- * If you do not find this file, copies can be found by writing to:
- * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
- */
-
-#ifndef AHCID_H_
-#define AHCID_H_
-#include <barrelfish/barrelfish.h>
-#include <dev/ahci_port_dev.h>
-#include <ahci/ahci_dma_pool.h>
-
-enum ahcid_port_status {
- AHCID_PORT_STATUS_UNINITIALIZED = 0,
- AHCID_PORT_STATUS_IDLE = 1,
- AHCID_PORT_STATUS_IDENTIFY_PENDING,
-};
-
-struct ahcid_port_info {
- ahci_port_t port;
- struct ahci_dma_region *command_list;
- struct ahci_dma_region *receive_fis;
- enum ahcid_port_status status;
- // XXX: use data in ahci_port_t?
- struct ahci_dma_region **prdts;
- size_t prdt_count;
- uint16_t identify_data[256];
- struct ahci_mgmt_binding *binding;
-};
-
-uint32_t ahcid_port_offset(uint32_t port);
-errval_t ahcid_ports_init(struct ahcid_port_info **ports, size_t num_ports,
- uint32_t active_ports_bf, void* base_address);
-
-#endif //AHCID_H_
+++ /dev/null
-/*
- * Copyright (c) 2011 ETH Zurich.
- * All rights reserved.
- *
- * This file is distributed under the terms in the attached LICENSE file.
- * If you do not find this file, copies can be found by writing to:
- * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
- */
-
-#ifndef AHCID_DEBUG_H_
-#define AHCID_DEBUG_H_
-
-
-/*****************************************************************
- * Debug printer:
- *****************************************************************/
-
-#if defined(AHCI_SERVICE_DEBUG) || defined(GLOBAL_DEBUG)
-#define AHCID_DEBUG(x...) printf("ahcid: " x)
-#else
-#define AHCID_DEBUG(x...) ((void)0)
-#endif
-
-#endif // AHCI_DEBUG_H_
+++ /dev/null
-/*
- * Copyright (c) 2011 ETH Zurich.
- * All rights reserved.
- *
- * This file is distributed under the terms in the attached LICENSE file.
- * If you do not find this file, copies can be found by writing to:
- * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
- */
-
-#include "ahcid.h"
-#include <ahci/ahci_dma_pool.h>
-#include <ahci/ahci_util.h>
-
-uint32_t ahcid_port_offset(uint32_t port)
-{
- return 0x100 + port * 0x80;
-}
-
-errval_t ahcid_ports_init(struct ahcid_port_info **ports, size_t num_ports,
- uint32_t active_ports_bf, void* base_address)
-{
- errval_t r;
- int i;
- for (i = 0; i < num_ports; i++) {
- if ((active_ports_bf >> i)&0x1) {
- // only initialize implemented ports
- ports[i] = calloc(1, sizeof(struct ahcid_port_info));
- if (ports[i] == NULL) {
- return LIB_ERR_MALLOC_FAIL;
- }
-
- ahci_port_initialize(&ports[i]->port, base_address + ahcid_port_offset(i));
-
- // setup dma regions for command list and receive FIS area
- r = ahci_port_alloc_dma_structs(&ports[i]->port,
- &ports[i]->command_list, &ports[i]->receive_fis);
- assert(err_is_ok(r));
-
- // enable rFIS area
- ahci_port_cmd_t cmd = ahci_port_cmd_rd(&ports[i]->port);
- cmd = ahci_port_cmd_fre_insert(cmd, 1);
- ahci_port_cmd_wr(&ports[i]->port, cmd);
- } else {
- ports[i] = NULL;
- }
- }
- return 0;
-}
-
--- /dev/null
+--------------------------------------------------------------------------
+-- Copyright (c) 2007-2011, ETH Zurich.
+-- All rights reserved.
+--
+-- This file is distributed under the terms in the attached LICENSE file.
+-- If you do not find this file, copies can be found by writing to:
+-- ETH Zurich D-INFK, Universitaetsstr. 6, CH-8092 Zurich. Attn: Systems Group.
+--
+-- Hakefile for /usr/drivers/ahci
+--
+--------------------------------------------------------------------------
+
+[
+ build application {
+ target = "ahcid",
+ mackerelDevices = [ "ata_identify", "ahci_port", "ahci_hba" ],
+ cFiles = [ "ahcid.c", "test.c" ],
+ addCFlags = ["-Wno-unused-variable", "-Wno-unused-function"],
+ addLibraries = [ "blk", "pci", "skb", "bench" ]
+ },
+
+ build application {
+ target = "ahci_test",
+ mackerelDevices = [ "ata_identify", "ahci_port", "ahci_hba" ],
+ cFiles = [ "ahcid.c", "test.c" ],
+ addCFlags = ["-DTESTING"],
+ addLibraries = [ "blk", "pci", "skb", "bench" ]
+ }
+]
--- /dev/null
+/*
+ * Copyright (c) 2016 ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#include "ahcid.h"
+#include "test.h"
+
+void* dq = NULL;
+struct waitset disk_ws;
+
+static struct ahci_disk* ad;
+static volatile bool driver_initialized = false;
+static struct device_mem hbabar;
+static struct waitset_chanstate *chan = NULL;
+
+
+struct device_id {
+ uint16_t vendor;
+ uint16_t device;
+};
+
+static void ahci_interrupt_handler(void *arg)
+{
+ void devq_interrupt_handler(void*);
+ devq_interrupt_handler(dq);
+
+#ifdef DISABLE_INTERRUPTS
+ assert(chan != NULL);
+ assert(dq != NULL);
+ errval_t err = waitset_chan_register(&disk_ws, chan, MKCLOSURE(ahci_interrupt_handler, dq));
+ if (err_is_fail(err) && err_no(err) == LIB_ERR_CHAN_ALREADY_REGISTERED) {
+ printf("Got actual interrupt?\n");
+ }
+ else if (err_is_fail(err)) {
+ USER_PANIC_ERR(err, "Can't register our dummy channel.");
+ }
+ err = waitset_chan_trigger(chan);
+ if (err_is_fail(err)) {
+ USER_PANIC_ERR(err, "trigger failed.");
+ }
+#endif
+}
+
+static void do_ahci_init(struct device_mem* bar_info, int nr_allocated_bars)
+{
+ // Although the AHCI specification requires the AHCI memory region to be in
+ // BAR 5 (BAR 0 to 4 are used for legacy IDE mode) the QEMU AHCI emulation
+ // incorrectly uses BAR 0. Because of this, ahcid consults both BAR 0 and
+ // BAR 5 to find the HBA's memory mapped I/O region.
+ // Since two BARs between 0-5 are I/O ports they are not passed to use by PCI.
+
+ if (nr_allocated_bars == 1) {
+ memcpy(&hbabar, &bar_info[0], sizeof(struct device_mem));
+ } else if (nr_allocated_bars == 3) {
+ memcpy(&hbabar, &bar_info[2], sizeof(struct device_mem));
+ } else {
+ printf("Strange device... not supported\n");
+ abort();
+ }
+
+ errval_t err = blk_ahci_init(&hbabar, &ad);
+ if (err_is_fail(err)) {
+ USER_PANIC_ERR(err, "AHCI HBA init failed.");
+ }
+
+ err = devq_create(ad, "", 0, &dq);
+ if (err_is_fail(err)) {
+ USER_PANIC_ERR(err, "devq create failed.");
+ }
+
+#if DISABLE_INTERRUPTS
+ waitset_init(&disk_ws);
+
+ // Hack: Why don't interrupts work?
+ chan = malloc(sizeof(struct waitset_chanstate));
+ waitset_chanstate_init(chan, CHANTYPE_AHCI);
+
+ err = waitset_chan_register(&disk_ws, chan, MKCLOSURE(ahci_interrupt_handler, dq));
+ if (err_is_fail(err)) {
+ USER_PANIC_ERR(err, "waitset_chan_regster failed.");
+ }
+ err = waitset_chan_trigger(chan);
+ if (err_is_fail(err)) {
+ USER_PANIC_ERR(err, "trigger failed.");
+ }
+#endif
+
+ driver_initialized = true;
+}
+
+static void ahci_reregister_handler(void *arg)
+{
+ errval_t err;
+ struct device_id *dev_id = arg;
+ err = pci_reregister_irq_for_device(PCI_CLASS_MASS_STORAGE, PCI_SUB_SATA,
+ PCI_DONT_CARE, dev_id->vendor, dev_id->device, PCI_DONT_CARE, PCI_DONT_CARE,
+ PCI_DONT_CARE, ahci_interrupt_handler, NULL,
+ ahci_reregister_handler, dev_id);
+ if (err_is_fail(err)) {
+ DEBUG_ERR(err, "pci_reregister_irq_for_device");
+ }
+
+ return;
+}
+
+int main(int argc, char **argv)
+{
+ int r;
+ r = skb_client_connect();
+ assert(err_is_ok(r));
+ r = pci_client_connect();
+ assert(err_is_ok(r));
+
+ if (argc >= 3) {
+ printf("Got %s as vendor_id:device_id\n", argv[2]);
+ uint64_t vendor_id, device_id;
+ vendor_id = strtol(argv[2], NULL, 16);
+ device_id = strtol(argv[2]+5, NULL, 16);
+
+ struct device_id *dev_id = malloc(sizeof(*dev_id));
+ dev_id->vendor = vendor_id;
+ dev_id->device = device_id;
+
+ r = pci_register_driver_movable_irq(do_ahci_init, PCI_CLASS_MASS_STORAGE,
+ PCI_SUB_SATA, PCI_DONT_CARE, vendor_id, device_id,
+ PCI_DONT_CARE, PCI_DONT_CARE, PCI_DONT_CARE,
+ ahci_interrupt_handler, NULL,
+ ahci_reregister_handler, dev_id);
+ if (err_is_fail(r)) {
+ printf("Couldn't register device %04"PRIx64":%04"PRIx64": %s\n", vendor_id,
+ device_id, err_getstring(r));
+ return 1;
+ }
+ printf("AHCID: registered device %04"PRIx64":%04"PRIx64"\n", vendor_id, device_id);
+ }
+ else {
+ printf("usage: ahcid <vendor id>:<device id>\n");
+ exit(1);
+ }
+ assert(driver_initialized);
+
+#ifdef TESTING
+ printf("Initialized driver, running tests:\n");
+ if (argc < 4) {
+ return 1;
+ }
+ if (strcmp(argv[3], "read|write") == 0) {
+ test_runner(2, AhciTest_READ, AhciTest_WRITE);
+ }
+ if (strcmp(argv[3], "read") == 0) {
+ test_runner(1, AhciTest_READ);
+ }
+ if (strcmp(argv[3], "write") == 0) {
+ test_runner(1, AhciTest_WRITE);
+ }
+ if (strcmp(argv[3], "verify") == 0) {
+ test_runner(1, AhciTest_VERIFY);
+ }
+#endif
+}
--- /dev/null
+/*
+ * Copyright (c) 2016, ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#ifndef _AHCID_
+#define _AHCID_
+
+#include <stdio.h>
+#include <string.h>
+#include <barrelfish/barrelfish.h>
+#include <barrelfish/waitset.h>
+#include <barrelfish/waitset_chan.h>
+#include <barrelfish/syscalls.h>
+#include <barrelfish/nameservice_client.h>
+#include <pci/pci.h>
+#include <skb/skb.h>
+#include <blk/ahci.h>
+#include <devif/queue.h>
+
+#define DISABLE_INTERRUPTS 1
+
+extern void* dq;
+extern struct waitset disk_ws;
+
+#endif // _AHCID_
--- /dev/null
+#include "ahcid.h"
+#include "test.h"
+
+#include <stdarg.h>
+#include <bench/bench.h>
+
+struct dma_mem {
+ lvaddr_t vaddr; ///< virtual address of the mapped region
+ lpaddr_t paddr; ///< physical address of the underlying frame
+ uint64_t bytes; ///< size of the region in bytes
+ uint64_t requested; ///< requested size of the region in bytes (<= bytes)
+ struct capref frame; ///< frame capability backing this region
+};
+
+void test_runner(int n, ...)
+{
+ va_list arguments;
+ va_start(arguments, n);
+
+ for (size_t i=0; i<n; i++) {
+ enum AhciTest test = va_arg(arguments, enum AhciTest);
+ switch (test) {
+ case AhciTest_READ:
+ ahci_perf_sequential(1024*1024*1024, 512, false);
+ ahci_perf_sequential(1024*1024*1024, 1024, false);
+ ahci_perf_sequential(1024*1024*1024, 2048, false);
+ ahci_perf_sequential(1024*1024*1024, 4096, false);
+ ahci_perf_sequential(1024*1024*1024, 8192, false);
+ ahci_perf_sequential(1024*1024*1024, 16384, false);
+ ahci_perf_sequential(1024*1024*1024, 32768, false);
+ ahci_perf_sequential(1024*1024*1024, 65536, false);
+ ahci_perf_sequential(1024*1024*1024, 131072, false);
+ ahci_perf_sequential(1024*1024*1024, 262144, false);
+ ahci_perf_sequential(1024*1024*1024, 524288, false);
+ ahci_perf_sequential(1024*1024*1024, 1048576, false);
+ ahci_perf_sequential(1024*1024*1024, 1048576*2, false);
+ //ahci_perf_sequential(1024*1024*1024, 1048576*4, false); // ERROR: tilsiter1 OOM
+ break;
+
+ case AhciTest_WRITE:
+ ahci_perf_sequential(1024*1024*256, 512, true);
+ ahci_perf_sequential(1024*1024*256, 1024, true);
+ ahci_perf_sequential(1024*1024*256, 2048, true);
+ ahci_perf_sequential(1024*1024*256, 4096, true);
+ ahci_perf_sequential(1024*1024*256, 8192, true);
+ ahci_perf_sequential(1024*1024*256, 16384, true);
+ ahci_perf_sequential(1024*1024*256, 32768, true);
+ ahci_perf_sequential(1024*1024*256, 65536, true);
+ ahci_perf_sequential(1024*1024*256, 131072, true);
+ ahci_perf_sequential(1024*1024*256, 262144, true);
+ ahci_perf_sequential(1024*1024*256, 524288, true);
+ ahci_perf_sequential(1024*1024*256, 1048576, true);
+ ahci_perf_sequential(1024*1024*256, 1048576*2, true);
+ //ahci_perf_sequential(1024*1024*256, 1048576*4, true); // ERROR: tilsiter1 OOM
+ break;
+
+ case AhciTest_VERIFY:
+ ahci_verify_sequential(1024*1024*256, 512);
+ ahci_verify_sequential(1024*1024*256, 1024);
+ ahci_verify_sequential(1024*1024*256, 2048);
+ ahci_verify_sequential(1024*1024*256, 4096);
+ ahci_verify_sequential(1024*1024*256, 8192);
+ ahci_verify_sequential(1024*1024*256, 16384);
+ ahci_verify_sequential(1024*1024*256, 32768);
+ ahci_verify_sequential(1024*1024*256, 65536);
+ ahci_verify_sequential(1024*1024*256, 131072);
+ ahci_verify_sequential(1024*1024*256, 262144);
+ ahci_verify_sequential(1024*1024*256, 524288);
+ ahci_verify_sequential(1024*1024*256, 1048576);
+ ahci_verify_sequential(1024*1024*256, 1048576*2);
+ ahci_verify_sequential(1024*1024*256, 1048576*4);
+ break;
+
+ case AhciTest_BASIC:
+ ahci_simple_test();
+ break;
+
+ default:
+ USER_PANIC("Unknown test?");
+ break;
+ }
+ }
+
+ // Harness line
+ printf("AHCI testing completed.\n");
+}
+
+static void frame_alloc_identify(size_t size, struct capref *frame, struct frame_identity *id)
+{
+ errval_t err;
+ size_t retbytes;
+
+ err = frame_alloc(frame, size, &retbytes);
+ if (err_is_fail(err)) {
+ USER_PANIC_ERR(err, "frame_alloc");
+ }
+
+ err = invoke_frame_identify(*frame, id);
+ if (err_is_fail(err)) {
+ USER_PANIC_ERR(err, "invoke_frame_identify");
+ }
+}
+
+static void wait_for_interrupt(void)
+{
+ errval_t err = event_dispatch(&disk_ws);
+ if (err_is_fail(err)) {
+ USER_PANIC_ERR(err, "error in event_dispatch for wait_for_interrupt");
+ }
+}
+
+void ahci_simple_test(void)
+{
+ errval_t err;
+ regionid_t region_id = 0;
+ lpaddr_t base = 0;
+ size_t length = 0;
+ bufferid_t buffer_id = 0;
+
+ // Allocate a buffer:
+ struct dma_mem mem;
+ err = frame_alloc(&mem.frame, 4096, &mem.bytes);
+ if (err_is_fail(err)) {
+ USER_PANIC_ERR(err, "frame_alloc");
+ }
+ struct frame_identity id;
+ err = invoke_frame_identify(mem.frame, &id);
+ if (err_is_fail(err)) {
+ USER_PANIC_ERR(err, "invoke_frame_identify");
+ }
+
+ err = devq_register(dq, mem.frame, ®ion_id);
+ if (err_is_fail(err)) {
+ USER_PANIC_ERR(err, "devq register");
+ }
+
+ uint64_t flags = 0x0;
+ devq_enqueue(dq, region_id, id.base, 512, 0x123, flags);
+ if (err_is_fail(err)) {
+ USER_PANIC_ERR(err, "devq enqueue");
+ }
+
+ do {
+ err = devq_dequeue(dq, ®ion_id, &base, &length, &buffer_id);
+ if (err_is_ok(err)) {
+ break;
+ }
+ if (err_is_fail(err) && err_no(err) != DEV_ERR_QUEUE_EMPTY) {
+ USER_PANIC_ERR(err, "devq dequeue");
+ }
+ wait_for_interrupt();
+ } while (err_no(err) == DEV_ERR_QUEUE_EMPTY);
+
+ assert (buffer_id == 0x123);
+ assert (base == id.base);
+ assert (length == 512);
+
+ err = devq_remove(dq, region_id);
+ if (err_is_fail(err)) {
+ USER_PANIC_ERR(err, "devq_remove failed.");
+ }
+
+ printf("[%s]: DONE\n", __FUNCTION__);
+}
+
+static void blocking_dequeue(void* q, regionid_t* region_id, lpaddr_t* base, size_t* length, bufferid_t* buffer_id)
+{
+ errval_t err;
+ do {
+ err = devq_dequeue(q, region_id, base, length, buffer_id);
+ if (err_is_ok(err)) {
+ break;
+ }
+ if (err_is_fail(err) && err_no(err) != DEV_ERR_QUEUE_EMPTY) {
+ USER_PANIC_ERR(err, "devq dequeue");
+ }
+
+ assert(err_no(err) == DEV_ERR_QUEUE_EMPTY);
+ wait_for_interrupt();
+ } while (err_no(err) == DEV_ERR_QUEUE_EMPTY);
+}
+
+static void receive_block(void)
+{
+ regionid_t rid = 0;
+ lpaddr_t base = 0;
+ size_t len = 0;
+ bufferid_t bid = 0;
+ blocking_dequeue(dq, &rid, &base, &len, &bid);
+
+ bool* status = (bool*) bid;
+ assert (*status == false); // Only write region once
+ *status = true;
+}
+
+void ahci_perf_sequential(size_t buffer_size, size_t block_size, bool write)
+{
+ bench_init();
+ errval_t err;
+ assert(buffer_size % block_size == 0);
+
+ size_t read_requests = buffer_size / block_size;
+ regionid_t region_id = 0;
+
+ static struct capref frame;
+ struct frame_identity id;
+ frame_alloc_identify(buffer_size, &frame, &id);
+
+ err = devq_register(dq, frame, ®ion_id);
+ if (err_is_fail(err)) {
+ USER_PANIC_ERR(err, "devq register");
+ }
+
+ uint64_t write_flag = (write) ? (1ULL << 63) : 0;
+ volatile bool *received = calloc(1, sizeof(bool) * read_requests);
+ cycles_t t1 = bench_tsc();
+ for (size_t i=0; i < read_requests; i++) {
+ uint64_t disk_block = write_flag | i;
+ do {
+ err = devq_enqueue(dq, region_id, id.base + (i*block_size), block_size, (bufferid_t)&received[i], disk_block);
+ if (err_is_ok(err)) {
+ break;
+ }
+ else if (err_no(err) == DEV_ERR_QUEUE_FULL) {
+ receive_block();
+ }
+ else {
+ USER_PANIC_ERR(err, "Can't receive block.");
+ }
+ } while (true);
+ }
+ // Make sure we have all requests:
+ for (size_t i=0; i<read_requests; i++) {
+ while (!received[i]) {
+ receive_block();
+ }
+ }
+ cycles_t t2 = bench_tsc();
+ cycles_t result = (t2 - t1 - bench_tscoverhead());
+
+ double result_ms = (double)bench_tsc_to_ms(result);
+ double bw = buffer_size / result_ms / 1000; // 1e3 to sec, 10e6 to MB
+ char* cmd = write ? "Write" : "Read";
+ printf("[%s] %s sequential size %zu bs %zu: %.2f [MB/s]\n", __FUNCTION__, cmd, buffer_size, block_size, bw);
+
+ err = devq_remove(dq, region_id);
+ if (err_is_fail(err)) {
+ USER_PANIC_ERR(err, "devq_remove failed.");
+ }
+
+ cap_destroy(frame);
+}
+
+void ahci_verify_sequential(size_t buffer_size, size_t block_size)
+{
+ bench_init();
+ errval_t err;
+ assert(buffer_size % block_size == 0);
+
+ size_t requests = buffer_size / block_size;
+ regionid_t region_id = 0;
+
+ struct capref frame;
+ struct frame_identity id;
+ frame_alloc_identify(buffer_size, &frame, &id);
+ err = devq_register(dq, frame, ®ion_id);
+ if (err_is_fail(err)) {
+ USER_PANIC_ERR(err, "devq register");
+ }
+
+ struct capref fcopy;
+ err = slot_alloc(&fcopy);
+ assert(err_is_ok(err));
+ err = cap_copy(fcopy, frame);
+ if (err_is_fail(err)) {
+ USER_PANIC_ERR(err, "copy failed.");
+ }
+ void* retaddr;
+ err = vspace_map_one_frame(&retaddr, id.bytes, fcopy, NULL, NULL);
+ if (err_is_fail(err)) {
+ USER_PANIC_ERR(err, "map copy failed.");
+ }
+
+ uint8_t rbyte = (uint8_t) rdtsc();
+ if (rbyte == 0) rbyte++;
+ memset(retaddr, rbyte, buffer_size);
+
+ uint64_t write_flag = (1ULL << 63);
+ bool *received = calloc(1, sizeof(bool) * requests);
+ for (size_t i=0; i < requests; i++) {
+ uint64_t disk_block = write_flag | i;
+ do {
+ err = devq_enqueue(dq, region_id, id.base + (i*block_size), block_size, (bufferid_t)&received[i], disk_block);
+ if (err_is_ok(err)) {
+ break;
+ }
+ else if (err_no(err) == DEV_ERR_QUEUE_FULL) {
+ receive_block();
+ }
+ else {
+ USER_PANIC_ERR(err, "Can't receive block.");
+ }
+ } while (true);
+ }
+ // Make sure we have all requests:
+ for (size_t i=0; i<requests; i++) {
+ //printf("%s:%s:%d: i: %zu requests: %zu\n", __FILE__, __FUNCTION__, __LINE__, i, requests);
+ while (!received[i]) {
+ receive_block();
+ }
+ }
+
+ memset(retaddr, 0x00, id.bytes);
+ memset((void*)received, 0x0, sizeof(bool)*requests);
+
+ for (size_t i=0; i < requests; i++) {
+ //printf("%s:%s:%d: i: %zu requests: %zu\n", __FILE__, __FUNCTION__, __LINE__, i, requests);
+ uint64_t disk_block = i;
+ do {
+ err = devq_enqueue(dq, region_id, id.base + (i*block_size), block_size, (bufferid_t)&received[i], disk_block);
+ if (err_is_ok(err)) {
+ break;
+ }
+ else if (err_no(err) == DEV_ERR_QUEUE_FULL) {
+ receive_block();
+ }
+ else {
+ USER_PANIC_ERR(err, "Can't receive block.");
+ }
+ } while (true);
+ }
+ // Make sure we have all requests:
+ for (size_t i=0; i<requests; i++) {
+ while (!received[i]) {
+ //printf("%s:%s:%d: i: %zu requests: %zu\n", __FILE__, __FUNCTION__, __LINE__, i, requests);
+ receive_block();
+ }
+ }
+
+ for (size_t i=0; i < buffer_size; i++) {
+ uint8_t* carr = retaddr;
+ if (carr[i] != rbyte) {
+ printf("%s:%s:%d: carr[%zu]=%d != rbyte=%d\n", __FILE__, __FUNCTION__, __LINE__, i, carr[i], rbyte);
+ }
+ assert(carr[i] == rbyte);
+ }
+
+ printf("[%s] SUCCESS (%zu %zu)\n", __FUNCTION__, buffer_size, block_size);
+ cap_destroy(fcopy);
+
+ err = devq_remove(dq, region_id);
+ if (err_is_fail(err)) {
+ USER_PANIC_ERR(err, "devq_remove failed.");
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2016, ETH Zurich.
+ * All rights reserved.
+ *
+ * This file is distributed under the terms in the attached LICENSE file.
+ * If you do not find this file, copies can be found by writing to:
+ * ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
+ */
+
+#ifndef AHCI_TEST
+#define AHCI_TEST
+
+#include <stdlib.h>
+#include <stdbool.h>
+
+enum AhciTest {
+ AhciTest_READ,
+ AhciTest_WRITE,
+ AhciTest_BASIC,
+ AhciTest_VERIFY,
+};
+
+void test_runner(int n, ...);
+
+void ahci_simple_test(void);
+void ahci_perf_sequential(size_t buffer_size, size_t block_size, bool write);
+void ahci_verify_sequential(size_t buffer_size, size_t block_size);
+
+
+#endif // AHCI_TEST
struct device_caps *c = &dev_caps[bus][dev][fun][idx];
errval_t err;
+ size = ROUND_UP(size, BASE_PAGE_SIZE); // Some BARs are less than 4 KiB
// first try with maximally-sized caps (we'll reduce this if it doesn't work)
uint8_t bits = log2ceil(size);
// TODO: This should be part of the routing step
int irq = pci_setup_interrupt(st->bus, st->dev, st->fun);
PCI_DEBUG("pci: init_device_handler_irq: init interrupt.\n");
- PCI_DEBUG("pci: irq = %u, core = %hhu, vector = %u\n", irq, coreid,
- vector);
pci_enable_interrupt_for_device(st->bus, st->dev, st->fun, st->pcie);
+ PCI_DEBUG("pci: Interrupt enabled.\n");
err = sys_debug_create_irq_src_cap(cap, irq);
- b->tx_vtbl.get_irq_cap_response(b, NOP_CONT, err, cap);
+ if (err_is_fail(err)) {
+ USER_PANIC_ERR(err, "create irq src cap failed.");
+ }
+
+ err = b->tx_vtbl.get_irq_cap_response(b, NOP_CONT, err, cap);
+ if (err_is_fail(err)) {
+ USER_PANIC_ERR(err, "cap response failed.");
+ }
}
static void get_bar_cap_handler(struct pci_binding *b, uint32_t idx,