| 1 | /* |
| 2 | * QTest testcase for PowerNV 10 interrupt controller (xive2) |
| 3 | * - Test irq to hardware thread |
| 4 | * - Test 'Pull Thread Context to Odd Thread Reporting Line' |
| 5 | * - Test irq to hardware group |
| 6 | * - Test irq to hardware group going through backlog |
| 7 | * - Test irq to pool thread |
| 8 | * |
| 9 | * Copyright (c) 2024, IBM Corporation. |
| 10 | * |
| 11 | * SPDX-License-Identifier: GPL-2.0-or-later |
| 12 | */ |
| 13 | #include "qemu/osdep.h" |
| 14 | #include "libqtest.h" |
| 15 | |
| 16 | #include "pnv-xive2-common.h" |
| 17 | #include "pnv-xscom.h" |
| 18 | #include "hw/intc/pnv_xive2_regs.h" |
| 19 | #include "hw/ppc/xive_regs.h" |
| 20 | #include "hw/ppc/xive2_regs.h" |
| 21 | |
| 22 | #define SMT 4 /* some tests will break if less than 4 */ |
| 23 | |
| 24 | |
| 25 | static void set_table(QTestState *qts, uint64_t type, uint64_t addr) |
| 26 | { |
| 27 | uint64_t vsd, size, log_size; |
| 28 | |
| 29 | /* |
| 30 | * First, let's make sure that all the resources used fit in the |
| 31 | * given table. |
| 32 | */ |
| 33 | switch (type) { |
| 34 | case VST_ESB: |
| 35 | size = MAX_IRQS / 4; |
| 36 | break; |
| 37 | case VST_EAS: |
| 38 | size = MAX_IRQS * 8; |
| 39 | break; |
| 40 | case VST_END: |
| 41 | size = MAX_ENDS * 32; |
| 42 | break; |
| 43 | case VST_NVP: |
| 44 | case VST_NVG: |
| 45 | case VST_NVC: |
| 46 | size = MAX_VPS * 32; |
| 47 | break; |
| 48 | case VST_SYNC: |
| 49 | size = 64 * 1024; |
| 50 | break; |
| 51 | default: |
| 52 | g_assert_not_reached(); |
| 53 | } |
| 54 | |
| 55 | g_assert_cmpuint(size, <=, XIVE_VST_SIZE); |
| 56 | log_size = ctzl(XIVE_VST_SIZE) - 12; |
| 57 | |
| 58 | vsd = ((uint64_t) VSD_MODE_EXCLUSIVE) << 62 | addr | log_size; |
| 59 | pnv_xive_xscom_write(qts, X_VC_VSD_TABLE_ADDR, type << 48); |
| 60 | pnv_xive_xscom_write(qts, X_VC_VSD_TABLE_DATA, vsd); |
| 61 | |
| 62 | if (type != VST_EAS && type != VST_IC && type != VST_ERQ) { |
| 63 | pnv_xive_xscom_write(qts, X_PC_VSD_TABLE_ADDR, type << 48); |
| 64 | pnv_xive_xscom_write(qts, X_PC_VSD_TABLE_DATA, vsd); |
| 65 | } |
| 66 | } |
| 67 | |
| 68 | static void set_tima8(QTestState *qts, uint32_t pir, uint32_t offset, |
| 69 | uint8_t b) |
| 70 | { |
| 71 | uint64_t ic_addr; |
| 72 | |
| 73 | ic_addr = XIVE_IC_TM_INDIRECT + (pir << XIVE_PAGE_SHIFT); |
| 74 | qtest_writeb(qts, ic_addr + offset, b); |
| 75 | } |
| 76 | |
| 77 | static void set_tima32(QTestState *qts, uint32_t pir, uint32_t offset, |
| 78 | uint32_t l) |
| 79 | { |
| 80 | uint64_t ic_addr; |
| 81 | |
| 82 | ic_addr = XIVE_IC_TM_INDIRECT + (pir << XIVE_PAGE_SHIFT); |
| 83 | qtest_writel(qts, ic_addr + offset, l); |
| 84 | } |
| 85 | |
| 86 | static uint8_t get_tima8(QTestState *qts, uint32_t pir, uint32_t offset) |
| 87 | { |
| 88 | uint64_t ic_addr; |
| 89 | |
| 90 | ic_addr = XIVE_IC_TM_INDIRECT + (pir << XIVE_PAGE_SHIFT); |
| 91 | return qtest_readb(qts, ic_addr + offset); |
| 92 | } |
| 93 | |
| 94 | static uint16_t get_tima16(QTestState *qts, uint32_t pir, uint32_t offset) |
| 95 | { |
| 96 | uint64_t ic_addr; |
| 97 | |
| 98 | ic_addr = XIVE_IC_TM_INDIRECT + (pir << XIVE_PAGE_SHIFT); |
| 99 | return qtest_readw(qts, ic_addr + offset); |
| 100 | } |
| 101 | |
| 102 | static uint32_t get_tima32(QTestState *qts, uint32_t pir, uint32_t offset) |
| 103 | { |
| 104 | uint64_t ic_addr; |
| 105 | |
| 106 | ic_addr = XIVE_IC_TM_INDIRECT + (pir << XIVE_PAGE_SHIFT); |
| 107 | return qtest_readl(qts, ic_addr + offset); |
| 108 | } |
| 109 | |
| 110 | static void reset_pool_threads(QTestState *qts) |
| 111 | { |
| 112 | uint8_t first_group = 0; |
| 113 | int i; |
| 114 | |
| 115 | for (i = 0; i < SMT; i++) { |
| 116 | uint32_t nvp_idx = 0x100 + i; |
| 117 | set_nvp(qts, nvp_idx, first_group); |
| 118 | set_tima32(qts, i, TM_QW2_HV_POOL + TM_WORD0, 0x000000ff); |
| 119 | set_tima32(qts, i, TM_QW2_HV_POOL + TM_WORD1, 0); |
| 120 | set_tima32(qts, i, TM_QW2_HV_POOL + TM_WORD2, TM_QW2W2_VP | nvp_idx); |
| 121 | } |
| 122 | } |
| 123 | |
| 124 | static void reset_hw_threads(QTestState *qts) |
| 125 | { |
| 126 | uint8_t first_group = 0; |
| 127 | uint32_t w1 = 0x000000ff; |
| 128 | int i; |
| 129 | |
| 130 | if (SMT >= 4) { |
| 131 | /* define 2 groups of 2, part of a bigger group of size 4 */ |
| 132 | set_nvg(qts, 0x80, 0x02); |
| 133 | set_nvg(qts, 0x82, 0x02); |
| 134 | set_nvg(qts, 0x81, 0); |
| 135 | first_group = 0x01; |
| 136 | w1 = 0x000300ff; |
| 137 | } |
| 138 | |
| 139 | for (i = 0; i < SMT; i++) { |
| 140 | set_nvp(qts, 0x80 + i, first_group); |
| 141 | set_tima32(qts, i, TM_QW3_HV_PHYS + TM_WORD0, 0x00ff00ff); |
| 142 | set_tima32(qts, i, TM_QW3_HV_PHYS + TM_WORD1, w1); |
| 143 | set_tima32(qts, i, TM_QW3_HV_PHYS + TM_WORD2, 0x80000000); |
| 144 | } |
| 145 | } |
| 146 | |
| 147 | static void reset_state(QTestState *qts) |
| 148 | { |
| 149 | size_t mem_used = XIVE_MEM_END - XIVE_MEM_START; |
| 150 | |
| 151 | qtest_memset(qts, XIVE_MEM_START, 0, mem_used); |
| 152 | reset_hw_threads(qts); |
| 153 | reset_pool_threads(qts); |
| 154 | } |
| 155 | |
| 156 | static void init_xive(QTestState *qts) |
| 157 | { |
| 158 | uint64_t val1, val2, range; |
| 159 | |
| 160 | /* |
| 161 | * We can take a few shortcuts here, as we know the default values |
| 162 | * used for xive initialization |
| 163 | */ |
| 164 | |
| 165 | /* |
| 166 | * Set the BARs. |
| 167 | * We reuse the same values used by firmware to ease debug. |
| 168 | */ |
| 169 | pnv_xive_xscom_write(qts, X_CQ_IC_BAR, XIVE_IC_BAR); |
| 170 | pnv_xive_xscom_write(qts, X_CQ_TM_BAR, XIVE_TM_BAR); |
| 171 | |
| 172 | /* ESB and NVPG use 2 pages per resource. The others only one page */ |
| 173 | range = (MAX_IRQS << 17) >> 25; |
| 174 | val1 = XIVE_ESB_BAR | range; |
| 175 | pnv_xive_xscom_write(qts, X_CQ_ESB_BAR, val1); |
| 176 | |
| 177 | range = (MAX_ENDS << 16) >> 25; |
| 178 | val1 = XIVE_END_BAR | range; |
| 179 | pnv_xive_xscom_write(qts, X_CQ_END_BAR, val1); |
| 180 | |
| 181 | range = (MAX_VPS << 17) >> 25; |
| 182 | val1 = XIVE_NVPG_BAR | range; |
| 183 | pnv_xive_xscom_write(qts, X_CQ_NVPG_BAR, val1); |
| 184 | |
| 185 | range = (MAX_VPS << 16) >> 25; |
| 186 | val1 = XIVE_NVC_BAR | range; |
| 187 | pnv_xive_xscom_write(qts, X_CQ_NVC_BAR, val1); |
| 188 | |
| 189 | /* |
| 190 | * Enable hw threads. |
| 191 | * We check the value written. Useless with current |
| 192 | * implementation, but it validates the xscom read path and it's |
| 193 | * what the hardware procedure says |
| 194 | */ |
| 195 | val1 = 0xF000000000000000ull; /* core 0, 4 threads */ |
| 196 | pnv_xive_xscom_write(qts, X_TCTXT_EN0, val1); |
| 197 | val2 = pnv_xive_xscom_read(qts, X_TCTXT_EN0); |
| 198 | g_assert_cmphex(val1, ==, val2); |
| 199 | |
| 200 | /* Memory tables */ |
| 201 | set_table(qts, VST_ESB, XIVE_ESB_MEM); |
| 202 | set_table(qts, VST_EAS, XIVE_EAS_MEM); |
| 203 | set_table(qts, VST_END, XIVE_END_MEM); |
| 204 | set_table(qts, VST_NVP, XIVE_NVP_MEM); |
| 205 | set_table(qts, VST_NVG, XIVE_NVG_MEM); |
| 206 | set_table(qts, VST_NVC, XIVE_NVC_MEM); |
| 207 | set_table(qts, VST_SYNC, XIVE_SYNC_MEM); |
| 208 | |
| 209 | reset_hw_threads(qts); |
| 210 | reset_pool_threads(qts); |
| 211 | } |
| 212 | |
| 213 | static void test_hw_irq(QTestState *qts) |
| 214 | { |
| 215 | uint32_t irq = 2; |
| 216 | uint32_t irq_data = 0x600df00d; |
| 217 | uint32_t end_index = 5; |
| 218 | uint32_t target_pir = 1; |
| 219 | uint32_t target_nvp = 0x80 + target_pir; |
| 220 | uint8_t priority = 5; |
| 221 | uint32_t reg32; |
| 222 | uint16_t reg16; |
| 223 | uint8_t pq, nsr, cppr; |
| 224 | |
| 225 | g_test_message("========================================================="); |
| 226 | g_test_message("Testing irq %d to hardware thread %d", irq, target_pir); |
| 227 | |
| 228 | /* irq config */ |
| 229 | set_eas(qts, irq, end_index, irq_data); |
| 230 | set_end(qts, end_index, target_nvp, priority, false /* group */); |
| 231 | |
| 232 | /* enable and trigger irq */ |
| 233 | get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_SET_PQ_00); |
| 234 | set_esb(qts, irq, XIVE_TRIGGER_PAGE, 0, 0); |
| 235 | |
| 236 | /* check irq is raised on cpu */ |
| 237 | pq = get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_GET); |
| 238 | g_assert_cmpuint(pq, ==, XIVE_ESB_PENDING); |
| 239 | |
| 240 | reg32 = get_tima32(qts, target_pir, TM_QW3_HV_PHYS + TM_WORD0); |
| 241 | nsr = reg32 >> 24; |
| 242 | cppr = (reg32 >> 16) & 0xFF; |
| 243 | g_assert_cmphex(nsr, ==, 0x80); |
| 244 | g_assert_cmphex(cppr, ==, 0xFF); |
| 245 | |
| 246 | /* ack the irq */ |
| 247 | reg16 = get_tima16(qts, target_pir, TM_SPC_ACK_HV_REG); |
| 248 | nsr = reg16 >> 8; |
| 249 | cppr = reg16 & 0xFF; |
| 250 | g_assert_cmphex(nsr, ==, 0x80); |
| 251 | g_assert_cmphex(cppr, ==, priority); |
| 252 | |
| 253 | /* check irq data is what was configured */ |
| 254 | reg32 = qtest_readl(qts, xive_get_queue_addr(end_index)); |
| 255 | g_assert_cmphex((reg32 & 0x7fffffff), ==, (irq_data & 0x7fffffff)); |
| 256 | |
| 257 | /* End Of Interrupt */ |
| 258 | set_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_STORE_EOI, 0); |
| 259 | pq = get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_GET); |
| 260 | g_assert_cmpuint(pq, ==, XIVE_ESB_RESET); |
| 261 | |
| 262 | /* reset CPPR */ |
| 263 | set_tima8(qts, target_pir, TM_QW3_HV_PHYS + TM_CPPR, 0xFF); |
| 264 | reg32 = get_tima32(qts, target_pir, TM_QW3_HV_PHYS + TM_WORD0); |
| 265 | nsr = reg32 >> 24; |
| 266 | cppr = (reg32 >> 16) & 0xFF; |
| 267 | g_assert_cmphex(nsr, ==, 0x00); |
| 268 | g_assert_cmphex(cppr, ==, 0xFF); |
| 269 | } |
| 270 | |
| 271 | static void test_pool_irq(QTestState *qts) |
| 272 | { |
| 273 | uint32_t irq = 2; |
| 274 | uint32_t irq_data = 0x600d0d06; |
| 275 | uint32_t end_index = 5; |
| 276 | uint32_t target_pir = 1; |
| 277 | uint32_t target_nvp = 0x100 + target_pir; |
| 278 | uint8_t priority = 5; |
| 279 | uint32_t reg32; |
| 280 | uint16_t reg16; |
| 281 | uint8_t pq, nsr, cppr, ipb; |
| 282 | |
| 283 | g_test_message("========================================================="); |
| 284 | g_test_message("Testing irq %d to pool thread %d", irq, target_pir); |
| 285 | |
| 286 | /* irq config */ |
| 287 | set_eas(qts, irq, end_index, irq_data); |
| 288 | set_end(qts, end_index, target_nvp, priority, false /* group */); |
| 289 | |
| 290 | /* enable and trigger irq */ |
| 291 | get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_SET_PQ_00); |
| 292 | set_esb(qts, irq, XIVE_TRIGGER_PAGE, 0, 0); |
| 293 | |
| 294 | /* check irq is raised on cpu */ |
| 295 | pq = get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_GET); |
| 296 | g_assert_cmpuint(pq, ==, XIVE_ESB_PENDING); |
| 297 | |
| 298 | /* check TIMA values in the PHYS ring (shared by POOL ring) */ |
| 299 | reg32 = get_tima32(qts, target_pir, TM_QW3_HV_PHYS + TM_WORD0); |
| 300 | nsr = reg32 >> 24; |
| 301 | cppr = (reg32 >> 16) & 0xFF; |
| 302 | g_assert_cmphex(nsr, ==, 0x40); |
| 303 | g_assert_cmphex(cppr, ==, 0xFF); |
| 304 | |
| 305 | /* check TIMA values in the POOL ring */ |
| 306 | reg32 = get_tima32(qts, target_pir, TM_QW2_HV_POOL + TM_WORD0); |
| 307 | nsr = reg32 >> 24; |
| 308 | cppr = (reg32 >> 16) & 0xFF; |
| 309 | ipb = (reg32 >> 8) & 0xFF; |
| 310 | g_assert_cmphex(nsr, ==, 0); |
| 311 | g_assert_cmphex(cppr, ==, 0); |
| 312 | g_assert_cmphex(ipb, ==, 0x80 >> priority); |
| 313 | |
| 314 | /* ack the irq */ |
| 315 | reg16 = get_tima16(qts, target_pir, TM_SPC_ACK_HV_REG); |
| 316 | nsr = reg16 >> 8; |
| 317 | cppr = reg16 & 0xFF; |
| 318 | g_assert_cmphex(nsr, ==, 0x40); |
| 319 | g_assert_cmphex(cppr, ==, priority); |
| 320 | |
| 321 | /* check irq data is what was configured */ |
| 322 | reg32 = qtest_readl(qts, xive_get_queue_addr(end_index)); |
| 323 | g_assert_cmphex((reg32 & 0x7fffffff), ==, (irq_data & 0x7fffffff)); |
| 324 | |
| 325 | /* check IPB is cleared in the POOL ring */ |
| 326 | reg32 = get_tima32(qts, target_pir, TM_QW2_HV_POOL + TM_WORD0); |
| 327 | ipb = (reg32 >> 8) & 0xFF; |
| 328 | g_assert_cmphex(ipb, ==, 0); |
| 329 | |
| 330 | /* End Of Interrupt */ |
| 331 | set_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_STORE_EOI, 0); |
| 332 | pq = get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_GET); |
| 333 | g_assert_cmpuint(pq, ==, XIVE_ESB_RESET); |
| 334 | |
| 335 | /* reset CPPR */ |
| 336 | set_tima8(qts, target_pir, TM_QW3_HV_PHYS + TM_CPPR, 0xFF); |
| 337 | reg32 = get_tima32(qts, target_pir, TM_QW3_HV_PHYS + TM_WORD0); |
| 338 | nsr = reg32 >> 24; |
| 339 | cppr = (reg32 >> 16) & 0xFF; |
| 340 | g_assert_cmphex(nsr, ==, 0x00); |
| 341 | g_assert_cmphex(cppr, ==, 0xFF); |
| 342 | } |
| 343 | |
| 344 | #define XIVE_ODD_CL 0x80 |
| 345 | static void test_pull_thread_ctx_to_odd_thread_cl(QTestState *qts) |
| 346 | { |
| 347 | uint32_t target_pir = 1; |
| 348 | uint32_t target_nvp = 0x80 + target_pir; |
| 349 | Xive2Nvp nvp; |
| 350 | uint8_t cl_pair[XIVE_REPORT_SIZE]; |
| 351 | uint32_t qw1w0, qw3w0, qw1w2, qw2w2; |
| 352 | uint8_t qw3b8; |
| 353 | uint32_t cl_word; |
| 354 | uint32_t word2; |
| 355 | |
| 356 | g_test_message("========================================================="); |
| 357 | g_test_message("Testing 'Pull Thread Context to Odd Thread Reporting " \ |
| 358 | "Line'"); |
| 359 | |
| 360 | /* clear odd cache line prior to pull operation */ |
| 361 | memset(cl_pair, 0, sizeof(cl_pair)); |
| 362 | get_nvp(qts, target_nvp, &nvp); |
| 363 | set_cl_pair(qts, &nvp, cl_pair); |
| 364 | |
| 365 | /* Read some values from TIMA that we expect to see in cacheline */ |
| 366 | qw1w0 = get_tima32(qts, target_pir, TM_QW1_OS + TM_WORD0); |
| 367 | qw3w0 = get_tima32(qts, target_pir, TM_QW3_HV_PHYS + TM_WORD0); |
| 368 | qw1w2 = get_tima32(qts, target_pir, TM_QW1_OS + TM_WORD2); |
| 369 | qw2w2 = get_tima32(qts, target_pir, TM_QW2_HV_POOL + TM_WORD2); |
| 370 | qw3b8 = get_tima8(qts, target_pir, TM_QW3_HV_PHYS + TM_WORD2); |
| 371 | |
| 372 | /* Execute the pull operation */ |
| 373 | set_tima8(qts, target_pir, TM_SPC_PULL_PHYS_CTX_OL, 0); |
| 374 | |
| 375 | /* Verify odd cache line values match TIMA after pull operation */ |
| 376 | get_cl_pair(qts, &nvp, cl_pair); |
| 377 | memcpy(&cl_word, &cl_pair[XIVE_ODD_CL + TM_QW1_OS + TM_WORD0], 4); |
| 378 | g_assert_cmphex(qw1w0, ==, be32_to_cpu(cl_word)); |
| 379 | memcpy(&cl_word, &cl_pair[XIVE_ODD_CL + TM_QW3_HV_PHYS + TM_WORD0], 4); |
| 380 | g_assert_cmphex(qw3w0, ==, be32_to_cpu(cl_word)); |
| 381 | memcpy(&cl_word, &cl_pair[XIVE_ODD_CL + TM_QW1_OS + TM_WORD2], 4); |
| 382 | g_assert_cmphex(qw1w2, ==, be32_to_cpu(cl_word)); |
| 383 | memcpy(&cl_word, &cl_pair[XIVE_ODD_CL + TM_QW2_HV_POOL + TM_WORD2], 4); |
| 384 | g_assert_cmphex(qw2w2, ==, be32_to_cpu(cl_word)); |
| 385 | g_assert_cmphex(qw3b8, ==, |
| 386 | cl_pair[XIVE_ODD_CL + TM_QW3_HV_PHYS + TM_WORD2]); |
| 387 | |
| 388 | /* Verify that all TIMA valid bits for target thread are cleared */ |
| 389 | word2 = get_tima32(qts, target_pir, TM_QW1_OS + TM_WORD2); |
| 390 | g_assert_cmphex(xive_get_field32(TM_QW1W2_VO, word2), ==, 0); |
| 391 | word2 = get_tima32(qts, target_pir, TM_QW2_HV_POOL + TM_WORD2); |
| 392 | g_assert_cmphex(xive_get_field32(TM_QW2W2_VP, word2), ==, 0); |
| 393 | word2 = get_tima32(qts, target_pir, TM_QW3_HV_PHYS + TM_WORD2); |
| 394 | g_assert_cmphex(xive_get_field32(TM_QW3W2_VT, word2), ==, 0); |
| 395 | } |
| 396 | |
| 397 | static void test_hw_group_irq(QTestState *qts) |
| 398 | { |
| 399 | uint32_t irq = 100; |
| 400 | uint32_t irq_data = 0xdeadbeef; |
| 401 | uint32_t end_index = 23; |
| 402 | uint32_t chosen_one; |
| 403 | uint32_t target_nvp = 0x81; /* group size = 4 */ |
| 404 | uint8_t priority = 6; |
| 405 | uint32_t reg32; |
| 406 | uint16_t reg16; |
| 407 | uint8_t pq, nsr, cppr; |
| 408 | |
| 409 | g_test_message("========================================================="); |
| 410 | g_test_message("Testing irq %d to hardware group of size 4", irq); |
| 411 | |
| 412 | /* irq config */ |
| 413 | set_eas(qts, irq, end_index, irq_data); |
| 414 | set_end(qts, end_index, target_nvp, priority, true /* group */); |
| 415 | |
| 416 | /* enable and trigger irq */ |
| 417 | get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_SET_PQ_00); |
| 418 | set_esb(qts, irq, XIVE_TRIGGER_PAGE, 0, 0); |
| 419 | |
| 420 | /* check irq is raised on cpu */ |
| 421 | pq = get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_GET); |
| 422 | g_assert_cmpuint(pq, ==, XIVE_ESB_PENDING); |
| 423 | |
| 424 | /* find the targeted vCPU */ |
| 425 | for (chosen_one = 0; chosen_one < SMT; chosen_one++) { |
| 426 | reg32 = get_tima32(qts, chosen_one, TM_QW3_HV_PHYS + TM_WORD0); |
| 427 | nsr = reg32 >> 24; |
| 428 | if (nsr == 0x82) { |
| 429 | break; |
| 430 | } |
| 431 | } |
| 432 | g_assert_cmphex(chosen_one, <, SMT); |
| 433 | cppr = (reg32 >> 16) & 0xFF; |
| 434 | g_assert_cmphex(nsr, ==, 0x82); |
| 435 | g_assert_cmphex(cppr, ==, 0xFF); |
| 436 | |
| 437 | /* ack the irq */ |
| 438 | reg16 = get_tima16(qts, chosen_one, TM_SPC_ACK_HV_REG); |
| 439 | nsr = reg16 >> 8; |
| 440 | cppr = reg16 & 0xFF; |
| 441 | g_assert_cmphex(nsr, ==, 0x82); |
| 442 | g_assert_cmphex(cppr, ==, priority); |
| 443 | |
| 444 | /* check irq data is what was configured */ |
| 445 | reg32 = qtest_readl(qts, xive_get_queue_addr(end_index)); |
| 446 | g_assert_cmphex((reg32 & 0x7fffffff), ==, (irq_data & 0x7fffffff)); |
| 447 | |
| 448 | /* End Of Interrupt */ |
| 449 | set_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_STORE_EOI, 0); |
| 450 | pq = get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_GET); |
| 451 | g_assert_cmpuint(pq, ==, XIVE_ESB_RESET); |
| 452 | |
| 453 | /* reset CPPR */ |
| 454 | set_tima8(qts, chosen_one, TM_QW3_HV_PHYS + TM_CPPR, 0xFF); |
| 455 | reg32 = get_tima32(qts, chosen_one, TM_QW3_HV_PHYS + TM_WORD0); |
| 456 | nsr = reg32 >> 24; |
| 457 | cppr = (reg32 >> 16) & 0xFF; |
| 458 | g_assert_cmphex(nsr, ==, 0x00); |
| 459 | g_assert_cmphex(cppr, ==, 0xFF); |
| 460 | } |
| 461 | |
| 462 | static void test_hw_group_irq_backlog(QTestState *qts) |
| 463 | { |
| 464 | uint32_t irq = 31; |
| 465 | uint32_t irq_data = 0x01234567; |
| 466 | uint32_t end_index = 129; |
| 467 | uint32_t target_nvp = 0x81; /* group size = 4 */ |
| 468 | uint32_t chosen_one = 3; |
| 469 | uint8_t blocking_priority, priority = 3; |
| 470 | uint32_t reg32; |
| 471 | uint16_t reg16; |
| 472 | uint8_t pq, nsr, cppr, lsmfb, i; |
| 473 | |
| 474 | g_test_message("========================================================="); |
| 475 | g_test_message("Testing irq %d to hardware group of size 4 going " \ |
| 476 | "through backlog", |
| 477 | irq); |
| 478 | |
| 479 | /* |
| 480 | * set current priority of all threads in the group to something |
| 481 | * higher than what we're about to trigger |
| 482 | */ |
| 483 | blocking_priority = priority - 1; |
| 484 | for (i = 0; i < SMT; i++) { |
| 485 | set_tima8(qts, i, TM_QW3_HV_PHYS + TM_CPPR, blocking_priority); |
| 486 | } |
| 487 | |
| 488 | /* irq config */ |
| 489 | set_eas(qts, irq, end_index, irq_data); |
| 490 | set_end(qts, end_index, target_nvp, priority, true /* group */); |
| 491 | |
| 492 | /* enable and trigger irq */ |
| 493 | get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_SET_PQ_00); |
| 494 | set_esb(qts, irq, XIVE_TRIGGER_PAGE, 0, 0); |
| 495 | |
| 496 | /* check irq is raised on cpu */ |
| 497 | pq = get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_GET); |
| 498 | g_assert_cmpuint(pq, ==, XIVE_ESB_PENDING); |
| 499 | |
| 500 | /* check no interrupt is pending on the 2 possible targets */ |
| 501 | for (i = 0; i < SMT; i++) { |
| 502 | reg32 = get_tima32(qts, i, TM_QW3_HV_PHYS + TM_WORD0); |
| 503 | nsr = reg32 >> 24; |
| 504 | cppr = (reg32 >> 16) & 0xFF; |
| 505 | lsmfb = reg32 & 0xFF; |
| 506 | g_assert_cmphex(nsr, ==, 0x0); |
| 507 | g_assert_cmphex(cppr, ==, blocking_priority); |
| 508 | g_assert_cmphex(lsmfb, ==, priority); |
| 509 | } |
| 510 | |
| 511 | /* lower priority of one thread */ |
| 512 | set_tima8(qts, chosen_one, TM_QW3_HV_PHYS + TM_CPPR, priority + 1); |
| 513 | |
| 514 | /* check backlogged interrupt is presented */ |
| 515 | reg32 = get_tima32(qts, chosen_one, TM_QW3_HV_PHYS + TM_WORD0); |
| 516 | nsr = reg32 >> 24; |
| 517 | cppr = (reg32 >> 16) & 0xFF; |
| 518 | g_assert_cmphex(nsr, ==, 0x82); |
| 519 | g_assert_cmphex(cppr, ==, priority + 1); |
| 520 | |
| 521 | /* ack the irq */ |
| 522 | reg16 = get_tima16(qts, chosen_one, TM_SPC_ACK_HV_REG); |
| 523 | nsr = reg16 >> 8; |
| 524 | cppr = reg16 & 0xFF; |
| 525 | g_assert_cmphex(nsr, ==, 0x82); |
| 526 | g_assert_cmphex(cppr, ==, priority); |
| 527 | |
| 528 | /* check irq data is what was configured */ |
| 529 | reg32 = qtest_readl(qts, xive_get_queue_addr(end_index)); |
| 530 | g_assert_cmphex((reg32 & 0x7fffffff), ==, (irq_data & 0x7fffffff)); |
| 531 | |
| 532 | /* End Of Interrupt */ |
| 533 | set_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_STORE_EOI, 0); |
| 534 | pq = get_esb(qts, irq, XIVE_EOI_PAGE, XIVE_ESB_GET); |
| 535 | g_assert_cmpuint(pq, ==, XIVE_ESB_RESET); |
| 536 | |
| 537 | /* reset CPPR */ |
| 538 | set_tima8(qts, chosen_one, TM_QW3_HV_PHYS + TM_CPPR, 0xFF); |
| 539 | reg32 = get_tima32(qts, chosen_one, TM_QW3_HV_PHYS + TM_WORD0); |
| 540 | nsr = reg32 >> 24; |
| 541 | cppr = (reg32 >> 16) & 0xFF; |
| 542 | lsmfb = reg32 & 0xFF; |
| 543 | g_assert_cmphex(nsr, ==, 0x00); |
| 544 | g_assert_cmphex(cppr, ==, 0xFF); |
| 545 | g_assert_cmphex(lsmfb, ==, 0xFF); |
| 546 | } |
| 547 | |
| 548 | static void test_xive(const void *data) |
| 549 | { |
| 550 | const PnvChip *chip = data; |
| 551 | const char *machine = pnv_get_machine_type(chip->chip_type); |
| 552 | QTestState *qts; |
| 553 | |
| 554 | qts = qtest_initf("-M %s -smp %d,cores=1,threads=%d -nographic " |
| 555 | "-nodefaults -S " |
| 556 | "-d guest_errors -trace '*xive*'", |
| 557 | machine, SMT, SMT); |
| 558 | init_xive(qts); |
| 559 | |
| 560 | test_hw_irq(qts); |
| 561 | |
| 562 | /* omit reset_state here and use settings from test_hw_irq */ |
| 563 | test_pull_thread_ctx_to_odd_thread_cl(qts); |
| 564 | |
| 565 | reset_state(qts); |
| 566 | test_pool_irq(qts); |
| 567 | |
| 568 | reset_state(qts); |
| 569 | test_hw_group_irq(qts); |
| 570 | |
| 571 | reset_state(qts); |
| 572 | test_hw_group_irq_backlog(qts); |
| 573 | |
| 574 | reset_state(qts); |
| 575 | test_flush_sync_inject(qts); |
| 576 | |
| 577 | reset_state(qts); |
| 578 | test_nvpg_bar(qts); |
| 579 | |
| 580 | qtest_quit(qts); |
| 581 | } |
| 582 | |
| 583 | int main(int argc, char **argv) |
| 584 | { |
| 585 | g_test_init(&argc, &argv, NULL); |
| 586 | |
| 587 | for (int i = 0; i < ARRAY_SIZE(pnv_chips); i++) { |
| 588 | /* xive2 exists from Power10 onwards */ |
| 589 | if (pnv_chips[i].chip_type < PNV_CHIP_POWER10) { |
| 590 | continue; |
| 591 | } |
| 592 | |
| 593 | g_autofree char *tname = g_strdup_printf("pnv-xive2/%s", |
| 594 | pnv_chips[i].cpu_model); |
| 595 | qtest_add_data_func(tname, &pnv_chips[i], test_xive); |
| 596 | } |
| 597 | return g_test_run(); |
| 598 | } |