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