| 1 | /* |
| 2 | * qtest e820 fw_cfg test case |
| 3 | * |
| 4 | * Validate the "etc/e820" fw_cfg table that QEMU hands to the firmware. |
| 5 | * |
| 6 | * Copyright (c) 2026 Advanced Micro Devices, Inc. |
| 7 | * |
| 8 | * Authors: |
| 9 | * FangSheng Huang <FangSheng.Huang@amd.com> |
| 10 | * |
| 11 | * SPDX-License-Identifier: GPL-2.0-or-later |
| 12 | */ |
| 13 | |
| 14 | #include "qemu/osdep.h" |
| 15 | |
| 16 | #include "libqtest.h" |
| 17 | #include "libqos/fw_cfg.h" |
| 18 | #include "qemu/bswap.h" |
| 19 | #include "qemu/units.h" |
| 20 | |
| 21 | /* e820 entry layout and types (cf. hw/i386/e820_memory_layout.h) */ |
| 22 | #define E820_RAM 1 |
| 23 | #define E820_SOFT_RESERVED 0xefffffff |
| 24 | |
| 25 | struct e820_entry { |
| 26 | uint64_t address; |
| 27 | uint64_t length; |
| 28 | uint32_t type; |
| 29 | } QEMU_PACKED; |
| 30 | |
| 31 | #define E820_MAX_ENTRIES 128 |
| 32 | |
| 33 | /* |
| 34 | * Read and structurally validate "etc/e820": the file is a packed array |
| 35 | * of struct e820_entry, so its size must be a whole multiple of the entry |
| 36 | * size and every entry must have a non-zero length. Returns the entry |
| 37 | * count and fills @table. |
| 38 | */ |
| 39 | static size_t get_e820_table(QFWCFG *fw_cfg, struct e820_entry *table) |
| 40 | { |
| 41 | size_t filesize, n, i; |
| 42 | |
| 43 | filesize = qfw_cfg_get_file(fw_cfg, "etc/e820", table, |
| 44 | E820_MAX_ENTRIES * sizeof(*table)); |
| 45 | g_assert_cmpint(filesize, >, 0); |
| 46 | g_assert_cmpint(filesize % sizeof(struct e820_entry), ==, 0); |
| 47 | |
| 48 | n = filesize / sizeof(struct e820_entry); |
| 49 | g_assert_cmpint(n, <=, E820_MAX_ENTRIES); |
| 50 | |
| 51 | for (i = 0; i < n; i++) { |
| 52 | g_assert_cmpint(le64_to_cpu(table[i].length), >, 0); |
| 53 | } |
| 54 | |
| 55 | return n; |
| 56 | } |
| 57 | |
| 58 | static void test_e820_basic(void) |
| 59 | { |
| 60 | struct e820_entry table[E820_MAX_ENTRIES]; |
| 61 | QFWCFG *fw_cfg; |
| 62 | QTestState *s; |
| 63 | size_t n, i; |
| 64 | bool found_ram = false, found_soft_reserved = false; |
| 65 | |
| 66 | s = qtest_init("-machine q35 -m 256M"); |
| 67 | fw_cfg = pc_fw_cfg_init(s); |
| 68 | |
| 69 | n = get_e820_table(fw_cfg, table); |
| 70 | for (i = 0; i < n; i++) { |
| 71 | switch (le32_to_cpu(table[i].type)) { |
| 72 | case E820_RAM: |
| 73 | found_ram = true; |
| 74 | break; |
| 75 | case E820_SOFT_RESERVED: |
| 76 | found_soft_reserved = true; |
| 77 | break; |
| 78 | } |
| 79 | } |
| 80 | |
| 81 | /* baseline: RAM present, no SOFT_RESERVED range */ |
| 82 | g_assert_true(found_ram); |
| 83 | g_assert_false(found_soft_reserved); |
| 84 | |
| 85 | pc_fw_cfg_uninit(fw_cfg); |
| 86 | qtest_quit(s); |
| 87 | } |
| 88 | |
| 89 | static void test_e820_sp_mem(void) |
| 90 | { |
| 91 | struct e820_entry table[E820_MAX_ENTRIES]; |
| 92 | QFWCFG *fw_cfg; |
| 93 | QTestState *s; |
| 94 | size_t n, i; |
| 95 | int soft_reserved = 0; |
| 96 | uint64_t soft_reserved_len = 0; |
| 97 | |
| 98 | s = qtest_init("-machine q35 -m 256M,slots=2,maxmem=2G " |
| 99 | "-object memory-backend-ram,id=ram0,size=256M " |
| 100 | "-numa node,nodeid=0,memdev=ram0 " |
| 101 | "-object memory-backend-ram,id=spm0,size=128M " |
| 102 | "-device sp-mem,id=sp0,memdev=spm0,node=0"); |
| 103 | fw_cfg = pc_fw_cfg_init(s); |
| 104 | |
| 105 | n = get_e820_table(fw_cfg, table); |
| 106 | for (i = 0; i < n; i++) { |
| 107 | if (le32_to_cpu(table[i].type) == E820_SOFT_RESERVED) { |
| 108 | soft_reserved++; |
| 109 | soft_reserved_len = le64_to_cpu(table[i].length); |
| 110 | } |
| 111 | } |
| 112 | |
| 113 | /* exactly one SOFT_RESERVED range, sized to the backend */ |
| 114 | g_assert_cmpint(soft_reserved, ==, 1); |
| 115 | g_assert_cmpint(soft_reserved_len, ==, 128 * MiB); |
| 116 | |
| 117 | pc_fw_cfg_uninit(fw_cfg); |
| 118 | qtest_quit(s); |
| 119 | } |
| 120 | |
| 121 | int main(int argc, char **argv) |
| 122 | { |
| 123 | g_test_init(&argc, &argv, NULL); |
| 124 | |
| 125 | qtest_add_func("e820/basic", test_e820_basic); |
| 126 | qtest_add_func("e820/sp-mem", test_e820_sp_mem); |
| 127 | |
| 128 | return g_test_run(); |
| 129 | } |