| 1 | /* |
| 2 | * QTest testcase for Q35 northbridge |
| 3 | * |
| 4 | * Copyright (c) 2015 Red Hat, Inc. |
| 5 | * |
| 6 | * Author: Gerd Hoffmann <kraxel@redhat.com> |
| 7 | * |
| 8 | * This work is licensed under the terms of the GNU GPL, version 2 or later. |
| 9 | * See the COPYING file in the top-level directory. |
| 10 | */ |
| 11 | |
| 12 | #include "qemu/osdep.h" |
| 13 | #include "libqtest.h" |
| 14 | #include "libqos/pci.h" |
| 15 | #include "libqos/pci-pc.h" |
| 16 | #include "hw/pci-host/q35.h" |
| 17 | #include "qobject/qdict.h" |
| 18 | |
| 19 | #define TSEG_SIZE_TEST_GUEST_RAM_MBYTES 128 |
| 20 | |
| 21 | /* @esmramc_tseg_sz: ESMRAMC.TSEG_SZ bitmask for selecting the requested TSEG |
| 22 | * size. Must be a subset of |
| 23 | * MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_MASK. |
| 24 | * |
| 25 | * @extended_tseg_mbytes: Size of the extended TSEG. Only consulted if |
| 26 | * @esmramc_tseg_sz equals |
| 27 | * MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_MASK precisely. |
| 28 | * |
| 29 | * @expected_tseg_mbytes: Expected guest-visible TSEG size in megabytes, |
| 30 | * matching @esmramc_tseg_sz and @extended_tseg_mbytes |
| 31 | * above. |
| 32 | */ |
| 33 | struct TsegSizeArgs { |
| 34 | uint8_t esmramc_tseg_sz; |
| 35 | uint16_t extended_tseg_mbytes; |
| 36 | uint16_t expected_tseg_mbytes; |
| 37 | }; |
| 38 | typedef struct TsegSizeArgs TsegSizeArgs; |
| 39 | |
| 40 | static const TsegSizeArgs tseg_1mb = { |
| 41 | .esmramc_tseg_sz = MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_1MB, |
| 42 | .extended_tseg_mbytes = 0, |
| 43 | .expected_tseg_mbytes = 1, |
| 44 | }; |
| 45 | static const TsegSizeArgs tseg_2mb = { |
| 46 | .esmramc_tseg_sz = MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_2MB, |
| 47 | .extended_tseg_mbytes = 0, |
| 48 | .expected_tseg_mbytes = 2, |
| 49 | }; |
| 50 | static const TsegSizeArgs tseg_8mb = { |
| 51 | .esmramc_tseg_sz = MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_8MB, |
| 52 | .extended_tseg_mbytes = 0, |
| 53 | .expected_tseg_mbytes = 8, |
| 54 | }; |
| 55 | static const TsegSizeArgs tseg_ext_16mb = { |
| 56 | .esmramc_tseg_sz = MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_MASK, |
| 57 | .extended_tseg_mbytes = 16, |
| 58 | .expected_tseg_mbytes = 16, |
| 59 | }; |
| 60 | |
| 61 | static void smram_set_bit(QPCIDevice *pcidev, uint8_t mask, bool enabled) |
| 62 | { |
| 63 | uint8_t smram; |
| 64 | |
| 65 | smram = qpci_config_readb(pcidev, MCH_HOST_BRIDGE_SMRAM); |
| 66 | if (enabled) { |
| 67 | smram |= mask; |
| 68 | } else { |
| 69 | smram &= ~mask; |
| 70 | } |
| 71 | qpci_config_writeb(pcidev, MCH_HOST_BRIDGE_SMRAM, smram); |
| 72 | } |
| 73 | |
| 74 | static bool smram_test_bit(QPCIDevice *pcidev, uint8_t mask) |
| 75 | { |
| 76 | uint8_t smram; |
| 77 | |
| 78 | smram = qpci_config_readb(pcidev, MCH_HOST_BRIDGE_SMRAM); |
| 79 | return smram & mask; |
| 80 | } |
| 81 | |
| 82 | static void test_smram_lock(void) |
| 83 | { |
| 84 | QPCIBus *pcibus; |
| 85 | QPCIDevice *pcidev; |
| 86 | QTestState *qts; |
| 87 | |
| 88 | qts = qtest_init("-M q35"); |
| 89 | |
| 90 | pcibus = qpci_new_pc(qts, NULL); |
| 91 | g_assert(pcibus != NULL); |
| 92 | |
| 93 | pcidev = qpci_device_find(pcibus, 0); |
| 94 | g_assert(pcidev != NULL); |
| 95 | |
| 96 | /* check open is settable */ |
| 97 | smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, false); |
| 98 | g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == false); |
| 99 | smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, true); |
| 100 | g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == true); |
| 101 | |
| 102 | /* lock, check open is cleared & not settable */ |
| 103 | smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_LCK, true); |
| 104 | g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == false); |
| 105 | smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, true); |
| 106 | g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == false); |
| 107 | |
| 108 | /* reset */ |
| 109 | qtest_system_reset(qts); |
| 110 | |
| 111 | /* check open is settable again */ |
| 112 | smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, false); |
| 113 | g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == false); |
| 114 | smram_set_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN, true); |
| 115 | g_assert(smram_test_bit(pcidev, MCH_HOST_BRIDGE_SMRAM_D_OPEN) == true); |
| 116 | |
| 117 | g_free(pcidev); |
| 118 | qpci_free_pc(pcibus); |
| 119 | |
| 120 | qtest_quit(qts); |
| 121 | } |
| 122 | |
| 123 | static void test_tseg_size(const void *data) |
| 124 | { |
| 125 | const TsegSizeArgs *args = data; |
| 126 | QPCIBus *pcibus; |
| 127 | QPCIDevice *pcidev; |
| 128 | uint8_t smram_val; |
| 129 | uint8_t esmramc_val; |
| 130 | uint32_t ram_offs; |
| 131 | QTestState *qts; |
| 132 | |
| 133 | if (args->esmramc_tseg_sz == MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_MASK) { |
| 134 | qts = qtest_initf("-M q35 -m %uM -global mch.extended-tseg-mbytes=%u", |
| 135 | TSEG_SIZE_TEST_GUEST_RAM_MBYTES, |
| 136 | args->extended_tseg_mbytes); |
| 137 | } else { |
| 138 | qts = qtest_initf("-M q35 -m %uM", TSEG_SIZE_TEST_GUEST_RAM_MBYTES); |
| 139 | } |
| 140 | |
| 141 | /* locate the DRAM controller */ |
| 142 | pcibus = qpci_new_pc(qts, NULL); |
| 143 | g_assert(pcibus != NULL); |
| 144 | pcidev = qpci_device_find(pcibus, 0); |
| 145 | g_assert(pcidev != NULL); |
| 146 | |
| 147 | /* Set TSEG size. Restrict TSEG visibility to SMM by setting T_EN. */ |
| 148 | esmramc_val = qpci_config_readb(pcidev, MCH_HOST_BRIDGE_ESMRAMC); |
| 149 | esmramc_val &= ~MCH_HOST_BRIDGE_ESMRAMC_TSEG_SZ_MASK; |
| 150 | esmramc_val |= args->esmramc_tseg_sz; |
| 151 | esmramc_val |= MCH_HOST_BRIDGE_ESMRAMC_T_EN; |
| 152 | qpci_config_writeb(pcidev, MCH_HOST_BRIDGE_ESMRAMC, esmramc_val); |
| 153 | |
| 154 | /* Enable TSEG by setting G_SMRAME. Close TSEG by setting D_CLS. */ |
| 155 | smram_val = qpci_config_readb(pcidev, MCH_HOST_BRIDGE_SMRAM); |
| 156 | smram_val &= ~(MCH_HOST_BRIDGE_SMRAM_D_OPEN | |
| 157 | MCH_HOST_BRIDGE_SMRAM_D_LCK); |
| 158 | smram_val |= (MCH_HOST_BRIDGE_SMRAM_D_CLS | |
| 159 | MCH_HOST_BRIDGE_SMRAM_G_SMRAME); |
| 160 | qpci_config_writeb(pcidev, MCH_HOST_BRIDGE_SMRAM, smram_val); |
| 161 | |
| 162 | /* lock TSEG */ |
| 163 | smram_val |= MCH_HOST_BRIDGE_SMRAM_D_LCK; |
| 164 | qpci_config_writeb(pcidev, MCH_HOST_BRIDGE_SMRAM, smram_val); |
| 165 | |
| 166 | /* Now check that the byte right before the TSEG is r/w, and that the first |
| 167 | * byte in the TSEG always reads as 0xff. |
| 168 | */ |
| 169 | ram_offs = (TSEG_SIZE_TEST_GUEST_RAM_MBYTES - args->expected_tseg_mbytes) * |
| 170 | 1024 * 1024 - 1; |
| 171 | g_assert_cmpint(qtest_readb(qts, ram_offs), ==, 0); |
| 172 | qtest_writeb(qts, ram_offs, 1); |
| 173 | g_assert_cmpint(qtest_readb(qts, ram_offs), ==, 1); |
| 174 | |
| 175 | ram_offs++; |
| 176 | g_assert_cmpint(qtest_readb(qts, ram_offs), ==, 0xff); |
| 177 | qtest_writeb(qts, ram_offs, 1); |
| 178 | g_assert_cmpint(qtest_readb(qts, ram_offs), ==, 0xff); |
| 179 | |
| 180 | g_free(pcidev); |
| 181 | qpci_free_pc(pcibus); |
| 182 | qtest_quit(qts); |
| 183 | } |
| 184 | |
| 185 | #define SMBASE 0x30000 |
| 186 | #define SMRAM_TEST_PATTERN 0x32 |
| 187 | #define SMRAM_TEST_RESET_PATTERN 0x23 |
| 188 | |
| 189 | static void test_smram_smbase_lock(void) |
| 190 | { |
| 191 | QPCIBus *pcibus; |
| 192 | QPCIDevice *pcidev; |
| 193 | QTestState *qts; |
| 194 | int i; |
| 195 | |
| 196 | qts = qtest_init("-M q35"); |
| 197 | |
| 198 | pcibus = qpci_new_pc(qts, NULL); |
| 199 | g_assert(pcibus != NULL); |
| 200 | |
| 201 | pcidev = qpci_device_find(pcibus, 0); |
| 202 | g_assert(pcidev != NULL); |
| 203 | |
| 204 | /* check that SMRAM is not enabled by default */ |
| 205 | g_assert(qpci_config_readb(pcidev, MCH_HOST_BRIDGE_F_SMBASE) == 0); |
| 206 | qtest_writeb(qts, SMBASE, SMRAM_TEST_PATTERN); |
| 207 | g_assert_cmpint(qtest_readb(qts, SMBASE), ==, SMRAM_TEST_PATTERN); |
| 208 | |
| 209 | /* enable SMRAM at SMBASE */ |
| 210 | qpci_config_writeb(pcidev, MCH_HOST_BRIDGE_F_SMBASE, 0xff); |
| 211 | g_assert(qpci_config_readb(pcidev, MCH_HOST_BRIDGE_F_SMBASE) == 0x01); |
| 212 | /* lock SMRAM at SMBASE */ |
| 213 | qpci_config_writeb(pcidev, MCH_HOST_BRIDGE_F_SMBASE, 0x02); |
| 214 | g_assert(qpci_config_readb(pcidev, MCH_HOST_BRIDGE_F_SMBASE) == 0x02); |
| 215 | |
| 216 | /* check that SMRAM at SMBASE is locked and can't be unlocked */ |
| 217 | g_assert_cmpint(qtest_readb(qts, SMBASE), ==, 0xff); |
| 218 | for (i = 0; i <= 0xff; i++) { |
| 219 | /* make sure register is immutable */ |
| 220 | qpci_config_writeb(pcidev, MCH_HOST_BRIDGE_F_SMBASE, i); |
| 221 | g_assert(qpci_config_readb(pcidev, MCH_HOST_BRIDGE_F_SMBASE) == 0x02); |
| 222 | |
| 223 | /* RAM access should go into black hole */ |
| 224 | qtest_writeb(qts, SMBASE, SMRAM_TEST_PATTERN); |
| 225 | g_assert_cmpint(qtest_readb(qts, SMBASE), ==, 0xff); |
| 226 | } |
| 227 | |
| 228 | /* reset */ |
| 229 | qtest_system_reset(qts); |
| 230 | |
| 231 | /* check RAM at SMBASE is available after reset */ |
| 232 | g_assert_cmpint(qtest_readb(qts, SMBASE), ==, SMRAM_TEST_PATTERN); |
| 233 | g_assert(qpci_config_readb(pcidev, MCH_HOST_BRIDGE_F_SMBASE) == 0); |
| 234 | qtest_writeb(qts, SMBASE, SMRAM_TEST_RESET_PATTERN); |
| 235 | g_assert_cmpint(qtest_readb(qts, SMBASE), ==, SMRAM_TEST_RESET_PATTERN); |
| 236 | |
| 237 | g_free(pcidev); |
| 238 | qpci_free_pc(pcibus); |
| 239 | |
| 240 | qtest_quit(qts); |
| 241 | } |
| 242 | |
| 243 | int main(int argc, char **argv) |
| 244 | { |
| 245 | g_test_init(&argc, &argv, NULL); |
| 246 | |
| 247 | qtest_add_func("/q35/smram/lock", test_smram_lock); |
| 248 | |
| 249 | qtest_add_data_func("/q35/tseg-size/1mb", &tseg_1mb, test_tseg_size); |
| 250 | qtest_add_data_func("/q35/tseg-size/2mb", &tseg_2mb, test_tseg_size); |
| 251 | qtest_add_data_func("/q35/tseg-size/8mb", &tseg_8mb, test_tseg_size); |
| 252 | qtest_add_data_func("/q35/tseg-size/ext/16mb", &tseg_ext_16mb, |
| 253 | test_tseg_size); |
| 254 | qtest_add_func("/q35/smram/smbase_lock", test_smram_smbase_lock); |
| 255 | |
| 256 | return g_test_run(); |
| 257 | } |