| /* SPDX-License-Identifier: GPL-2.0-only */ |
| /* |
| * Service Call tests |
| * |
| * Copyright (c) 2020 IBM Corp |
| * |
| * Authors: |
| * Claudio Imbrenda <imbrenda@linux.ibm.com> |
| */ |
| |
| #include <libcflat.h> |
| #include <asm/page.h> |
| #include <asm/asm-offsets.h> |
| #include <asm/interrupt.h> |
| #include <sclp.h> |
| |
| #define PGM_NONE 1 |
| #define PGM_BIT_SPEC (1ULL << PGM_INT_CODE_SPECIFICATION) |
| #define PGM_BIT_ADDR (1ULL << PGM_INT_CODE_ADDRESSING) |
| #define PGM_BIT_PRIV (1ULL << PGM_INT_CODE_PRIVILEGED_OPERATION) |
| #define MKPTR(x) ((void *)(uint64_t)(x)) |
| |
| #define LC_SIZE (2 * PAGE_SIZE) |
| |
| static uint8_t pagebuf[LC_SIZE] __attribute__((aligned(LC_SIZE))); /* scratch pages used for some tests */ |
| static uint8_t prefix_buf[LC_SIZE] __attribute__((aligned(LC_SIZE))); /* temporary lowcore for test_sccb_prefix */ |
| /* SCCB template to be used */ |
| static union { |
| uint8_t raw[PAGE_SIZE]; |
| SCCBHeader header; |
| WriteEventData data; |
| } sccb_template; |
| static uint32_t valid_code; /* valid command code for READ SCP INFO */ |
| |
| /* |
| * Perform one service call, handling exceptions and interrupts. |
| */ |
| static int sclp_service_call_test(unsigned int command, void *sccb) |
| { |
| int cc; |
| |
| sclp_mark_busy(); |
| sclp_setup_int(); |
| cc = servc(command, __pa(sccb)); |
| if (lowcore.pgm_int_code) { |
| sclp_handle_ext(); |
| return 0; |
| } |
| if (!cc) |
| sclp_wait_busy(); |
| return cc; |
| } |
| |
| /* |
| * Perform one test at the given address, optionally using the SCCB template, |
| * checking for the expected program interrupts and return codes. |
| * |
| * The parameter buf_len indicates the number of bytes of the template that |
| * should be copied to the test address, and should be 0 when the test |
| * address is invalid, in which case nothing is copied. |
| * |
| * The template is used to simplify tests where the same buffer content is |
| * used many times in a row, at different addresses. |
| * |
| * Returns true in case of success or false in case of failure |
| */ |
| static bool test_one_sccb(uint32_t cmd, uint8_t *addr, uint16_t buf_len, uint64_t exp_pgm, uint16_t exp_rc) |
| { |
| SCCBHeader *h = (SCCBHeader *)addr; |
| int res, pgm; |
| |
| /* Copy the template to the test address if needed */ |
| if (buf_len) |
| memcpy(addr, sccb_template.raw, buf_len); |
| if (exp_pgm != PGM_NONE) |
| expect_pgm_int(); |
| /* perform the actual call */ |
| res = sclp_service_call_test(cmd, h); |
| if (res) { |
| report_info("SCLP not ready (command %#x, address %p, cc %d)", cmd, addr, res); |
| return false; |
| } |
| pgm = clear_pgm_int(); |
| /* Check if the program exception was one of the expected ones */ |
| if (!((1ULL << pgm) & exp_pgm)) { |
| report_info("First failure at addr %p, buf_len %d, cmd %#x, pgm code %d", |
| addr, buf_len, cmd, pgm); |
| return false; |
| } |
| /* Check if the response code is the one expected */ |
| if (exp_rc && exp_rc != h->response_code) { |
| report_info("First failure at addr %p, buf_len %d, cmd %#x, resp code %#x", |
| addr, buf_len, cmd, h->response_code); |
| return false; |
| } |
| return true; |
| } |
| |
| /* |
| * Wrapper for test_one_sccb to be used when the template should not be |
| * copied and the memory address should not be touched. |
| */ |
| static bool test_one_ro(uint32_t cmd, uint8_t *addr, uint64_t exp_pgm, uint16_t exp_rc) |
| { |
| return test_one_sccb(cmd, addr, 0, exp_pgm, exp_rc); |
| } |
| |
| /* |
| * Wrapper for test_one_sccb to set up a simple SCCB template. |
| * |
| * The parameter sccb_len indicates the value that will be saved in the SCCB |
| * length field of the SCCB, buf_len indicates the number of bytes of |
| * template that need to be copied to the actual test address. In many cases |
| * it's enough to clear/copy the first 8 bytes of the buffer, while the SCCB |
| * itself can be larger. |
| * |
| * Returns true in case of success or false in case of failure |
| */ |
| static bool test_one_simple(uint32_t cmd, uint8_t *addr, uint16_t sccb_len, |
| uint16_t buf_len, uint64_t exp_pgm, uint16_t exp_rc) |
| { |
| memset(sccb_template.raw, 0, sizeof(sccb_template.raw)); |
| sccb_template.header.length = sccb_len; |
| return test_one_sccb(cmd, addr, buf_len, exp_pgm, exp_rc); |
| } |
| |
| /* |
| * Test SCCB lengths < 8. |
| */ |
| static void test_sccb_too_short(void) |
| { |
| int len; |
| |
| for (len = 0; len < 8; len++) |
| if (!test_one_simple(valid_code, pagebuf, len, 8, PGM_BIT_SPEC, 0)) |
| break; |
| |
| report(len == 8, "SCCB too short"); |
| } |
| |
| /* |
| * Test SCCBs that are not 64-bit aligned. |
| */ |
| static void test_sccb_unaligned(void) |
| { |
| int offset; |
| |
| for (offset = 1; offset < 8; offset++) |
| if (!test_one_simple(valid_code, offset + pagebuf, 8, 8, PGM_BIT_SPEC, 0)) |
| break; |
| report(offset == 8, "SCCB unaligned"); |
| } |
| |
| /* |
| * Test SCCBs whose address is in the lowcore or prefix area. |
| */ |
| static void test_sccb_prefix(void) |
| { |
| uint8_t scratch[LC_SIZE]; |
| uint32_t prefix, new_prefix; |
| int offset; |
| |
| /* |
| * copy the current lowcore to the future new location, otherwise we |
| * will not have a valid lowcore after setting the new prefix. |
| */ |
| memcpy(prefix_buf, 0, LC_SIZE); |
| /* save the current prefix (it's probably going to be 0) */ |
| prefix = get_prefix(); |
| /* |
| * save the current content of absolute pages 0 and 1, so we can |
| * restore them after we trash them later on |
| */ |
| memcpy(scratch, (void *)(intptr_t)prefix, LC_SIZE); |
| /* set the new prefix to prefix_buf */ |
| new_prefix = (uint32_t)(intptr_t)prefix_buf; |
| set_prefix(new_prefix); |
| |
| /* |
| * testing with SCCB addresses in the lowcore; since we can't |
| * actually trash the lowcore (unsurprisingly, things break if we |
| * do), this will be a read-only test. |
| */ |
| for (offset = 0; offset < LC_SIZE; offset += 8) |
| if (!test_one_ro(valid_code, MKPTR(offset), PGM_BIT_SPEC, 0)) |
| break; |
| report(offset == LC_SIZE, "SCCB low pages"); |
| |
| /* |
| * the SCLP should not even touch the memory, but we will write the |
| * SCCBs all over the two pages starting at absolute address 0, thus |
| * trashing them; we will need to restore them later. |
| */ |
| for (offset = 0; offset < LC_SIZE; offset += 8) |
| if (!test_one_simple(valid_code, MKPTR(new_prefix + offset), 8, 8, PGM_BIT_SPEC, 0)) |
| break; |
| report(offset == LC_SIZE, "SCCB prefix pages"); |
| |
| /* restore the previous contents of absolute pages 0 and 1 */ |
| memcpy(prefix_buf, 0, LC_SIZE); |
| /* restore the prefix to the original value */ |
| set_prefix(prefix); |
| } |
| |
| /* |
| * Test SCCBs that are above 2GB. If outside of memory, an addressing |
| * exception is also allowed. |
| */ |
| static void test_sccb_high(void) |
| { |
| SCCBHeader *h = (SCCBHeader *)pagebuf; |
| uintptr_t a[33 * 4 * 2 + 2]; /* for the list of addresses to test */ |
| |
| uint64_t maxram; |
| int i, pgm, len = 0; |
| |
| h->length = 8; |
| /* addresses with 1 bit set in the first 33 bits */ |
| for (i = 0; i < 33; i++) |
| a[len++] = 1UL << (i + 31); |
| /* addresses with 2 consecutive bits set in the first 33 bits */ |
| for (i = 0; i < 33; i++) |
| a[len++] = 3UL << (i + 31); |
| /* addresses with all bits set in bits 0..N */ |
| for (i = 0; i < 33; i++) |
| a[len++] = 0xffffffff80000000UL << i; |
| /* addresses with all bits set in bits N..33 */ |
| a[len++] = 0x80000000; |
| for (i = 1; i < 33; i++, len++) |
| a[len] = a[len - 1] | (1UL << (i + 31)); |
| /* all the addresses above, but adding the offset of a valid buffer */ |
| for (i = 0; i < len; i++) |
| a[len + i] = a[i] + (intptr_t)h; |
| len += i; |
| /* two more hand-crafted addresses */ |
| a[len++] = 0xdeadbeef00000000; |
| a[len++] = 0xdeaddeadbeef0000; |
| |
| maxram = get_ram_size(); |
| for (i = 0; i < len; i++) { |
| pgm = PGM_BIT_SPEC | (a[i] >= maxram ? PGM_BIT_ADDR : 0); |
| if (!test_one_ro(valid_code, (void *)a[i], pgm, 0)) |
| break; |
| } |
| report(i == len, "SCCB high addresses"); |
| } |
| |
| /* |
| * Test invalid commands, both invalid command detail codes and valid |
| * ones with invalid command class code. |
| */ |
| static void test_inval(void) |
| { |
| const uint16_t res = SCLP_RC_INVALID_SCLP_COMMAND; |
| uint32_t cmd; |
| int i; |
| |
| report_prefix_push("Invalid command"); |
| for (i = 0; i < 65536; i++) { |
| cmd = 0xdead0000 | i; |
| if (!test_one_simple(cmd, pagebuf, PAGE_SIZE, PAGE_SIZE, PGM_NONE, res)) |
| break; |
| } |
| report(i == 65536, "Command detail code"); |
| |
| for (i = 0; i < 256; i++) { |
| cmd = (valid_code & ~0xff) | i; |
| if (cmd == valid_code) |
| continue; |
| if (!test_one_simple(cmd, pagebuf, PAGE_SIZE, PAGE_SIZE, PGM_NONE, res)) |
| break; |
| } |
| report(i == 256, "Command class code"); |
| report_prefix_pop(); |
| } |
| |
| |
| /* |
| * Test short SCCBs (but larger than 8). |
| */ |
| static void test_short(void) |
| { |
| const uint16_t res = SCLP_RC_INSUFFICIENT_SCCB_LENGTH; |
| int len; |
| |
| for (len = 8; len < 144; len++) |
| if (!test_one_simple(valid_code, pagebuf, len, len, PGM_NONE, res)) |
| break; |
| report(len == 144, "Insufficient SCCB length (Read SCP info)"); |
| |
| for (len = 8; len < 40; len++) |
| if (!test_one_simple(SCLP_READ_CPU_INFO, pagebuf, len, len, PGM_NONE, res)) |
| break; |
| report(len == 40, "Insufficient SCCB length (Read CPU info)"); |
| } |
| |
| /* |
| * Test SCCB page boundary violations. |
| */ |
| static void test_boundary(void) |
| { |
| const uint32_t cmd = SCLP_CMD_WRITE_EVENT_DATA; |
| const uint16_t res = SCLP_RC_SCCB_BOUNDARY_VIOLATION; |
| WriteEventData *sccb = &sccb_template.data; |
| int len, offset; |
| |
| memset(sccb_template.raw, 0, sizeof(sccb_template.raw)); |
| sccb->h.function_code = SCLP_FC_NORMAL_WRITE; |
| for (len = 32; len <= 4096; len++) { |
| offset = len & 7 ? len & ~7 : len - 8; |
| for (offset = 4096 - offset; offset < 4096; offset += 8) { |
| sccb->h.length = len; |
| if (!test_one_sccb(cmd, offset + pagebuf, len, PGM_NONE, res)) |
| goto out; |
| } |
| } |
| out: |
| report(len > 4096 && offset == 4096, "SCCB page boundary violation"); |
| } |
| |
| /* |
| * Test excessively long SCCBs. |
| */ |
| static void test_toolong(void) |
| { |
| const uint32_t cmd = SCLP_CMD_WRITE_EVENT_DATA; |
| const uint16_t res = SCLP_RC_SCCB_BOUNDARY_VIOLATION; |
| WriteEventData *sccb = &sccb_template.data; |
| int len; |
| |
| memset(sccb_template.raw, 0, sizeof(sccb_template.raw)); |
| sccb->h.function_code = SCLP_FC_NORMAL_WRITE; |
| for (len = 4097; len < 8192; len++) { |
| sccb->h.length = len; |
| if (!test_one_sccb(cmd, pagebuf, PAGE_SIZE, PGM_NONE, res)) |
| break; |
| } |
| report(len == 8192, "SCCB bigger than 4k"); |
| } |
| |
| /* |
| * Test privileged operation. |
| */ |
| static void test_priv(void) |
| { |
| SCCBHeader *h = (SCCBHeader *)pagebuf; |
| |
| report_prefix_push("Privileged operation"); |
| h->length = 8; |
| expect_pgm_int(); |
| enter_pstate(); |
| servc(valid_code, __pa(h)); |
| check_pgm_int_code(PGM_INT_CODE_PRIVILEGED_OPERATION); |
| report_prefix_pop(); |
| } |
| |
| /* |
| * Test addressing exceptions. We need to test SCCB addresses between the |
| * end of available memory and 2GB, because after 2GB a specification |
| * exception is also allowed. |
| * Only applicable if the VM has less than 2GB of memory |
| */ |
| static void test_addressing(void) |
| { |
| unsigned long i, maxram = get_ram_size(); |
| |
| /* the VM has more than 2GB of memory */ |
| if (maxram >= 0x80000000) { |
| report_skip("Invalid SCCB address"); |
| return; |
| } |
| /* test all possible valid addresses immediately after the end of memory |
| * up to 64KB after the end of memory |
| */ |
| for (i = 0; i < 0x10000 && i + maxram < 0x80000000; i += 8) |
| if (!test_one_ro(valid_code, MKPTR(i + maxram), PGM_BIT_ADDR, 0)) |
| goto out; |
| /* test more addresses until we reach 1MB after end of memory; |
| * increment by a prime number (times 8) in order to test all |
| * possible valid offsets inside pages |
| */ |
| for (; i < 0x100000 && i + maxram < 0x80000000 ; i += 808) |
| if (!test_one_ro(valid_code, MKPTR(i + maxram), PGM_BIT_ADDR, 0)) |
| goto out; |
| /* test the remaining addresses until we reach address 2GB; |
| * increment by a prime number (times 8) in order to test all |
| * possible valid offsets inside pages |
| */ |
| for (; i + maxram < 0x80000000; i += 800024) |
| if (!test_one_ro(valid_code, MKPTR(i + maxram), PGM_BIT_ADDR, 0)) |
| goto out; |
| out: |
| report(i + maxram >= 0x80000000, "Invalid SCCB address"); |
| } |
| |
| /* |
| * Test some bits in the instruction format that are specified to be ignored. |
| */ |
| static void test_instbits(void) |
| { |
| SCCBHeader *h = (SCCBHeader *)pagebuf; |
| int cc; |
| |
| sclp_mark_busy(); |
| h->length = 8; |
| sclp_setup_int(); |
| |
| asm volatile( |
| " .insn rre,0xb2204200,%1,%2\n" /* servc %1,%2 */ |
| " ipm %0\n" |
| " srl %0,28" |
| : "=&d" (cc) : "d" (valid_code), "a" (__pa(pagebuf)) |
| : "cc", "memory"); |
| /* No exception, but also no command accepted, so no interrupt is |
| * expected. We need to clear the flag manually otherwise we will |
| * loop forever when we try to report failure. |
| */ |
| if (cc) |
| sclp_handle_ext(); |
| else |
| sclp_wait_busy(); |
| report(cc == 0, "Instruction format ignored bits"); |
| } |
| |
| /* |
| * Find a valid READ INFO command code; not all codes are always allowed, and |
| * probing should be performed in the right order. |
| */ |
| static void find_valid_sclp_code(void) |
| { |
| const unsigned int commands[] = { SCLP_CMDW_READ_SCP_INFO_FORCED, |
| SCLP_CMDW_READ_SCP_INFO }; |
| SCCBHeader *h = (SCCBHeader *)pagebuf; |
| int i, cc; |
| |
| for (i = 0; i < ARRAY_SIZE(commands); i++) { |
| sclp_mark_busy(); |
| memset(h, 0, sizeof(*h)); |
| h->length = 4096; |
| |
| valid_code = commands[i]; |
| cc = sclp_service_call(commands[i], h); |
| if (cc) |
| break; |
| if (h->response_code == SCLP_RC_NORMAL_READ_COMPLETION) |
| return; |
| if (h->response_code != SCLP_RC_INVALID_SCLP_COMMAND) |
| break; |
| } |
| report_abort("READ_SCP_INFO failed"); |
| } |
| |
| int main(void) |
| { |
| report_prefix_push("sclp"); |
| find_valid_sclp_code(); |
| |
| /* Test some basic things */ |
| test_instbits(); |
| test_priv(); |
| test_addressing(); |
| |
| /* Test the specification exceptions */ |
| test_sccb_too_short(); |
| test_sccb_unaligned(); |
| test_sccb_prefix(); |
| test_sccb_high(); |
| |
| /* Test the expected response codes */ |
| test_inval(); |
| test_short(); |
| test_boundary(); |
| test_toolong(); |
| |
| return report_summary(); |
| } |