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