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