1// Copyright 2018 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 <lib/fzl/mapped-vmo.h>
6
7#include <limits.h>
8#include <stddef.h>
9#include <unittest/unittest.h>
10#include <zircon/syscalls.h>
11
12namespace {
13
14constexpr size_t page = PAGE_SIZE;
15constexpr char vmo_name[ZX_MAX_NAME_LEN] = "my-vmo";
16
17bool CreateTest() {
18    BEGIN_TEST;
19
20    fbl::unique_ptr<fzl::MappedVmo> mapped_vmo;
21    zx_status_t status = fzl::MappedVmo::Create(page, vmo_name, &mapped_vmo);
22
23    EXPECT_EQ(status, ZX_OK);
24    EXPECT_NONNULL(mapped_vmo);
25    EXPECT_NE(mapped_vmo->GetVmo(), ZX_HANDLE_INVALID);
26    EXPECT_EQ(mapped_vmo->GetSize(), page);
27    EXPECT_NONNULL(mapped_vmo->GetData());
28
29    auto data = static_cast<const uint8_t*>(mapped_vmo->GetData());
30    for (size_t i = 0; i < page; ++i) {
31        EXPECT_EQ(data[i], 0);
32    }
33
34    auto vmo = mapped_vmo->GetVmo();
35    char name[ZX_MAX_NAME_LEN] = {};
36    status = zx_object_get_property(vmo, ZX_PROP_NAME, name, ZX_MAX_NAME_LEN);
37    EXPECT_EQ(status, ZX_OK);
38    for (size_t i = 0; i < ZX_MAX_NAME_LEN; ++i) {
39        EXPECT_EQ(name[i], vmo_name[i]);
40    }
41
42    END_TEST;
43}
44
45bool ReadTest() {
46    BEGIN_TEST;
47
48    fbl::unique_ptr<fzl::MappedVmo> mapped_vmo;
49    zx_status_t status = fzl::MappedVmo::Create(page, vmo_name, &mapped_vmo);
50    EXPECT_EQ(status, ZX_OK);
51
52    uint8_t bytes[page];
53    memset(bytes, 0xff, page);
54
55    status = zx_vmo_read(mapped_vmo->GetVmo(), bytes, 0, page);
56    EXPECT_EQ(status, ZX_OK);
57    for (size_t i = 0; i < page; ++i) {
58        EXPECT_EQ(bytes[i], 0);
59    }
60
61    END_TEST;
62}
63
64// Test that touching memory, then zx_vmo_reading, works as expected.
65bool WriteMappingTest() {
66    BEGIN_TEST;
67
68    fbl::unique_ptr<fzl::MappedVmo> mapped_vmo;
69    zx_status_t status = fzl::MappedVmo::Create(page, vmo_name, &mapped_vmo);
70    EXPECT_EQ(status, ZX_OK);
71
72    auto data = static_cast<uint8_t*>(mapped_vmo->GetData());
73    memset(data, 0xff, page);
74
75    uint8_t bytes[page] = {};
76    status = zx_vmo_read(mapped_vmo->GetVmo(), bytes, 0, page);
77    EXPECT_EQ(status, ZX_OK);
78    for (size_t i = 0; i < page; ++i) {
79        EXPECT_EQ(bytes[i], 0xff);
80    }
81
82    END_TEST;
83}
84
85// Test that zx_vmo_writing, then reading memory, works as expected.
86bool ReadMappingTest() {
87    BEGIN_TEST;
88
89    fbl::unique_ptr<fzl::MappedVmo> mapped_vmo;
90    zx_status_t status = fzl::MappedVmo::Create(page, vmo_name, &mapped_vmo);
91    EXPECT_EQ(status, ZX_OK);
92
93    uint8_t bytes[page];
94    memset(bytes, 0xff, page);
95    status = zx_vmo_write(mapped_vmo->GetVmo(), bytes, 0, page);
96    EXPECT_EQ(status, ZX_OK);
97
98    auto data = static_cast<uint8_t*>(mapped_vmo->GetData());
99    for (size_t i = 0; i < page; ++i) {
100        EXPECT_EQ(data[i], 0xff);
101    }
102
103    END_TEST;
104}
105
106bool EmptyNameTest() {
107    BEGIN_TEST;
108
109    fbl::unique_ptr<fzl::MappedVmo> mapped_vmo;
110    zx_status_t status = fzl::MappedVmo::Create(page, "", &mapped_vmo);
111    EXPECT_EQ(status, ZX_OK);
112
113    auto vmo = mapped_vmo->GetVmo();
114    char name[ZX_MAX_NAME_LEN] = {};
115    status = zx_object_get_property(vmo, ZX_PROP_NAME, name, ZX_MAX_NAME_LEN);
116    EXPECT_EQ(status, ZX_OK);
117    for (size_t i = 0; i < ZX_MAX_NAME_LEN; ++i) {
118        EXPECT_EQ(name[i], 0);
119    }
120
121    END_TEST;
122}
123
124bool NullptrNameTest() {
125    BEGIN_TEST;
126
127    fbl::unique_ptr<fzl::MappedVmo> mapped_vmo;
128    zx_status_t status = fzl::MappedVmo::Create(page, nullptr, &mapped_vmo);
129    EXPECT_EQ(status, ZX_OK);
130
131    auto vmo = mapped_vmo->GetVmo();
132    char name[ZX_MAX_NAME_LEN] = {};
133    status = zx_object_get_property(vmo, ZX_PROP_NAME, name, ZX_MAX_NAME_LEN);
134    EXPECT_EQ(status, ZX_OK);
135    for (size_t i = 0; i < ZX_MAX_NAME_LEN; ++i) {
136        EXPECT_EQ(name[i], 0);
137    }
138
139    END_TEST;
140}
141
142bool LongNameTest() {
143    BEGIN_TEST;
144
145    char long_name[page];
146    memset(long_name, 'x', page);
147    long_name[page - 1] = 0;
148
149    fbl::unique_ptr<fzl::MappedVmo> mapped_vmo;
150    zx_status_t status = fzl::MappedVmo::Create(page, long_name, &mapped_vmo);
151    EXPECT_EQ(status, ZX_OK);
152
153    auto vmo = mapped_vmo->GetVmo();
154    char name[ZX_MAX_NAME_LEN] = {};
155    status = zx_object_get_property(vmo, ZX_PROP_NAME, name, ZX_MAX_NAME_LEN);
156    EXPECT_EQ(status, ZX_OK);
157    for (size_t i = 0; i < ZX_MAX_NAME_LEN - 1; ++i) {
158        EXPECT_EQ(name[i], 'x');
159    }
160    EXPECT_EQ(name[ZX_MAX_NAME_LEN - 1], 0);
161
162    END_TEST;
163}
164
165bool GoodSizesTest() {
166    BEGIN_TEST;
167
168    size_t sizes[] = {
169        page,
170        16 * page,
171        page * page,
172        page + 1,
173    };
174
175    for (size_t size : sizes) {
176        fbl::unique_ptr<fzl::MappedVmo> mapped_vmo;
177        zx_status_t status = fzl::MappedVmo::Create(size, vmo_name, &mapped_vmo);
178        EXPECT_EQ(status, ZX_OK);
179
180        auto data = static_cast<const uint8_t*>(mapped_vmo->GetData());
181        for (size_t i = 0; i < size; ++i) {
182            EXPECT_EQ(data[i], 0);
183        }
184    }
185
186    END_TEST;
187}
188
189bool BadSizesTest() {
190    BEGIN_TEST;
191
192    // Size 0 should fail.
193    fbl::unique_ptr<fzl::MappedVmo> mapped_vmo;
194    zx_status_t status = fzl::MappedVmo::Create(0, vmo_name, &mapped_vmo);
195    EXPECT_EQ(status, ZX_ERR_INVALID_ARGS);
196    EXPECT_NULL(mapped_vmo);
197
198    // So should an aburdly big request.
199    status = fzl::MappedVmo::Create(SIZE_MAX, vmo_name, &mapped_vmo);
200    EXPECT_EQ(status, ZX_ERR_OUT_OF_RANGE);
201    EXPECT_NULL(mapped_vmo);
202
203    END_TEST;
204}
205
206bool GoodShrinkTest() {
207    BEGIN_TEST;
208
209    size_t size = page * page;
210
211    fbl::unique_ptr<fzl::MappedVmo> mapped_vmo;
212    zx_status_t status = fzl::MappedVmo::Create(size, vmo_name, &mapped_vmo);
213    EXPECT_EQ(status, ZX_OK);
214
215    while (size > 2 * page) {
216        // The current size.
217        status = mapped_vmo->Shrink(mapped_vmo->GetSize());
218        EXPECT_EQ(status, ZX_OK);
219        EXPECT_EQ(mapped_vmo->GetSize(), size);
220
221        // A paged aligned size.
222        size >>= 1;
223        status = mapped_vmo->Shrink(size);
224        EXPECT_EQ(status, ZX_OK);
225        EXPECT_EQ(mapped_vmo->GetSize(), size);
226    }
227
228    // TODO: Test that shrinking the map causes subsequent memory
229    // accesses to fail.
230
231    END_TEST;
232}
233
234bool BadShrinkTest() {
235    BEGIN_TEST;
236
237    const size_t size = 16 * page;
238
239    fbl::unique_ptr<fzl::MappedVmo> mapped_vmo;
240    zx_status_t status = fzl::MappedVmo::Create(size, vmo_name, &mapped_vmo);
241    EXPECT_EQ(status, ZX_OK);
242
243    // Shrinking to 0 should fail.
244    status = mapped_vmo->Shrink(0);
245    EXPECT_EQ(status, ZX_ERR_INVALID_ARGS);
246    EXPECT_EQ(mapped_vmo->GetSize(), size);
247
248    // Growing via shrink should also fail.
249    status = mapped_vmo->Shrink(2 * mapped_vmo->GetSize());
250    EXPECT_EQ(status, ZX_ERR_INVALID_ARGS);
251    EXPECT_EQ(mapped_vmo->GetSize(), size);
252
253    // Growing to a misaligned size should also fail.
254    status = mapped_vmo->Shrink(page + 23);
255    EXPECT_EQ(status, ZX_ERR_INVALID_ARGS);
256    EXPECT_EQ(mapped_vmo->GetSize(), size);
257
258    END_TEST;
259}
260
261bool AlignedGoodGrowTest() {
262    BEGIN_TEST;
263
264    const size_t original_size = page;
265    const size_t grow_size = 2 * page;
266
267    fbl::unique_ptr<fzl::MappedVmo> mapped_vmo;
268    zx_status_t status = fzl::MappedVmo::Create(original_size, vmo_name, &mapped_vmo);
269    EXPECT_EQ(status, ZX_OK);
270
271    // Growing to the current size should always succeed.
272    status = mapped_vmo->Grow(mapped_vmo->GetSize());
273    EXPECT_EQ(status, ZX_OK);
274
275    status = mapped_vmo->Grow(grow_size);
276    if (status == ZX_OK) {
277        EXPECT_EQ(mapped_vmo->GetSize(), grow_size);
278        // Check the last byte.
279        auto data = static_cast<const uint8_t*>(mapped_vmo->GetData());
280        EXPECT_EQ(data[grow_size - 1], 0);
281    } else {
282        // We might just get unlucky and get a page adjacent to
283        // something and not be able to grow. If so, assert that the
284        // size did not change.
285        EXPECT_EQ(mapped_vmo->GetSize(), original_size);
286    }
287
288    END_TEST;
289}
290
291bool UnalignedGoodGrowTest() {
292    BEGIN_TEST;
293
294    const size_t original_size = page;
295    const size_t grow_size = 2 * page + 1;
296    const size_t rounded_grow_size = 3 * page;
297
298    fbl::unique_ptr<fzl::MappedVmo> mapped_vmo;
299    zx_status_t status = fzl::MappedVmo::Create(original_size, vmo_name, &mapped_vmo);
300    EXPECT_EQ(status, ZX_OK);
301
302    // Growing to the current size should always succeed.
303    status = mapped_vmo->Grow(mapped_vmo->GetSize());
304    EXPECT_EQ(status, ZX_OK);
305
306    status = mapped_vmo->Grow(grow_size);
307    if (status == ZX_OK) {
308        EXPECT_EQ(mapped_vmo->GetSize(), rounded_grow_size);
309        // Check the last byte.
310        auto data = static_cast<const uint8_t*>(mapped_vmo->GetData());
311        EXPECT_EQ(data[grow_size - 1], 0);
312    } else {
313        // We might just get unlucky and get a page adjacent to
314        // something and not be able to grow. If so, assert that the
315        // size did not change.
316        EXPECT_EQ(mapped_vmo->GetSize(), original_size);
317    }
318
319    END_TEST;
320}
321
322bool BadGrowTest() {
323    BEGIN_TEST;
324
325    const size_t original_size = 2 * page;
326    const size_t grow_size = page;
327
328    fbl::unique_ptr<fzl::MappedVmo> mapped_vmo;
329    zx_status_t status = fzl::MappedVmo::Create(original_size, vmo_name, &mapped_vmo);
330    EXPECT_EQ(status, ZX_OK);
331
332    // Growing from 2 pages to 1 should fail.
333    status = mapped_vmo->Grow(grow_size);
334    EXPECT_EQ(status, ZX_ERR_INVALID_ARGS);
335    EXPECT_EQ(mapped_vmo->GetSize(), original_size);
336
337    // Growing from 2 pages to nothing should also fail.
338    status = mapped_vmo->Grow(0);
339    EXPECT_EQ(status, ZX_ERR_INVALID_ARGS);
340    EXPECT_EQ(mapped_vmo->GetSize(), original_size);
341
342    END_TEST;
343}
344
345BEGIN_TEST_CASE(MappedVmoTest)
346RUN_TEST(CreateTest)
347RUN_TEST(ReadTest)
348RUN_TEST(WriteMappingTest)
349RUN_TEST(ReadMappingTest)
350RUN_TEST(EmptyNameTest)
351RUN_TEST(NullptrNameTest)
352RUN_TEST(LongNameTest)
353RUN_TEST(GoodSizesTest)
354RUN_TEST(BadSizesTest)
355RUN_TEST(GoodShrinkTest)
356RUN_TEST(BadShrinkTest)
357RUN_TEST(AlignedGoodGrowTest)
358RUN_TEST(UnalignedGoodGrowTest)
359RUN_TEST(BadGrowTest)
360END_TEST_CASE(MappedVmoTest)
361
362} // namespace
363