1// Copyright 2017 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <errno.h>
6#include <fcntl.h>
7#include <limits.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#include <sys/stat.h>
12#include <sys/types.h>
13#include <time.h>
14#include <threads.h>
15#include <unistd.h>
16
17#include <block-client/client.h>
18#include <zircon/device/block.h>
19#include <zircon/syscalls.h>
20#include <fbl/algorithm.h>
21#include <fbl/alloc_checker.h>
22#include <fbl/array.h>
23#include <fbl/limits.h>
24#include <fbl/unique_ptr.h>
25#include <fbl/unique_fd.h>
26#include <pretty/hexdump.h>
27#include <unittest/unittest.h>
28
29#include <blktest/blktest.h>
30
31namespace tests {
32
33static int get_testdev(uint64_t* blk_size, uint64_t* blk_count) {
34    const char* blkdev_path = getenv(BLKTEST_BLK_DEV);
35    ASSERT_NONNULL(blkdev_path, "No test device specified");
36    // Open the block device
37    int fd = open(blkdev_path, O_RDWR);
38    if (fd < 0) {
39        printf("OPENING BLKDEV (path=%s) FAILURE. Errno: %d\n", blkdev_path, errno);
40    }
41    ASSERT_GE(fd, 0, "Could not open block device");
42    block_info_t info;
43    ssize_t rc = ioctl_block_get_info(fd, &info);
44    ASSERT_GE(rc, 0, "Could not get block size");
45    *blk_size = info.block_size;
46    *blk_count = info.block_count;
47    return fd;
48}
49
50static bool blkdev_test_simple(void) {
51    BEGIN_TEST;
52
53    uint64_t blk_size, blk_count;
54    fbl::unique_fd fd(get_testdev(&blk_size, &blk_count));
55    int64_t buffer_size = blk_size * 2;
56
57    fbl::AllocChecker checker;
58    fbl::unique_ptr<uint8_t[]> buf(new (&checker) uint8_t[buffer_size]);
59    ASSERT_TRUE(checker.check());
60    fbl::unique_ptr<uint8_t[]> out(new (&checker) uint8_t[buffer_size]);
61    ASSERT_TRUE(checker.check());
62
63    memset(buf.get(), 'a', sizeof(buf));
64    memset(out.get(), 0, sizeof(out));
65
66    // Write three blocks.
67    ASSERT_EQ(write(fd.get(), buf.get(), buffer_size), buffer_size);
68    ASSERT_EQ(write(fd.get(), buf.get(), buffer_size / 2), buffer_size / 2);
69
70    // Seek to the start of the device and read the contents
71    ASSERT_EQ(lseek(fd.get(), 0, SEEK_SET), 0, "");
72    ASSERT_EQ(read(fd.get(), out.get(), buffer_size), buffer_size);
73    ASSERT_EQ(memcmp(out.get(), buf.get(), buffer_size), 0);
74    ASSERT_EQ(read(fd.get(), out.get(), buffer_size / 2), buffer_size / 2);
75    ASSERT_EQ(memcmp(out.get(), buf.get(), buffer_size / 2), 0);
76
77    END_TEST;
78}
79
80bool blkdev_test_bad_requests(void) {
81    BEGIN_TEST;
82
83    uint64_t blk_size, blk_count;
84    fbl::unique_fd fd(get_testdev(&blk_size, &blk_count));
85
86    fbl::AllocChecker checker;
87    fbl::unique_ptr<uint8_t[]> buf(new (&checker) uint8_t[blk_size * 4]);
88    ASSERT_TRUE(checker.check());
89    memset(buf.get(), 'a', blk_size * 4);
90
91    // Read / write non-multiples of the block size
92    ASSERT_EQ(write(fd.get(), buf.get(), blk_size - 1), -1);
93    ASSERT_EQ(write(fd.get(), buf.get(), blk_size / 2), -1);
94    ASSERT_EQ(write(fd.get(), buf.get(), blk_size * 2 - 1), -1);
95    ASSERT_EQ(read(fd.get(), buf.get(), blk_size - 1), -1);
96    ASSERT_EQ(read(fd.get(), buf.get(), blk_size / 2), -1);
97    ASSERT_EQ(read(fd.get(), buf.get(), blk_size * 2 - 1), -1);
98
99    // Read / write from unaligned offset
100    ASSERT_EQ(lseek(fd.get(), 1, SEEK_SET), 1);
101    ASSERT_EQ(write(fd.get(), buf.get(), blk_size), -1);
102    ASSERT_EQ(errno, EINVAL);
103    ASSERT_EQ(read(fd.get(), buf.get(), blk_size), -1);
104    ASSERT_EQ(errno, EINVAL);
105
106    // Read / write from beyond end of device
107    off_t dev_size = blk_size * blk_count;
108    ASSERT_EQ(lseek(fd.get(), dev_size, SEEK_SET), dev_size);
109    ASSERT_EQ(write(fd.get(), buf.get(), blk_size), -1);
110    ASSERT_EQ(read(fd.get(), buf.get(), blk_size), -1);
111
112    END_TEST;
113}
114
115#if 0
116bool blkdev_test_multiple(void) {
117    uint8_t buf[PAGE_SIZE];
118    uint8_t out[PAGE_SIZE];
119
120    BEGIN_TEST;
121    int fd1 = get_testdev("blkdev-test-A", PAGE_SIZE, 512);
122    int fd2 = get_testdev("blkdev-test-B", PAGE_SIZE, 512);
123
124    // Write 'a' to fd1, write 'b', to fd2
125    memset(buf, 'a', sizeof(buf));
126    ASSERT_EQ(write(fd1, buf, sizeof(buf)), (ssize_t) sizeof(buf), "");
127    memset(buf, 'b', sizeof(buf));
128    ASSERT_EQ(write(fd2, buf, sizeof(buf)), (ssize_t) sizeof(buf), "");
129
130    ASSERT_EQ(lseek(fd1, 0, SEEK_SET), 0, "");
131    ASSERT_EQ(lseek(fd2, 0, SEEK_SET), 0, "");
132
133    // Read 'b' from fd2, read 'a' from fd1
134    ASSERT_EQ(read(fd2, out, sizeof(buf)), (ssize_t) sizeof(buf), "");
135    ASSERT_EQ(memcmp(out, buf, sizeof(out)), 0, "");
136    close(fd2);
137
138    memset(buf, 'a', sizeof(buf));
139    ASSERT_EQ(read(fd1, out, sizeof(buf)), (ssize_t) sizeof(buf), "");
140    ASSERT_EQ(memcmp(out, buf, sizeof(out)), 0, "");
141    close(fd1);
142
143    END_TEST;
144}
145#endif
146
147bool blkdev_test_fifo_no_op(void) {
148    // Get a FIFO connection to a blkdev and immediately close it
149    BEGIN_TEST;
150    uint64_t blk_size, blk_count;
151    int fd = get_testdev(&blk_size, &blk_count);
152    zx_handle_t fifo;
153    ssize_t expected = sizeof(fifo);
154    ASSERT_EQ(ioctl_block_get_fifos(fd, &fifo), expected, "Failed to get FIFO");
155    ASSERT_EQ(ioctl_block_fifo_close(fd), ZX_OK, "Failed to close fifo");
156    close(fd);
157    END_TEST;
158}
159
160static void fill_random(uint8_t* buf, uint64_t size) {
161    for (size_t i = 0; i < size; i++) {
162        buf[i] = static_cast<uint8_t>(rand());
163    }
164}
165
166bool blkdev_test_fifo_basic(void) {
167    BEGIN_TEST;
168    uint64_t blk_size, blk_count;
169    // Set up the initial handshake connection with the blkdev
170    int fd = get_testdev(&blk_size, &blk_count);
171    zx_handle_t fifo;
172    ssize_t expected = sizeof(fifo);
173    ASSERT_EQ(ioctl_block_get_fifos(fd, &fifo), expected, "Failed to get FIFO");
174    groupid_t group = 0;
175
176    // Create an arbitrary VMO, fill it with some stuff
177    const uint64_t vmo_size = blk_size * 3;
178    zx_handle_t vmo;
179    ASSERT_EQ(zx_vmo_create(vmo_size, 0, &vmo), ZX_OK, "Failed to create VMO");
180    fbl::AllocChecker ac;
181    fbl::unique_ptr<uint8_t[]> buf(new (&ac) uint8_t[vmo_size]);
182    ASSERT_TRUE(ac.check(), "");
183    fill_random(buf.get(), vmo_size);
184
185    ASSERT_EQ(zx_vmo_write(vmo, buf.get(), 0, vmo_size), ZX_OK, "");
186
187    // Send a handle to the vmo to the block device, get a vmoid which identifies it
188    vmoid_t vmoid;
189    expected = sizeof(vmoid_t);
190    zx_handle_t xfer_vmo;
191    ASSERT_EQ(zx_handle_duplicate(vmo, ZX_RIGHT_SAME_RIGHTS, &xfer_vmo), ZX_OK, "");
192    ASSERT_EQ(ioctl_block_attach_vmo(fd, &xfer_vmo, &vmoid), expected,
193              "Failed to attach vmo");
194
195    // Batch write the VMO to the blkdev
196    // Split it into two requests, spread across the disk
197    block_fifo_request_t requests[2];
198    requests[0].group      = group;
199    requests[0].vmoid      = vmoid;
200    requests[0].opcode     = BLOCKIO_WRITE;
201    requests[0].length     = 1;
202    requests[0].vmo_offset = 0;
203    requests[0].dev_offset = 0;
204
205    requests[1].group      = group;
206    requests[1].vmoid      = vmoid;
207    requests[1].opcode     = BLOCKIO_WRITE;
208    requests[1].length     = 2;
209    requests[1].vmo_offset = 1;
210    requests[1].dev_offset = 100;
211
212    fifo_client_t* client;
213    ASSERT_EQ(block_fifo_create_client(fifo, &client), ZX_OK, "");
214    ASSERT_EQ(block_fifo_txn(client, &requests[0], fbl::count_of(requests)), ZX_OK, "");
215
216    // Empty the vmo, then read the info we just wrote to the disk
217    fbl::unique_ptr<uint8_t[]> out(new (&ac) uint8_t[vmo_size]());
218    ASSERT_TRUE(ac.check(), "");
219
220    ASSERT_EQ(zx_vmo_write(vmo, out.get(), 0, vmo_size), ZX_OK, "");
221    requests[0].opcode = BLOCKIO_READ;
222    requests[1].opcode = BLOCKIO_READ;
223    ASSERT_EQ(block_fifo_txn(client, &requests[0], fbl::count_of(requests)), ZX_OK, "");
224    ASSERT_EQ(zx_vmo_read(vmo, out.get(), 0, vmo_size), ZX_OK, "");
225    ASSERT_EQ(memcmp(buf.get(), out.get(), blk_size * 3), 0, "Read data not equal to written data");
226
227    // Close the current vmo
228    requests[0].opcode = BLOCKIO_CLOSE_VMO;
229    ASSERT_EQ(block_fifo_txn(client, &requests[0], 1), ZX_OK, "");
230
231    ASSERT_EQ(zx_handle_close(vmo), ZX_OK, "");
232    block_fifo_release_client(client);
233    ASSERT_EQ(ioctl_block_fifo_close(fd), ZX_OK, "Failed to close fifo");
234    close(fd);
235    END_TEST;
236}
237
238bool blkdev_test_fifo_whole_disk(void) {
239    BEGIN_TEST;
240    uint64_t blk_size, blk_count;
241    // Set up the initial handshake connection with the blkdev
242    int fd = get_testdev(&blk_size, &blk_count);
243    zx_handle_t fifo;
244    ssize_t expected = sizeof(fifo);
245    ASSERT_EQ(ioctl_block_get_fifos(fd, &fifo), expected, "Failed to get FIFO");
246    groupid_t group = 0;
247
248    // Create an arbitrary VMO, fill it with some stuff
249    uint64_t vmo_size = blk_size * blk_count;
250    zx_handle_t vmo;
251    ASSERT_EQ(zx_vmo_create(vmo_size, 0, &vmo), ZX_OK, "Failed to create VMO");
252    fbl::AllocChecker ac;
253    fbl::unique_ptr<uint8_t[]> buf(new (&ac) uint8_t[vmo_size]);
254    ASSERT_TRUE(ac.check(), "");
255    fill_random(buf.get(), vmo_size);
256
257    ASSERT_EQ(zx_vmo_write(vmo, buf.get(), 0, vmo_size), ZX_OK, "");
258
259    // Send a handle to the vmo to the block device, get a vmoid which identifies it
260    vmoid_t vmoid;
261    expected = sizeof(vmoid_t);
262    zx_handle_t xfer_vmo;
263    ASSERT_EQ(zx_handle_duplicate(vmo, ZX_RIGHT_SAME_RIGHTS, &xfer_vmo), ZX_OK, "");
264    ASSERT_EQ(ioctl_block_attach_vmo(fd, &xfer_vmo, &vmoid), expected,
265              "Failed to attach vmo");
266
267    // Batch write the VMO to the blkdev
268    block_fifo_request_t request;
269    request.group      = group;
270    request.vmoid      = vmoid;
271    request.opcode     = BLOCKIO_WRITE;
272    request.length     = static_cast<uint32_t>(blk_count);
273    request.vmo_offset = 0;
274    request.dev_offset = 0;
275
276    fifo_client_t* client;
277    ASSERT_EQ(block_fifo_create_client(fifo, &client), ZX_OK, "");
278    ASSERT_EQ(block_fifo_txn(client, &request, 1), ZX_OK, "");
279
280    // Empty the vmo, then read the info we just wrote to the disk
281    fbl::unique_ptr<uint8_t[]> out(new (&ac) uint8_t[vmo_size]());
282    ASSERT_TRUE(ac.check(), "");
283
284    ASSERT_EQ(zx_vmo_write(vmo, out.get(), 0, vmo_size), ZX_OK, "");
285    request.opcode = BLOCKIO_READ;
286    ASSERT_EQ(block_fifo_txn(client, &request, 1), ZX_OK, "");
287    ASSERT_EQ(zx_vmo_read(vmo, out.get(), 0, vmo_size), ZX_OK, "");
288    ASSERT_EQ(memcmp(buf.get(), out.get(), blk_size * 3), 0, "Read data not equal to written data");
289
290    // Close the current vmo
291    request.opcode = BLOCKIO_CLOSE_VMO;
292    ASSERT_EQ(block_fifo_txn(client, &request, 1), ZX_OK, "");
293
294    ASSERT_EQ(zx_handle_close(vmo), ZX_OK, "");
295    block_fifo_release_client(client);
296    ASSERT_EQ(ioctl_block_fifo_close(fd), ZX_OK, "Failed to close fifo");
297    close(fd);
298    END_TEST;
299}
300
301typedef struct {
302    uint64_t vmo_size;
303    zx_handle_t vmo;
304    vmoid_t vmoid;
305    fbl::unique_ptr<uint8_t[]> buf;
306} test_vmo_object_t;
307
308// Creates a VMO, fills it with data, and gives it to the block device.
309bool create_vmo_helper(int fd, test_vmo_object_t* obj, size_t kBlockSize) {
310    obj->vmo_size = kBlockSize + (rand() % 5) * kBlockSize;
311    ASSERT_EQ(zx_vmo_create(obj->vmo_size, 0, &obj->vmo), ZX_OK,
312              "Failed to create vmo");
313    fbl::AllocChecker ac;
314    obj->buf.reset(new (&ac) uint8_t[obj->vmo_size]);
315    ASSERT_TRUE(ac.check(), "");
316    fill_random(obj->buf.get(), obj->vmo_size);
317    ASSERT_EQ(zx_vmo_write(obj->vmo, obj->buf.get(), 0, obj->vmo_size),
318              ZX_OK, "Failed to write to vmo");
319
320    ssize_t expected = sizeof(vmoid_t);
321    zx_handle_t xfer_vmo;
322    ASSERT_EQ(zx_handle_duplicate(obj->vmo, ZX_RIGHT_SAME_RIGHTS, &xfer_vmo), ZX_OK,
323              "Failed to duplicate vmo");
324    ASSERT_EQ(ioctl_block_attach_vmo(fd, &xfer_vmo, &obj->vmoid), expected,
325              "Failed to attach vmo");
326    return true;
327}
328
329// Write all vmos in a striped pattern on disk.
330// For objs.size() == 10,
331// i = 0 will write vmo block 0, 1, 2, 3... to dev block 0, 10, 20, 30...
332// i = 1 will write vmo block 0, 1, 2, 3... to dev block 1, 11, 21, 31...
333bool write_striped_vmo_helper(fifo_client_t* client, test_vmo_object_t* obj, size_t i, size_t objs,
334                              groupid_t group, size_t kBlockSize) {
335    // Make a separate request for each block
336    size_t blocks = obj->vmo_size / kBlockSize;
337    fbl::AllocChecker ac;
338    fbl::Array<block_fifo_request_t> requests(new (&ac) block_fifo_request_t[blocks], blocks);
339    ASSERT_TRUE(ac.check(), "");
340    for (size_t b = 0; b < blocks; b++) {
341        requests[b].group      = group;
342        requests[b].vmoid      = obj->vmoid;
343        requests[b].opcode     = BLOCKIO_WRITE;
344        requests[b].length     = 1;
345        requests[b].vmo_offset = b;
346        requests[b].dev_offset = i + b * objs;
347    }
348    // Write entire vmos at once
349    ASSERT_EQ(block_fifo_txn(client, &requests[0], requests.size()), ZX_OK, "");
350    return true;
351}
352
353// Verifies the result from "write_striped_vmo_helper"
354bool read_striped_vmo_helper(fifo_client_t* client, test_vmo_object_t* obj, size_t i, size_t objs,
355                             groupid_t group, size_t kBlockSize) {
356    // First, empty out the VMO
357    fbl::AllocChecker ac;
358    fbl::unique_ptr<uint8_t[]> out(new (&ac) uint8_t[obj->vmo_size]());
359    ASSERT_TRUE(ac.check(), "");
360    ASSERT_EQ(zx_vmo_write(obj->vmo, out.get(), 0, obj->vmo_size),
361              ZX_OK, "");
362
363    // Next, read to the vmo from the disk
364    size_t blocks = obj->vmo_size / kBlockSize;
365    fbl::Array<block_fifo_request_t> requests(new (&ac) block_fifo_request_t[blocks], blocks);
366    ASSERT_TRUE(ac.check(), "");
367    for (size_t b = 0; b < blocks; b++) {
368        requests[b].group      = group;
369        requests[b].vmoid      = obj->vmoid;
370        requests[b].opcode     = BLOCKIO_READ;
371        requests[b].length     = 1;
372        requests[b].vmo_offset = b;
373        requests[b].dev_offset = i + b * objs;
374    }
375    // Read entire vmos at once
376    ASSERT_EQ(block_fifo_txn(client, &requests[0], requests.size()), ZX_OK, "");
377
378    // Finally, write from the vmo to an out buffer, where we can compare
379    // the results with the input buffer.
380    ASSERT_EQ(zx_vmo_read(obj->vmo, out.get(), 0, obj->vmo_size),
381              ZX_OK, "");
382    ASSERT_EQ(memcmp(obj->buf.get(), out.get(), obj->vmo_size), 0,
383              "Read data not equal to written data");
384    return true;
385}
386
387// Tears down an object created by "create_vmo_helper".
388bool close_vmo_helper(fifo_client_t* client, test_vmo_object_t* obj, groupid_t group) {
389    block_fifo_request_t request;
390    request.group = group;
391    request.vmoid = obj->vmoid;
392    request.opcode = BLOCKIO_CLOSE_VMO;
393    ASSERT_EQ(block_fifo_txn(client, &request, 1), ZX_OK, "");
394    ASSERT_EQ(zx_handle_close(obj->vmo), ZX_OK, "");
395    return true;
396}
397
398bool blkdev_test_fifo_multiple_vmo(void) {
399    BEGIN_TEST;
400    // Set up the initial handshake connection with the blkdev
401    uint64_t blk_size, blk_count;
402    int fd = get_testdev(&blk_size, &blk_count);
403    zx_handle_t fifo;
404    ssize_t expected = sizeof(fifo);
405    ASSERT_EQ(ioctl_block_get_fifos(fd, &fifo), expected, "Failed to get FIFO");
406    groupid_t group = 0;
407    fifo_client_t* client;
408    ASSERT_EQ(block_fifo_create_client(fifo, &client), ZX_OK, "");
409
410    // Create multiple VMOs
411    fbl::AllocChecker ac;
412    fbl::Array<test_vmo_object_t> objs(new (&ac) test_vmo_object_t[10](), 10);
413    ASSERT_TRUE(ac.check(), "");
414    for (size_t i = 0; i < objs.size(); i++) {
415        ASSERT_TRUE(create_vmo_helper(fd, &objs[i], blk_size), "");
416    }
417
418    for (size_t i = 0; i < objs.size(); i++) {
419        ASSERT_TRUE(write_striped_vmo_helper(client, &objs[i], i, objs.size(), group, blk_size), "");
420    }
421
422    for (size_t i = 0; i < objs.size(); i++) {
423        ASSERT_TRUE(read_striped_vmo_helper(client, &objs[i], i, objs.size(), group, blk_size), "");
424    }
425
426    for (size_t i = 0; i < objs.size(); i++) {
427        ASSERT_TRUE(close_vmo_helper(client, &objs[i], group), "");
428    }
429
430    block_fifo_release_client(client);
431    ASSERT_EQ(ioctl_block_fifo_close(fd), ZX_OK, "Failed to close fifo");
432    close(fd);
433    END_TEST;
434}
435
436typedef struct {
437    test_vmo_object_t* obj;
438    size_t i;
439    size_t objs;
440    int fd;
441    fifo_client_t* client;
442    groupid_t group;
443    size_t kBlockSize;
444} test_thread_arg_t;
445
446int fifo_vmo_thread(void* arg) {
447    test_thread_arg_t* fifoarg = (test_thread_arg_t*) arg;
448    test_vmo_object_t* obj = fifoarg->obj;
449    size_t i = fifoarg->i;
450    size_t objs = fifoarg->objs;
451    int fd = fifoarg->fd;
452    fifo_client_t* client = fifoarg->client;
453    groupid_t group = fifoarg->group;
454    size_t kBlockSize = fifoarg->kBlockSize;
455
456    ASSERT_TRUE(create_vmo_helper(fd, obj, kBlockSize), "");
457    ASSERT_TRUE(write_striped_vmo_helper(client, obj, i, objs, group, kBlockSize), "");
458    ASSERT_TRUE(read_striped_vmo_helper(client, obj, i, objs, group, kBlockSize), "");
459    ASSERT_TRUE(close_vmo_helper(client, obj, group), "");
460    return 0;
461}
462
463bool blkdev_test_fifo_multiple_vmo_multithreaded(void) {
464    BEGIN_TEST;
465    // Set up the initial handshake connection with the blkdev
466    uint64_t kBlockSize, blk_count;
467    int fd = get_testdev(&kBlockSize, &blk_count);
468    zx_handle_t fifo;
469    ssize_t expected = sizeof(fifo);
470    ASSERT_EQ(ioctl_block_get_fifos(fd, &fifo), expected, "Failed to get FIFO");
471    fifo_client_t* client;
472    ASSERT_EQ(block_fifo_create_client(fifo, &client), ZX_OK, "");
473
474    // Create multiple VMOs
475    size_t num_threads = MAX_TXN_GROUP_COUNT;
476    fbl::AllocChecker ac;
477    fbl::Array<test_vmo_object_t> objs(new (&ac) test_vmo_object_t[num_threads](), num_threads);
478    ASSERT_TRUE(ac.check(), "");
479
480    fbl::Array<thrd_t> threads(new (&ac) thrd_t[num_threads](), num_threads);
481    ASSERT_TRUE(ac.check(), "");
482
483    fbl::Array<test_thread_arg_t> thread_args(new (&ac) test_thread_arg_t[num_threads](),
484                                               num_threads);
485    ASSERT_TRUE(ac.check(), "");
486
487    for (size_t i = 0; i < num_threads; i++) {
488        // Yes, this does create a bunch of duplicate fields, but it's an easy way to
489        // transfer some data without creating global variables.
490        thread_args[i].obj = &objs[i];
491        thread_args[i].i = i;
492        thread_args[i].objs = objs.size();
493        thread_args[i].fd = fd;
494        thread_args[i].client = client;
495        thread_args[i].group = static_cast<groupid_t>(i);
496        thread_args[i].kBlockSize = kBlockSize;
497        ASSERT_EQ(thrd_create(&threads[i], fifo_vmo_thread, &thread_args[i]),
498                  thrd_success, "");
499    }
500
501    for (size_t i = 0; i < num_threads; i++) {
502        int res;
503        ASSERT_EQ(thrd_join(threads[i], &res), thrd_success, "");
504        ASSERT_EQ(res, 0, "");
505    }
506
507    block_fifo_release_client(client);
508    ASSERT_EQ(ioctl_block_fifo_close(fd), ZX_OK, "Failed to close fifo");
509    close(fd);
510    END_TEST;
511}
512
513bool blkdev_test_fifo_unclean_shutdown(void) {
514    BEGIN_TEST;
515    // Set up the blkdev
516    uint64_t kBlockSize, blk_count;
517    int fd = get_testdev(&kBlockSize, &blk_count);
518
519    // Create a connection to the blkdev
520    zx_handle_t fifo;
521    ssize_t expected = sizeof(fifo);
522    ASSERT_EQ(ioctl_block_get_fifos(fd, &fifo), expected, "Failed to get FIFO");
523    ASSERT_EQ(ioctl_block_get_fifos(fd, &fifo), ZX_ERR_ALREADY_BOUND,
524              "Expected fifo to already be bound");
525    fifo_client_t* client;
526    ASSERT_EQ(block_fifo_create_client(fifo, &client), ZX_OK, "");
527    groupid_t group = 0;
528
529    // Create multiple VMOs
530    fbl::AllocChecker ac;
531    fbl::Array<test_vmo_object_t> objs(new (&ac) test_vmo_object_t[10](), 10);
532    ASSERT_TRUE(ac.check(), "");
533    for (size_t i = 0; i < objs.size(); i++) {
534        ASSERT_TRUE(create_vmo_helper(fd, &objs[i], kBlockSize), "");
535    }
536
537    // Now that we've set up the connection for a few VMOs, shut down the fifo
538    ASSERT_EQ(zx_handle_close(fifo), ZX_OK, "");
539
540    // Attempting to batch any operations to the fifo should fail
541    block_fifo_request_t request;
542    request.group = group;
543    request.vmoid = objs[0].vmoid;
544    request.opcode = BLOCKIO_CLOSE_VMO;
545    ASSERT_NE(block_fifo_txn(client, &request, 1), ZX_OK,
546              "Expected operation to fail after closing FIFO");
547
548    // Free the dead client
549    block_fifo_release_client(client);
550
551    // Give the block server a moment to realize our side of the fifo has been closed
552    usleep(10000);
553
554    // The block server should still be functioning. We should be able to re-bind to it
555    expected = sizeof(fifo);
556    ASSERT_EQ(ioctl_block_get_fifos(fd, &fifo), expected, "Failed to get FIFO");
557    ASSERT_EQ(block_fifo_create_client(fifo, &client), ZX_OK, "");
558
559    for (size_t i = 0; i < objs.size(); i++) {
560        ASSERT_TRUE(create_vmo_helper(fd, &objs[i], kBlockSize), "");
561    }
562    for (size_t i = 0; i < objs.size(); i++) {
563        ASSERT_TRUE(write_striped_vmo_helper(client, &objs[i], i, objs.size(), group, kBlockSize), "");
564    }
565    for (size_t i = 0; i < objs.size(); i++) {
566        ASSERT_TRUE(read_striped_vmo_helper(client, &objs[i], i, objs.size(), group, kBlockSize), "");
567    }
568    for (size_t i = 0; i < objs.size(); i++) {
569        ASSERT_TRUE(close_vmo_helper(client, &objs[i], group), "");
570    }
571
572    block_fifo_release_client(client);
573    ASSERT_EQ(ioctl_block_fifo_close(fd), ZX_OK, "Failed to close fifo");
574    close(fd);
575    END_TEST;
576}
577
578bool blkdev_test_fifo_bad_client_vmoid(void) {
579    // Try to flex the server's error handling by sending 'malicious' client requests.
580    BEGIN_TEST;
581    // Set up the blkdev
582    uint64_t kBlockSize, blk_count;
583    int fd = get_testdev(&kBlockSize, &blk_count);
584
585    // Create a connection to the blkdev
586    zx_handle_t fifo;
587    ssize_t expected = sizeof(fifo);
588    ASSERT_EQ(ioctl_block_get_fifos(fd, &fifo), expected, "Failed to get FIFO");
589    fifo_client_t* client;
590    ASSERT_EQ(block_fifo_create_client(fifo, &client), ZX_OK, "");
591    groupid_t group = 0;
592
593    // Create a vmo
594    test_vmo_object_t obj;
595    ASSERT_TRUE(create_vmo_helper(fd, &obj, kBlockSize), "");
596
597    // Bad request: Writing to the wrong vmoid
598    block_fifo_request_t request;
599    request.group      = group;
600    request.vmoid      = static_cast<vmoid_t>(obj.vmoid + 5);
601    request.opcode     = BLOCKIO_WRITE;
602    request.length     = 1;
603    request.vmo_offset = 0;
604    request.dev_offset = 0;
605    ASSERT_EQ(block_fifo_txn(client, &request, 1), ZX_ERR_IO, "Expected IO error with bad vmoid");
606
607    block_fifo_release_client(client);
608    ASSERT_EQ(ioctl_block_fifo_close(fd), ZX_OK, "Failed to close fifo");
609    close(fd);
610    END_TEST;
611}
612
613bool blkdev_test_fifo_bad_client_unaligned_request(void) {
614    // Try to flex the server's error handling by sending 'malicious' client requests.
615    BEGIN_TEST;
616    // Set up the blkdev
617    uint64_t kBlockSize, blk_count;
618    int fd = get_testdev(&kBlockSize, &blk_count);
619
620    // Create a connection to the blkdev
621    zx_handle_t fifo;
622    ssize_t expected = sizeof(fifo);
623    ASSERT_EQ(ioctl_block_get_fifos(fd, &fifo), expected, "Failed to get FIFO");
624    fifo_client_t* client;
625    ASSERT_EQ(block_fifo_create_client(fifo, &client), ZX_OK, "");
626    groupid_t group = 0;
627
628    // Create a vmo of at least size "kBlockSize * 2", since we'll
629    // be reading "kBlockSize" bytes from an offset below, and we want it
630    // to fit within the bounds of the VMO.
631    test_vmo_object_t obj;
632    ASSERT_TRUE(create_vmo_helper(fd, &obj, kBlockSize * 2), "");
633
634    block_fifo_request_t request;
635    request.group      = group;
636    request.vmoid      = static_cast<vmoid_t>(obj.vmoid);
637    request.opcode     = BLOCKIO_WRITE;
638
639    // Send a request that has zero length
640    request.length     = 0;
641    request.vmo_offset = 0;
642    request.dev_offset = 0;
643    ASSERT_EQ(block_fifo_txn(client, &request, 1), ZX_ERR_INVALID_ARGS, "");
644
645    block_fifo_release_client(client);
646    ASSERT_EQ(ioctl_block_fifo_close(fd), ZX_OK, "Failed to close fifo");
647    close(fd);
648    END_TEST;
649}
650
651bool blkdev_test_fifo_bad_client_overflow(void) {
652    // Try to flex the server's error handling by sending 'malicious' client requests.
653    BEGIN_TEST;
654    // Set up the blkdev
655    uint64_t kBlockSize, blk_count;
656    int fd = get_testdev(&kBlockSize, &blk_count);
657
658    // Create a connection to the blkdev
659    zx_handle_t fifo;
660    ssize_t expected = sizeof(fifo);
661    ASSERT_EQ(ioctl_block_get_fifos(fd, &fifo), expected, "Failed to get FIFO");
662    fifo_client_t* client;
663    ASSERT_EQ(block_fifo_create_client(fifo, &client), ZX_OK, "");
664    groupid_t group = 0;
665
666    // Create a vmo of at least size "kBlockSize * 2", since we'll
667    // be reading "kBlockSize" bytes from an offset below, and we want it
668    // to fit within the bounds of the VMO.
669    test_vmo_object_t obj;
670    ASSERT_TRUE(create_vmo_helper(fd, &obj, kBlockSize * 2), "");
671
672    block_fifo_request_t request;
673    request.group      = group;
674    request.vmoid      = static_cast<vmoid_t>(obj.vmoid);
675    request.opcode     = BLOCKIO_WRITE;
676
677    // Send a request that is barely out-of-bounds for the device
678    request.length     = 1;
679    request.vmo_offset = 0;
680    request.dev_offset = blk_count;
681    ASSERT_EQ(block_fifo_txn(client, &request, 1), ZX_ERR_OUT_OF_RANGE);
682
683    // Send a request that is half out-of-bounds for the device
684    request.length     = 2;
685    request.vmo_offset = 0;
686    request.dev_offset = blk_count - 1;
687    ASSERT_EQ(block_fifo_txn(client, &request, 1), ZX_ERR_OUT_OF_RANGE);
688
689    // Send a request that is very out-of-bounds for the device
690    request.length     = 1;
691    request.vmo_offset = 0;
692    request.dev_offset = blk_count + 1;
693    ASSERT_EQ(block_fifo_txn(client, &request, 1), ZX_ERR_OUT_OF_RANGE);
694
695    // Send a request that tries to overflow the VMO
696    request.length     = 2;
697    request.vmo_offset = fbl::numeric_limits<uint64_t>::max();
698    request.dev_offset = 0;
699    ASSERT_EQ(block_fifo_txn(client, &request, 1), ZX_ERR_OUT_OF_RANGE);
700
701    // Send a request that tries to overflow the device
702    request.length     = 2;
703    request.vmo_offset = 0;
704    request.dev_offset = fbl::numeric_limits<uint64_t>::max();
705    ASSERT_EQ(block_fifo_txn(client, &request, 1), ZX_ERR_OUT_OF_RANGE);
706
707    block_fifo_release_client(client);
708    ASSERT_EQ(ioctl_block_fifo_close(fd), ZX_OK, "Failed to close fifo");
709    close(fd);
710    END_TEST;
711}
712
713bool blkdev_test_fifo_bad_client_bad_vmo(void) {
714    // Try to flex the server's error handling by sending 'malicious' client requests.
715    BEGIN_TEST;
716    // Set up the blkdev
717    uint64_t kBlockSize, blk_count;
718    int fd = get_testdev(&kBlockSize, &blk_count);
719
720    // Create a connection to the blkdev
721    zx_handle_t fifo;
722    ssize_t expected = sizeof(fifo);
723    ASSERT_EQ(ioctl_block_get_fifos(fd, &fifo), expected, "Failed to get FIFO");
724    fifo_client_t* client;
725    ASSERT_EQ(block_fifo_create_client(fifo, &client), ZX_OK, "");
726    groupid_t group = 0;
727
728    // Create a vmo of one block.
729    //
730    // The underlying VMO may be rounded up to the nearest PAGE_SIZE.
731    test_vmo_object_t obj;
732    obj.vmo_size = kBlockSize;
733    ASSERT_EQ(zx_vmo_create(obj.vmo_size, 0, &obj.vmo), ZX_OK,
734              "Failed to create vmo");
735    fbl::AllocChecker ac;
736    obj.buf.reset(new (&ac) uint8_t[obj.vmo_size]);
737    ASSERT_TRUE(ac.check(), "");
738    fill_random(obj.buf.get(), obj.vmo_size);
739    ASSERT_EQ(zx_vmo_write(obj.vmo, obj.buf.get(), 0, obj.vmo_size),
740              ZX_OK, "Failed to write to vmo");
741    zx_handle_t xfer_vmo;
742    ASSERT_EQ(zx_handle_duplicate(obj.vmo, ZX_RIGHT_SAME_RIGHTS, &xfer_vmo), ZX_OK,
743              "Failed to duplicate vmo");
744    expected = sizeof(vmoid_t);
745    ASSERT_EQ(ioctl_block_attach_vmo(fd, &xfer_vmo, &obj.vmoid), expected,
746              "Failed to attach vmo");
747
748    // Send a request to write to write multiple blocks -- enough that
749    // the request is larger than the VMO.
750    const uint64_t length = 1 + (fbl::round_up(obj.vmo_size,
751                                               static_cast<uint64_t>(PAGE_SIZE))
752                                 / kBlockSize);
753    block_fifo_request_t request;
754    request.group      = group;
755    request.vmoid      = static_cast<vmoid_t>(obj.vmoid);
756    request.opcode     = BLOCKIO_WRITE;
757    request.length     = static_cast<uint32_t>(length);
758    request.vmo_offset = 0;
759    request.dev_offset = 0;
760    ASSERT_EQ(block_fifo_txn(client, &request, 1), ZX_ERR_OUT_OF_RANGE, "");
761    // Do the same thing, but for reading
762    request.opcode     = BLOCKIO_READ;
763    ASSERT_EQ(block_fifo_txn(client, &request, 1), ZX_ERR_OUT_OF_RANGE, "");
764
765    block_fifo_release_client(client);
766    ASSERT_EQ(ioctl_block_fifo_close(fd), ZX_OK, "Failed to close fifo");
767    close(fd);
768    END_TEST;
769}
770
771BEGIN_TEST_CASE(blkdev_tests)
772RUN_TEST(blkdev_test_simple)
773RUN_TEST(blkdev_test_bad_requests)
774#if 0
775RUN_TEST(blkdev_test_multiple)
776#endif
777RUN_TEST(blkdev_test_fifo_no_op)
778RUN_TEST(blkdev_test_fifo_basic)
779//RUN_TEST(blkdev_test_fifo_whole_disk)
780RUN_TEST(blkdev_test_fifo_multiple_vmo)
781RUN_TEST(blkdev_test_fifo_multiple_vmo_multithreaded)
782// TODO(smklein): Test ops across different vmos
783RUN_TEST(blkdev_test_fifo_unclean_shutdown)
784RUN_TEST(blkdev_test_fifo_bad_client_vmoid)
785RUN_TEST(blkdev_test_fifo_bad_client_unaligned_request)
786RUN_TEST(blkdev_test_fifo_bad_client_overflow)
787RUN_TEST(blkdev_test_fifo_bad_client_bad_vmo)
788END_TEST_CASE(blkdev_tests)
789
790} // namespace tests
791