1This lab project contains a new testcase \verb+ata_rw28_test+ to test the 2Flounder-generated interface for \acs{ata} in LBA28 addressing mode. This 3chapter walks through its code to demonstrate the steps needed to access disks 4using the Flounder backend. 5 6The application first initializes the necessary bindings and \acs{rpc} client. 7It then uses the \acs{rpc} wrapper around the Flounder-based ATA interface 8geared towards LBA28 addressing mode. The test itself is performed by writing 9\lstinline+0xdeadbeef+ in multiple 512 byte blocks and verifying that the data 10is actually written to disk by reading it back and checking the contents. The 11test concludes with releasing the port. 12 13\section{Datastructures} 14 15To be able to perform \acs{rpc} calls to read from or write to the disk, an 16\lstinline+ahci_binding+ as well as an \lstinline+ahci_ata_rw28_binding+ and an 17\lstinline+ata_rw28_rpc_client+ are necessary. \lstinline+ata_rw28_test+ 18defines these as global variables out of convenience: 19 20\begin{lstlisting} 21struct ahci_ata_rw28_binding ahci_ata_rw28_binding; 22struct ata_rw28_rpc_client ata_rw28_rpc; 23struct ata_rw28_binding *ata_rw28_binding = NULL; 24struct ahci_binding *ahci_binding = NULL; 25\end{lstlisting} 26 27The required header files are: 28 29\begin{lstlisting} 30#include <barrelfish/barrelfish.h> 31#include <barrelfish/waitset.h> 32#include <if/ata_rw28_defs.h> 33#include <if/ata_rw28_ahci_defs.h> 34#include <if/ata_rw28_rpcclient_defs.h> 35\end{lstlisting} 36 37\section{Initialization} 38 39First, we need to initialize the \acs{dma} pool which is used to manage frames 40that are mapped uncached and are therefore suitable for \acs{dma} transfers. We 41initialize the pool to be 1MB in size: 42 43\begin{lstlisting} 44ahci_dma_pool_init(1024*1024); 45\end{lstlisting} 46 47Next, we need to initialize \verb+libahci+ and specify which \ac{ahci} port we 48want to use. For simplicity, we use port $0$ which is the first device 49detected. To achieve blocking behaviour, we enter a spinloop and wait for the 50callback from \verb+ahcid+: 51 52\begin{lstlisting} 53err = ahci_init(0, ahci_bind_cb, NULL, get_default_waitset()); 54if (err_is_fail(err) || 55 err_is_fail(err=wait_bind((void**)&ahci_binding))) { 56 USER_PANIC_ERR(err, "ahci_init"); 57} 58\end{lstlisting} 59 60The callback \lstinline+ahci_bind_cb+ simply sets the global 61\lstinline+ahci_binding+ and \lstinline+wait_bind+ waits for this global to be 62set: 63 64\begin{lstlisting} 65static void ahci_bind_cb(void *st, 66 errval_t err, struct ahci_binding *_binding) 67{ 68 bind_err = err; 69 if (err_is_ok(err)) { 70 ahci_binding = _binding; 71 } 72} 73 74static errval_t wait_bind(void **bind_p) 75{ 76 while (!*bind_p && err_is_ok(bind_err)) { 77 messages_wait_and_handle_next(); 78 } 79 return bind_err; 80} 81\end{lstlisting} 82 83The \acs{rpc} client can be constructed by first initializing the 84\lstinline+ata_rw28+ binding and then building an \acs{rpc} client on top of 85it. The pointer to the binding is stored for convenience as it is used 86frequently: 87 88\begin{lstlisting} 89err = ahci_ata_rw28_init(&ahci_ata_rw28_binding, get_default_waitset(), 90 ahci_binding); 91if (err_is_fail(err)) { 92 USER_PANIC_ERR(err, "ahci_ata_rw28_init"); 93} 94 95ata_rw28_binding = (struct ata_rw28_binding*)&ahci_ata_rw28_binding; 96 97err = ata_rw28_rpc_client_init(&ata_rw28_rpc, ata_rw28_binding); 98if (err_is_fail(err)) { 99 USER_PANIC_ERR(err, "ata_rw28_rpc_client_init"); 100} 101\end{lstlisting} 102 103\acs{rpc} calls can now be made to perform operations on the disk. 104 105\section{Data Manipulation} 106 107\lstinline+write_and_check_32+ is the function used to write 108\lstinline+0xdeadbeef+ to the disk and verify that writing succeeded. It 109accepts arbitrary 32 bit patterns that are written to disk. First off, we need 110to calculate some values, allocate a buffer and fill this buffer with the 111pattern: 112 113\begin{lstlisting} 114static void write_and_check_32(uint32_t pat, size_t start_lba, 115 size_t block_size, size_t block_count) 116{ 117 errval_t err; 118 size_t bytes = block_size*block_count; 119 uint8_t *buf = malloc(bytes); 120 assert(buf); 121 size_t step = sizeof(pat); 122 size_t count = bytes / step; 123 assert(bytes % sizeof(pat) == 0); 124 for (size_t i = 0; i < count; ++i) 125 *(uint32_t*)(buf+i*step) = pat; 126\end{lstlisting} 127 128The actual writing is very simple. We issue the \lstinline+write_dma+ \acs{rpc} 129call, pass it the binding, the buffer, the number of bytes to write, the 130\ac{lba} on the disk where we want to write to and do some basic error 131handling: 132 133\begin{lstlisting} 134 printf("writing data\n"); 135 errval_t status; 136 err = ata_rw28_rpc.vtbl.write_dma(&ata_rw28_rpc, buf, bytes, 137 start_lba, &status); 138 if (err_is_fail(err)) 139 USER_PANIC_ERR(err, "write_dma rpc"); 140 if (err_is_fail(status)) 141 USER_PANIC_ERR(status, "write_dma status"); 142\end{lstlisting} 143 144Reading data is equally simple: 145 146\begin{lstlisting} 147 size_t bytes_read; 148 err = ata_rw28_rpc.vtbl.read_dma(&ata_rw28_rpc, bytes, 149 start_lba, &buf, &bytes_read); 150 if (err_is_fail(err)) 151 USER_PANIC_ERR(err, "read_dma rpc"); 152 if (!buf) 153 USER_PANIC("read_dma -> !buf"); 154 if (bytes_read != bytes) 155 USER_PANIC("read_dma -> bytes_read != bytes"); 156\end{lstlisting} 157 158At the end, we do a simple verification and free the allocated buffer. 159 160\section{Cleanup} 161 162To return ownership of the port and clean up resources, a simple call to 163\lstinline+ahci_close+ suffices: 164 165\begin{lstlisting} 166 ahci_close(ahci_binding, NOP_CONT); 167\end{lstlisting} 168