1// Copyright 2017 The Fuchsia Authors 2// 3// Use of this source code is governed by a MIT-style 4// license that can be found in the LICENSE file or at 5// https://opensource.org/licenses/MIT 6 7#include <err.h> 8#include <fbl/algorithm.h> 9#include <lib/memory_limit.h> 10#include <lib/unittest/unittest.h> 11#include <sys/types.h> 12 13typedef struct { 14 uintptr_t base; 15 size_t size; 16} test_range_t; 17 18// Memory map read off a NUC 19static const test_range_t nuc_ranges[] = { 20 {0, 0x58000}, 21 {0x59000, 0x45000}, 22 {0x100000, 0x85d8b000}, 23 {0x85eb6000, 0x4375000}, 24 {0x8b2ff000, 0x1000}, 25 {0x100000000, 0x36f000000}, 26}; 27 28static const mem_limit_ctx_t nuc_ctx = { 29 .kernel_base = 0x100000, 30 .kernel_size = 4 * MB, 31 .ramdisk_base = 0x818e4000, 32 .ramdisk_size = 4 * MB, 33 .memory_limit = 0, 34 .found_kernel = 0, 35 .found_ramdisk = 0, 36}; 37 38// Memory map read off an Acer12 Switch 39static const test_range_t switch_alpha_12_ranges[] = { 40 {0, 0x58000}, 41 {0x59000, 0x2d000}, 42 {0x100000, 0x7359d000}, 43 {0x736c8000, 0xb1000}, 44 {0x74079000, 0x16215000}, 45 {0x8aefe000, 0x1000}, 46 {0x100000000, 0x6f000000}, 47}; 48 49static const mem_limit_ctx_t switch_alpha_12_ctx = { 50 .kernel_base = 0x100000, 51 .kernel_size = 4u * MB, 52 .ramdisk_base = 0x71b20000, 53 .ramdisk_size = 4u * MB, 54 .memory_limit = 0, 55 .found_kernel = 0, 56 .found_ramdisk = 0, 57}; 58 59// rpi3 has a single contiguous 512MB block of memory 60static const test_range_t rpi3_ranges[] = { 61 {0xffff000000000000, 0x20000000}, 62}; 63 64static const mem_limit_ctx_t rpi3_ctx = { 65 .kernel_base = 0xffff000000000000, 66 .kernel_size = 4 * MB, 67 .ramdisk_base = 0xffff000007d44000, 68 .ramdisk_size = 3u * MB, 69 .memory_limit = 0, 70 .found_kernel = 0, 71 .found_ramdisk = 0, 72}; 73 74typedef struct { 75 mem_limit_ctx_t ctx; 76 const test_range_t* ranges; 77 size_t range_cnt; 78} platform_test_case_t; 79 80// Run a test against a given platform's configuration using a provided memory 81// limit. Tests are generated via macros below. 82static bool test_runner(const platform_test_case_t test, size_t mem_limit) { 83 BEGIN_TEST; 84 auto ctx = test.ctx; 85 size_t size = 0; 86 iovec_t vecs[2]; 87 size_t total_platform_size = 0; 88 89 90 ctx.memory_limit = mem_limit; 91 ctx.found_kernel = 0; 92 ctx.found_ramdisk = 0; 93 94 for (size_t i = 0; i < test.range_cnt; i++) { 95 size_t used; 96 total_platform_size += test.ranges[i].size; 97 zx_status_t status = mem_limit_get_iovs(&ctx, test.ranges[i].base, test.ranges[i].size, vecs, &used); 98 99 ASSERT_EQ(ZX_OK, status, "checking mem_limit_get_iovs status"); 100 101 for (size_t j = 0; j < used; j++) { 102 size += vecs[j].iov_len; 103 } 104 } 105 106 EXPECT_TRUE(ctx.found_kernel, "checking the kernel was found"); 107 EXPECT_TRUE(ctx.found_ramdisk, "checking the ramdisk was found"); 108 if (mem_limit > total_platform_size) { 109 EXPECT_LE(size, total_platform_size, "limit > platform size, so size should equal the platform size"); 110 } else { 111 EXPECT_EQ(mem_limit, size, "check that size equals the limit"); 112 } 113 EXPECT_LE(size, total_platform_size, "check the size is smaller than the total range"); 114 115 END_TEST; 116} 117 118// TODO: These tests ensure that we segment things up and find the kernel and 119// ramdisk, but they don't really cover any interesting cases. They will be 120// more useful if some specific cases are written, but those should be easily 121// addable as we find new problems down the line. 122const platform_test_case_t test_cases[] = { 123 {nuc_ctx, nuc_ranges, fbl::count_of(nuc_ranges)}, 124 {switch_alpha_12_ctx, switch_alpha_12_ranges, fbl::count_of(switch_alpha_12_ranges)}, 125 {rpi3_ctx, rpi3_ranges, fbl::count_of(rpi3_ranges)}, 126}; 127 128// Test that the memory limit is expanded if the ramdisk would otherwise be 129// truncated for being too large. 130static bool ml_test_large_ramdisk() { 131 BEGIN_TEST; 132 mem_limit_ctx_t ctx = nuc_ctx; 133 size_t memory_limit = 64 * MB; 134 size_t size = 0; 135 size_t used; 136 iovec_t vecs[2]; 137 138 ctx.ramdisk_size = 64 * MB; 139 ctx.memory_limit = memory_limit; 140 for (size_t i = 0; i < fbl::count_of(nuc_ranges); i++) { 141 zx_status_t status = mem_limit_get_iovs(&ctx, nuc_ranges[i].base, nuc_ranges[i].size, vecs, &used); 142 EXPECT_EQ(ZX_OK, status, "checking status"); 143 for (size_t i = 0; i < used; i++) { 144 size += vecs[i].iov_len; 145 } 146 } 147 148 EXPECT_EQ(true, ctx.found_kernel, "checking kernel"); 149 EXPECT_EQ(true, ctx.found_ramdisk, "checking ramdisk"); 150 EXPECT_NE(memory_limit, size, "checking that size and limit don't match"); 151 EXPECT_EQ(ctx.kernel_size + ctx.ramdisk_size, size, "checking the limit grew to fit kernel + ramdisk"); 152 END_TEST; 153} 154 155// Generate tests against the test runner so that we can test all tests pass or fail against 156// a full range of configuration setups. 157#define ML_TEST_NAME(platform_name, limit) ml_test_##platform_name##_##limit 158#define ML_TEST_GEN(platform_name, test_case, limit) \ 159 static bool ML_TEST_NAME(platform_name, limit)() { \ 160 BEGIN_TEST; \ 161 return test_runner(test_case, limit * MB); \ 162 END_TEST; \ 163 } 164 165// Test a range of platforms and memory configurations 166ML_TEST_GEN(nuc, test_cases[0], 32); 167ML_TEST_GEN(nuc, test_cases[0], 64); 168ML_TEST_GEN(nuc, test_cases[0], 96); 169ML_TEST_GEN(nuc, test_cases[0], 128); 170ML_TEST_GEN(nuc, test_cases[0], 256); 171ML_TEST_GEN(nuc, test_cases[0], 512); 172ML_TEST_GEN(nuc, test_cases[0], 1024); 173ML_TEST_GEN(nuc, test_cases[0], 1536); 174ML_TEST_GEN(nuc, test_cases[0], 2048); 175ML_TEST_GEN(nuc, test_cases[0], 3072); 176ML_TEST_GEN(nuc, test_cases[0], 4096); 177ML_TEST_GEN(switch_alpha_12, test_cases[1], 32); 178ML_TEST_GEN(switch_alpha_12, test_cases[1], 64); 179ML_TEST_GEN(switch_alpha_12, test_cases[1], 96); 180ML_TEST_GEN(switch_alpha_12, test_cases[1], 128); 181ML_TEST_GEN(switch_alpha_12, test_cases[1], 256); 182ML_TEST_GEN(switch_alpha_12, test_cases[1], 512); 183ML_TEST_GEN(switch_alpha_12, test_cases[1], 1024); 184ML_TEST_GEN(switch_alpha_12, test_cases[1], 1536); 185ML_TEST_GEN(switch_alpha_12, test_cases[1], 2048); 186ML_TEST_GEN(switch_alpha_12, test_cases[1], 3072); 187ML_TEST_GEN(switch_alpha_12, test_cases[1], 4096); 188ML_TEST_GEN(rpi3, test_cases[2], 32); 189ML_TEST_GEN(rpi3, test_cases[2], 64); 190ML_TEST_GEN(rpi3, test_cases[2], 96); 191ML_TEST_GEN(rpi3, test_cases[2], 128); 192ML_TEST_GEN(rpi3, test_cases[2], 256); 193ML_TEST_GEN(rpi3, test_cases[2], 512); 194ML_TEST_GEN(rpi3, test_cases[2], 1024); 195ML_TEST_GEN(rpi3, test_cases[2], 1536); 196ML_TEST_GEN(rpi3, test_cases[2], 2048); 197ML_TEST_GEN(rpi3, test_cases[2], 3072); 198ML_TEST_GEN(rpi3, test_cases[2], 4096); 199 200#define ML_UNITTEST(platform_name, limit) \ 201 UNITTEST(#platform_name " " #limit "MB", ml_test_##platform_name##_##limit) 202UNITTEST_START_TESTCASE(memlimit_tests) 203UNITTEST("Test with an oversized ramdisk", ml_test_large_ramdisk) 204ML_UNITTEST(nuc, 32) 205ML_UNITTEST(nuc, 64) 206ML_UNITTEST(nuc, 96) 207ML_UNITTEST(nuc, 128) 208ML_UNITTEST(nuc, 256) 209ML_UNITTEST(nuc, 512) 210ML_UNITTEST(nuc, 1024) 211ML_UNITTEST(nuc, 1536) 212ML_UNITTEST(nuc, 2048) 213ML_UNITTEST(nuc, 3072) 214ML_UNITTEST(nuc, 4096) 215ML_UNITTEST(switch_alpha_12, 32) 216ML_UNITTEST(switch_alpha_12, 64) 217ML_UNITTEST(switch_alpha_12, 96) 218ML_UNITTEST(switch_alpha_12, 128) 219ML_UNITTEST(switch_alpha_12, 256) 220ML_UNITTEST(switch_alpha_12, 512) 221ML_UNITTEST(switch_alpha_12, 1024) 222ML_UNITTEST(switch_alpha_12, 1536) 223ML_UNITTEST(switch_alpha_12, 2048) 224ML_UNITTEST(switch_alpha_12, 3072) 225ML_UNITTEST(switch_alpha_12, 4096) 226ML_UNITTEST(rpi3, 32) 227ML_UNITTEST(rpi3, 64) 228ML_UNITTEST(rpi3, 96) 229ML_UNITTEST(rpi3, 128) 230ML_UNITTEST(rpi3, 256) 231ML_UNITTEST(rpi3, 512) 232ML_UNITTEST(rpi3, 1024) 233ML_UNITTEST(rpi3, 1536) 234ML_UNITTEST(rpi3, 2048) 235ML_UNITTEST(rpi3, 3072) 236ML_UNITTEST(rpi3, 4096) 237UNITTEST_END_TESTCASE(memlimit_tests, "memlim_tests", "Memory limit tests"); 238