diff mbox series

[kvm-unit-tests,v8,6/6] s390x: SCLP unit test

Message ID 20200120184256.188698-7-imbrenda@linux.ibm.com (mailing list archive)
State New, archived
Headers show
Series s390x: SCLP Unit test | expand

Commit Message

Claudio Imbrenda Jan. 20, 2020, 6:42 p.m. UTC
SCLP unit test. Testing the following:

* Correctly ignoring instruction bits that should be ignored
* Privileged instruction check
* Check for addressing exceptions
* Specification exceptions:
  - SCCB size less than 8
  - SCCB unaligned
  - SCCB overlaps prefix or lowcore
  - SCCB address higher than 2GB
* Return codes for
  - Invalid command
  - SCCB too short (but at least 8)
  - SCCB page boundary violation

Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
Reviewed-by: Thomas Huth <thuth@redhat.com>
Acked-by: Janosch Frank <frankja@linux.ibm.com>
---
 s390x/Makefile      |   1 +
 s390x/sclp.c        | 474 ++++++++++++++++++++++++++++++++++++++++++++
 s390x/unittests.cfg |   8 +
 3 files changed, 483 insertions(+)
 create mode 100644 s390x/sclp.c

Comments

David Hildenbrand Jan. 22, 2020, 9:55 a.m. UTC | #1
[...]

>  all: directories test_cases test_cases_binary
> diff --git a/s390x/sclp.c b/s390x/sclp.c
> new file mode 100644
> index 0000000..215347e
> --- /dev/null
> +++ b/s390x/sclp.c
> @@ -0,0 +1,474 @@
> +/*
> + * Service Call tests
> + *
> + * Copyright (c) 2019 IBM Corp

Should be 2020 now. Will fix that up.

[...]

> +/**
> + * 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, buf_len);
> +	if (exp_pgm != PGM_NONE)
> +		expect_pgm_int();

I still dislike the handling here. Any caller can simply do a

expect_pgm_int();
test_one_sccb(...)
check_pgm_int_code(PGM_);

Same thing goes for exp_rc. We really should drop exp_rc and exp_pgm
handling from the function completely and handle it in the caller. That
makes the tests actually readable.

But I feel like I am repeating myself. I'll queue this patch with the
2020 fixed up and might send a fixup myself (if I find some spare minutes).
David Hildenbrand Jan. 22, 2020, 10:10 a.m. UTC | #2
On 20.01.20 19:42, Claudio Imbrenda wrote:
> SCLP unit test. Testing the following:
> 
> * Correctly ignoring instruction bits that should be ignored
> * Privileged instruction check
> * Check for addressing exceptions
> * Specification exceptions:
>   - SCCB size less than 8
>   - SCCB unaligned
>   - SCCB overlaps prefix or lowcore
>   - SCCB address higher than 2GB
> * Return codes for
>   - Invalid command
>   - SCCB too short (but at least 8)
>   - SCCB page boundary violation
> 
> Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
> Reviewed-by: Thomas Huth <thuth@redhat.com>
> Acked-by: Janosch Frank <frankja@linux.ibm.com>
> ---
>  s390x/Makefile      |   1 +
>  s390x/sclp.c        | 474 ++++++++++++++++++++++++++++++++++++++++++++
>  s390x/unittests.cfg |   8 +
>  3 files changed, 483 insertions(+)
>  create mode 100644 s390x/sclp.c
> 
> diff --git a/s390x/Makefile b/s390x/Makefile
> index 3744372..ddb4b48 100644
> --- a/s390x/Makefile
> +++ b/s390x/Makefile
> @@ -16,6 +16,7 @@ tests += $(TEST_DIR)/diag288.elf
>  tests += $(TEST_DIR)/stsi.elf
>  tests += $(TEST_DIR)/skrf.elf
>  tests += $(TEST_DIR)/smp.elf
> +tests += $(TEST_DIR)/sclp.elf
>  tests_binary = $(patsubst %.elf,%.bin,$(tests))
>  
>  all: directories test_cases test_cases_binary
> diff --git a/s390x/sclp.c b/s390x/sclp.c
> new file mode 100644
> index 0000000..215347e
> --- /dev/null
> +++ b/s390x/sclp.c
> @@ -0,0 +1,474 @@
> +/*
> + * Service Call tests
> + *
> + * Copyright (c) 2019 IBM Corp
> + *
> + * Authors:
> + *  Claudio Imbrenda <imbrenda@linux.ibm.com>
> + *
> + * This code is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2.
> + */
> +
> +#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 */
> +static uint8_t sccb_template[PAGE_SIZE];				/* SCCB template to be used */
> +static uint32_t valid_code;						/* valid command code for READ SCP INFO */
> +static struct lowcore *lc;
> +
> +/**
> + * 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 (lc->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, 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, 0, sizeof(sccb_template));
> +	((SCCBHeader *)sccb_template)->length = sccb_len;
> +	return test_one_sccb(cmd, addr, buf_len, exp_pgm, exp_rc);

Doing a fresh ./configure + make on RHEL7 gives me

[linux1@rhkvm01 kvm-unit-tests]$ make
gcc  -std=gnu99 -ffreestanding -I /home/linux1/git/kvm-unit-tests/lib -I /home/linux1/git/kvm-unit-tests/lib/s390x -I lib -O2 -march=zEC12 -fno-delete-null-pointer-checks -g -MMD -MF s390x/.sclp.d -Wall -Wwrite-strings -Wempty-body -Wuninitialized -Wignored-qualifiers -Werror  -fomit-frame-pointer    -Wno-frame-address   -fno-pic    -Wclobbered  -Wunused-but-set-parameter  -Wmissing-parameter-type  -Wold-style-declaration -Woverride-init -Wmissing-prototypes -Wstrict-prototypes   -c -o s390x/sclp.o s390x/sclp.c
s390x/sclp.c: In function 'test_one_simple':
s390x/sclp.c:121:2: error: dereferencing type-punned pointer will break strict-aliasing rules [-Werror=strict-aliasing]
  ((SCCBHeader *)sccb_template)->length = sccb_len;
  ^
s390x/sclp.c: At top level:
cc1: error: unrecognized command line option "-Wno-frame-address" [-Werror]
cc1: all warnings being treated as errors
make: *** [s390x/sclp.o] Error 1
David Hildenbrand Jan. 22, 2020, 10:22 a.m. UTC | #3
On 22.01.20 11:10, David Hildenbrand wrote:
> On 20.01.20 19:42, Claudio Imbrenda wrote:
>> SCLP unit test. Testing the following:
>>
>> * Correctly ignoring instruction bits that should be ignored
>> * Privileged instruction check
>> * Check for addressing exceptions
>> * Specification exceptions:
>>   - SCCB size less than 8
>>   - SCCB unaligned
>>   - SCCB overlaps prefix or lowcore
>>   - SCCB address higher than 2GB
>> * Return codes for
>>   - Invalid command
>>   - SCCB too short (but at least 8)
>>   - SCCB page boundary violation
>>
>> Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
>> Reviewed-by: Thomas Huth <thuth@redhat.com>
>> Acked-by: Janosch Frank <frankja@linux.ibm.com>
>> ---
>>  s390x/Makefile      |   1 +
>>  s390x/sclp.c        | 474 ++++++++++++++++++++++++++++++++++++++++++++
>>  s390x/unittests.cfg |   8 +
>>  3 files changed, 483 insertions(+)
>>  create mode 100644 s390x/sclp.c
>>
>> diff --git a/s390x/Makefile b/s390x/Makefile
>> index 3744372..ddb4b48 100644
>> --- a/s390x/Makefile
>> +++ b/s390x/Makefile
>> @@ -16,6 +16,7 @@ tests += $(TEST_DIR)/diag288.elf
>>  tests += $(TEST_DIR)/stsi.elf
>>  tests += $(TEST_DIR)/skrf.elf
>>  tests += $(TEST_DIR)/smp.elf
>> +tests += $(TEST_DIR)/sclp.elf
>>  tests_binary = $(patsubst %.elf,%.bin,$(tests))
>>  
>>  all: directories test_cases test_cases_binary
>> diff --git a/s390x/sclp.c b/s390x/sclp.c
>> new file mode 100644
>> index 0000000..215347e
>> --- /dev/null
>> +++ b/s390x/sclp.c
>> @@ -0,0 +1,474 @@
>> +/*
>> + * Service Call tests
>> + *
>> + * Copyright (c) 2019 IBM Corp
>> + *
>> + * Authors:
>> + *  Claudio Imbrenda <imbrenda@linux.ibm.com>
>> + *
>> + * This code is free software; you can redistribute it and/or modify it
>> + * under the terms of the GNU General Public License version 2.
>> + */
>> +
>> +#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 */
>> +static uint8_t sccb_template[PAGE_SIZE];				/* SCCB template to be used */
>> +static uint32_t valid_code;						/* valid command code for READ SCP INFO */
>> +static struct lowcore *lc;
>> +
>> +/**
>> + * 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 (lc->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, 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, 0, sizeof(sccb_template));
>> +	((SCCBHeader *)sccb_template)->length = sccb_len;
>> +	return test_one_sccb(cmd, addr, buf_len, exp_pgm, exp_rc);
> 
> Doing a fresh ./configure + make on RHEL7 gives me
> 
> [linux1@rhkvm01 kvm-unit-tests]$ make
> gcc  -std=gnu99 -ffreestanding -I /home/linux1/git/kvm-unit-tests/lib -I /home/linux1/git/kvm-unit-tests/lib/s390x -I lib -O2 -march=zEC12 -fno-delete-null-pointer-checks -g -MMD -MF s390x/.sclp.d -Wall -Wwrite-strings -Wempty-body -Wuninitialized -Wignored-qualifiers -Werror  -fomit-frame-pointer    -Wno-frame-address   -fno-pic    -Wclobbered  -Wunused-but-set-parameter  -Wmissing-parameter-type  -Wold-style-declaration -Woverride-init -Wmissing-prototypes -Wstrict-prototypes   -c -o s390x/sclp.o s390x/sclp.c
> s390x/sclp.c: In function 'test_one_simple':
> s390x/sclp.c:121:2: error: dereferencing type-punned pointer will break strict-aliasing rules [-Werror=strict-aliasing]
>   ((SCCBHeader *)sccb_template)->length = sccb_len;
>   ^
> s390x/sclp.c: At top level:
> cc1: error: unrecognized command line option "-Wno-frame-address" [-Werror]
> cc1: all warnings being treated as errors
> make: *** [s390x/sclp.o] Error 1

The following makes it work:


diff --git a/s390x/sclp.c b/s390x/sclp.c
index c13fa60..0b8117a 100644
--- a/s390x/sclp.c
+++ b/s390x/sclp.c
@@ -117,8 +117,10 @@ static bool test_one_ro(uint32_t cmd, uint8_t *addr, uint64_t exp_pgm, uint16_t
 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)
 {
+       SCCBHeader *header = (void *)sccb_template;
+
        memset(sccb_template, 0, sizeof(sccb_template));
-       ((SCCBHeader *)sccb_template)->length = sccb_len;
+       header->length = sccb_len;
        return test_one_sccb(cmd, addr, buf_len, exp_pgm, exp_rc);
 }
Thomas Huth Jan. 22, 2020, 10:31 a.m. UTC | #4
On 22/01/2020 11.22, David Hildenbrand wrote:
> On 22.01.20 11:10, David Hildenbrand wrote:
>> On 20.01.20 19:42, Claudio Imbrenda wrote:
>>> SCLP unit test. Testing the following:
>>>
>>> * Correctly ignoring instruction bits that should be ignored
>>> * Privileged instruction check
>>> * Check for addressing exceptions
>>> * Specification exceptions:
>>>   - SCCB size less than 8
>>>   - SCCB unaligned
>>>   - SCCB overlaps prefix or lowcore
>>>   - SCCB address higher than 2GB
>>> * Return codes for
>>>   - Invalid command
>>>   - SCCB too short (but at least 8)
>>>   - SCCB page boundary violation
>>>
>>> Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
>>> Reviewed-by: Thomas Huth <thuth@redhat.com>
>>> Acked-by: Janosch Frank <frankja@linux.ibm.com>
>>> ---
>>>  s390x/Makefile      |   1 +
>>>  s390x/sclp.c        | 474 ++++++++++++++++++++++++++++++++++++++++++++
>>>  s390x/unittests.cfg |   8 +
>>>  3 files changed, 483 insertions(+)
>>>  create mode 100644 s390x/sclp.c
>>>
>>> diff --git a/s390x/Makefile b/s390x/Makefile
>>> index 3744372..ddb4b48 100644
>>> --- a/s390x/Makefile
>>> +++ b/s390x/Makefile
>>> @@ -16,6 +16,7 @@ tests += $(TEST_DIR)/diag288.elf
>>>  tests += $(TEST_DIR)/stsi.elf
>>>  tests += $(TEST_DIR)/skrf.elf
>>>  tests += $(TEST_DIR)/smp.elf
>>> +tests += $(TEST_DIR)/sclp.elf
>>>  tests_binary = $(patsubst %.elf,%.bin,$(tests))
>>>  
>>>  all: directories test_cases test_cases_binary
>>> diff --git a/s390x/sclp.c b/s390x/sclp.c
>>> new file mode 100644
>>> index 0000000..215347e
>>> --- /dev/null
>>> +++ b/s390x/sclp.c
>>> @@ -0,0 +1,474 @@
>>> +/*
>>> + * Service Call tests
>>> + *
>>> + * Copyright (c) 2019 IBM Corp
>>> + *
>>> + * Authors:
>>> + *  Claudio Imbrenda <imbrenda@linux.ibm.com>
>>> + *
>>> + * This code is free software; you can redistribute it and/or modify it
>>> + * under the terms of the GNU General Public License version 2.
>>> + */
>>> +
>>> +#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 */
>>> +static uint8_t sccb_template[PAGE_SIZE];				/* SCCB template to be used */
>>> +static uint32_t valid_code;						/* valid command code for READ SCP INFO */
>>> +static struct lowcore *lc;
>>> +
>>> +/**
>>> + * 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 (lc->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, 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, 0, sizeof(sccb_template));
>>> +	((SCCBHeader *)sccb_template)->length = sccb_len;
>>> +	return test_one_sccb(cmd, addr, buf_len, exp_pgm, exp_rc);
>>
>> Doing a fresh ./configure + make on RHEL7 gives me
>>
>> [linux1@rhkvm01 kvm-unit-tests]$ make
>> gcc  -std=gnu99 -ffreestanding -I /home/linux1/git/kvm-unit-tests/lib -I /home/linux1/git/kvm-unit-tests/lib/s390x -I lib -O2 -march=zEC12 -fno-delete-null-pointer-checks -g -MMD -MF s390x/.sclp.d -Wall -Wwrite-strings -Wempty-body -Wuninitialized -Wignored-qualifiers -Werror  -fomit-frame-pointer    -Wno-frame-address   -fno-pic    -Wclobbered  -Wunused-but-set-parameter  -Wmissing-parameter-type  -Wold-style-declaration -Woverride-init -Wmissing-prototypes -Wstrict-prototypes   -c -o s390x/sclp.o s390x/sclp.c
>> s390x/sclp.c: In function 'test_one_simple':
>> s390x/sclp.c:121:2: error: dereferencing type-punned pointer will break strict-aliasing rules [-Werror=strict-aliasing]
>>   ((SCCBHeader *)sccb_template)->length = sccb_len;
>>   ^
>> s390x/sclp.c: At top level:
>> cc1: error: unrecognized command line option "-Wno-frame-address" [-Werror]
>> cc1: all warnings being treated as errors
>> make: *** [s390x/sclp.o] Error 1
> 
> The following makes it work:
> 
> 
> diff --git a/s390x/sclp.c b/s390x/sclp.c
> index c13fa60..0b8117a 100644
> --- a/s390x/sclp.c
> +++ b/s390x/sclp.c
> @@ -117,8 +117,10 @@ static bool test_one_ro(uint32_t cmd, uint8_t *addr, uint64_t exp_pgm, uint16_t
>  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)
>  {
> +       SCCBHeader *header = (void *)sccb_template;
> +
>         memset(sccb_template, 0, sizeof(sccb_template));
> -       ((SCCBHeader *)sccb_template)->length = sccb_len;
> +       header->length = sccb_len;

While that might silence the compiler warning, we still might get
aliasing problems here, I think.
The right way to solve this problem is to turn sccb_template into a
union of the various structs/arrays that you want to use and then access
the fields through the union instead ("type-punning through union").

 Thomas
David Hildenbrand Jan. 22, 2020, 10:32 a.m. UTC | #5
On 22.01.20 11:31, Thomas Huth wrote:
> On 22/01/2020 11.22, David Hildenbrand wrote:
>> On 22.01.20 11:10, David Hildenbrand wrote:
>>> On 20.01.20 19:42, Claudio Imbrenda wrote:
>>>> SCLP unit test. Testing the following:
>>>>
>>>> * Correctly ignoring instruction bits that should be ignored
>>>> * Privileged instruction check
>>>> * Check for addressing exceptions
>>>> * Specification exceptions:
>>>>   - SCCB size less than 8
>>>>   - SCCB unaligned
>>>>   - SCCB overlaps prefix or lowcore
>>>>   - SCCB address higher than 2GB
>>>> * Return codes for
>>>>   - Invalid command
>>>>   - SCCB too short (but at least 8)
>>>>   - SCCB page boundary violation
>>>>
>>>> Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
>>>> Reviewed-by: Thomas Huth <thuth@redhat.com>
>>>> Acked-by: Janosch Frank <frankja@linux.ibm.com>
>>>> ---
>>>>  s390x/Makefile      |   1 +
>>>>  s390x/sclp.c        | 474 ++++++++++++++++++++++++++++++++++++++++++++
>>>>  s390x/unittests.cfg |   8 +
>>>>  3 files changed, 483 insertions(+)
>>>>  create mode 100644 s390x/sclp.c
>>>>
>>>> diff --git a/s390x/Makefile b/s390x/Makefile
>>>> index 3744372..ddb4b48 100644
>>>> --- a/s390x/Makefile
>>>> +++ b/s390x/Makefile
>>>> @@ -16,6 +16,7 @@ tests += $(TEST_DIR)/diag288.elf
>>>>  tests += $(TEST_DIR)/stsi.elf
>>>>  tests += $(TEST_DIR)/skrf.elf
>>>>  tests += $(TEST_DIR)/smp.elf
>>>> +tests += $(TEST_DIR)/sclp.elf
>>>>  tests_binary = $(patsubst %.elf,%.bin,$(tests))
>>>>  
>>>>  all: directories test_cases test_cases_binary
>>>> diff --git a/s390x/sclp.c b/s390x/sclp.c
>>>> new file mode 100644
>>>> index 0000000..215347e
>>>> --- /dev/null
>>>> +++ b/s390x/sclp.c
>>>> @@ -0,0 +1,474 @@
>>>> +/*
>>>> + * Service Call tests
>>>> + *
>>>> + * Copyright (c) 2019 IBM Corp
>>>> + *
>>>> + * Authors:
>>>> + *  Claudio Imbrenda <imbrenda@linux.ibm.com>
>>>> + *
>>>> + * This code is free software; you can redistribute it and/or modify it
>>>> + * under the terms of the GNU General Public License version 2.
>>>> + */
>>>> +
>>>> +#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 */
>>>> +static uint8_t sccb_template[PAGE_SIZE];				/* SCCB template to be used */
>>>> +static uint32_t valid_code;						/* valid command code for READ SCP INFO */
>>>> +static struct lowcore *lc;
>>>> +
>>>> +/**
>>>> + * 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 (lc->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, 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, 0, sizeof(sccb_template));
>>>> +	((SCCBHeader *)sccb_template)->length = sccb_len;
>>>> +	return test_one_sccb(cmd, addr, buf_len, exp_pgm, exp_rc);
>>>
>>> Doing a fresh ./configure + make on RHEL7 gives me
>>>
>>> [linux1@rhkvm01 kvm-unit-tests]$ make
>>> gcc  -std=gnu99 -ffreestanding -I /home/linux1/git/kvm-unit-tests/lib -I /home/linux1/git/kvm-unit-tests/lib/s390x -I lib -O2 -march=zEC12 -fno-delete-null-pointer-checks -g -MMD -MF s390x/.sclp.d -Wall -Wwrite-strings -Wempty-body -Wuninitialized -Wignored-qualifiers -Werror  -fomit-frame-pointer    -Wno-frame-address   -fno-pic    -Wclobbered  -Wunused-but-set-parameter  -Wmissing-parameter-type  -Wold-style-declaration -Woverride-init -Wmissing-prototypes -Wstrict-prototypes   -c -o s390x/sclp.o s390x/sclp.c
>>> s390x/sclp.c: In function 'test_one_simple':
>>> s390x/sclp.c:121:2: error: dereferencing type-punned pointer will break strict-aliasing rules [-Werror=strict-aliasing]
>>>   ((SCCBHeader *)sccb_template)->length = sccb_len;
>>>   ^
>>> s390x/sclp.c: At top level:
>>> cc1: error: unrecognized command line option "-Wno-frame-address" [-Werror]
>>> cc1: all warnings being treated as errors
>>> make: *** [s390x/sclp.o] Error 1
>>
>> The following makes it work:
>>
>>
>> diff --git a/s390x/sclp.c b/s390x/sclp.c
>> index c13fa60..0b8117a 100644
>> --- a/s390x/sclp.c
>> +++ b/s390x/sclp.c
>> @@ -117,8 +117,10 @@ static bool test_one_ro(uint32_t cmd, uint8_t *addr, uint64_t exp_pgm, uint16_t
>>  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)
>>  {
>> +       SCCBHeader *header = (void *)sccb_template;
>> +
>>         memset(sccb_template, 0, sizeof(sccb_template));
>> -       ((SCCBHeader *)sccb_template)->length = sccb_len;
>> +       header->length = sccb_len;
> 
> While that might silence the compiler warning, we still might get
> aliasing problems here, I think.
> The right way to solve this problem is to turn sccb_template into a
> union of the various structs/arrays that you want to use and then access
> the fields through the union instead ("type-punning through union").

We do have the exact same thing in lib/s390x/sclp.c already, no?

Especially, new compilers don't seem to care?
Thomas Huth Jan. 22, 2020, 10:39 a.m. UTC | #6
On 22/01/2020 11.32, David Hildenbrand wrote:
> On 22.01.20 11:31, Thomas Huth wrote:
>> On 22/01/2020 11.22, David Hildenbrand wrote:
>>> On 22.01.20 11:10, David Hildenbrand wrote:
>>>> On 20.01.20 19:42, Claudio Imbrenda wrote:
>>>>> SCLP unit test. Testing the following:
>>>>>
>>>>> * Correctly ignoring instruction bits that should be ignored
>>>>> * Privileged instruction check
>>>>> * Check for addressing exceptions
>>>>> * Specification exceptions:
>>>>>   - SCCB size less than 8
>>>>>   - SCCB unaligned
>>>>>   - SCCB overlaps prefix or lowcore
>>>>>   - SCCB address higher than 2GB
>>>>> * Return codes for
>>>>>   - Invalid command
>>>>>   - SCCB too short (but at least 8)
>>>>>   - SCCB page boundary violation
>>>>>
>>>>> Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
>>>>> Reviewed-by: Thomas Huth <thuth@redhat.com>
>>>>> Acked-by: Janosch Frank <frankja@linux.ibm.com>
>>>>> ---
>>>>>  s390x/Makefile      |   1 +
>>>>>  s390x/sclp.c        | 474 ++++++++++++++++++++++++++++++++++++++++++++
>>>>>  s390x/unittests.cfg |   8 +
>>>>>  3 files changed, 483 insertions(+)
>>>>>  create mode 100644 s390x/sclp.c
>>>>>
>>>>> diff --git a/s390x/Makefile b/s390x/Makefile
>>>>> index 3744372..ddb4b48 100644
>>>>> --- a/s390x/Makefile
>>>>> +++ b/s390x/Makefile
>>>>> @@ -16,6 +16,7 @@ tests += $(TEST_DIR)/diag288.elf
>>>>>  tests += $(TEST_DIR)/stsi.elf
>>>>>  tests += $(TEST_DIR)/skrf.elf
>>>>>  tests += $(TEST_DIR)/smp.elf
>>>>> +tests += $(TEST_DIR)/sclp.elf
>>>>>  tests_binary = $(patsubst %.elf,%.bin,$(tests))
>>>>>  
>>>>>  all: directories test_cases test_cases_binary
>>>>> diff --git a/s390x/sclp.c b/s390x/sclp.c
>>>>> new file mode 100644
>>>>> index 0000000..215347e
>>>>> --- /dev/null
>>>>> +++ b/s390x/sclp.c
>>>>> @@ -0,0 +1,474 @@
>>>>> +/*
>>>>> + * Service Call tests
>>>>> + *
>>>>> + * Copyright (c) 2019 IBM Corp
>>>>> + *
>>>>> + * Authors:
>>>>> + *  Claudio Imbrenda <imbrenda@linux.ibm.com>
>>>>> + *
>>>>> + * This code is free software; you can redistribute it and/or modify it
>>>>> + * under the terms of the GNU General Public License version 2.
>>>>> + */
>>>>> +
>>>>> +#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 */
>>>>> +static uint8_t sccb_template[PAGE_SIZE];				/* SCCB template to be used */
>>>>> +static uint32_t valid_code;						/* valid command code for READ SCP INFO */
>>>>> +static struct lowcore *lc;
>>>>> +
>>>>> +/**
>>>>> + * 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 (lc->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, 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, 0, sizeof(sccb_template));
>>>>> +	((SCCBHeader *)sccb_template)->length = sccb_len;
>>>>> +	return test_one_sccb(cmd, addr, buf_len, exp_pgm, exp_rc);
>>>>
>>>> Doing a fresh ./configure + make on RHEL7 gives me
>>>>
>>>> [linux1@rhkvm01 kvm-unit-tests]$ make
>>>> gcc  -std=gnu99 -ffreestanding -I /home/linux1/git/kvm-unit-tests/lib -I /home/linux1/git/kvm-unit-tests/lib/s390x -I lib -O2 -march=zEC12 -fno-delete-null-pointer-checks -g -MMD -MF s390x/.sclp.d -Wall -Wwrite-strings -Wempty-body -Wuninitialized -Wignored-qualifiers -Werror  -fomit-frame-pointer    -Wno-frame-address   -fno-pic    -Wclobbered  -Wunused-but-set-parameter  -Wmissing-parameter-type  -Wold-style-declaration -Woverride-init -Wmissing-prototypes -Wstrict-prototypes   -c -o s390x/sclp.o s390x/sclp.c
>>>> s390x/sclp.c: In function 'test_one_simple':
>>>> s390x/sclp.c:121:2: error: dereferencing type-punned pointer will break strict-aliasing rules [-Werror=strict-aliasing]
>>>>   ((SCCBHeader *)sccb_template)->length = sccb_len;
>>>>   ^
>>>> s390x/sclp.c: At top level:
>>>> cc1: error: unrecognized command line option "-Wno-frame-address" [-Werror]
>>>> cc1: all warnings being treated as errors
>>>> make: *** [s390x/sclp.o] Error 1
>>>
>>> The following makes it work:
>>>
>>>
>>> diff --git a/s390x/sclp.c b/s390x/sclp.c
>>> index c13fa60..0b8117a 100644
>>> --- a/s390x/sclp.c
>>> +++ b/s390x/sclp.c
>>> @@ -117,8 +117,10 @@ static bool test_one_ro(uint32_t cmd, uint8_t *addr, uint64_t exp_pgm, uint16_t
>>>  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)
>>>  {
>>> +       SCCBHeader *header = (void *)sccb_template;
>>> +
>>>         memset(sccb_template, 0, sizeof(sccb_template));
>>> -       ((SCCBHeader *)sccb_template)->length = sccb_len;
>>> +       header->length = sccb_len;
>>
>> While that might silence the compiler warning, we still might get
>> aliasing problems here, I think.
>> The right way to solve this problem is to turn sccb_template into a
>> union of the various structs/arrays that you want to use and then access
>> the fields through the union instead ("type-punning through union").
> 
> We do have the exact same thing in lib/s390x/sclp.c already, no?

Maybe we should carefully check that code, too...

> Especially, new compilers don't seem to care?

I've seen horrible bugs due to these aliasing problems in the past -
without compiler warnings showing up! Certain versions of GCC assume
that they can re-order code with pointers that point to types of
different sizes, i.e. in the above example, I think they could assume
that they could re-order the memset() and the header->length = ... line.
I'd feel better if we play safe and use a union here.

 Thomas
David Hildenbrand Jan. 22, 2020, 10:40 a.m. UTC | #7
On 22.01.20 11:39, Thomas Huth wrote:
> On 22/01/2020 11.32, David Hildenbrand wrote:
>> On 22.01.20 11:31, Thomas Huth wrote:
>>> On 22/01/2020 11.22, David Hildenbrand wrote:
>>>> On 22.01.20 11:10, David Hildenbrand wrote:
>>>>> On 20.01.20 19:42, Claudio Imbrenda wrote:
>>>>>> SCLP unit test. Testing the following:
>>>>>>
>>>>>> * Correctly ignoring instruction bits that should be ignored
>>>>>> * Privileged instruction check
>>>>>> * Check for addressing exceptions
>>>>>> * Specification exceptions:
>>>>>>   - SCCB size less than 8
>>>>>>   - SCCB unaligned
>>>>>>   - SCCB overlaps prefix or lowcore
>>>>>>   - SCCB address higher than 2GB
>>>>>> * Return codes for
>>>>>>   - Invalid command
>>>>>>   - SCCB too short (but at least 8)
>>>>>>   - SCCB page boundary violation
>>>>>>
>>>>>> Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
>>>>>> Reviewed-by: Thomas Huth <thuth@redhat.com>
>>>>>> Acked-by: Janosch Frank <frankja@linux.ibm.com>
>>>>>> ---
>>>>>>  s390x/Makefile      |   1 +
>>>>>>  s390x/sclp.c        | 474 ++++++++++++++++++++++++++++++++++++++++++++
>>>>>>  s390x/unittests.cfg |   8 +
>>>>>>  3 files changed, 483 insertions(+)
>>>>>>  create mode 100644 s390x/sclp.c
>>>>>>
>>>>>> diff --git a/s390x/Makefile b/s390x/Makefile
>>>>>> index 3744372..ddb4b48 100644
>>>>>> --- a/s390x/Makefile
>>>>>> +++ b/s390x/Makefile
>>>>>> @@ -16,6 +16,7 @@ tests += $(TEST_DIR)/diag288.elf
>>>>>>  tests += $(TEST_DIR)/stsi.elf
>>>>>>  tests += $(TEST_DIR)/skrf.elf
>>>>>>  tests += $(TEST_DIR)/smp.elf
>>>>>> +tests += $(TEST_DIR)/sclp.elf
>>>>>>  tests_binary = $(patsubst %.elf,%.bin,$(tests))
>>>>>>  
>>>>>>  all: directories test_cases test_cases_binary
>>>>>> diff --git a/s390x/sclp.c b/s390x/sclp.c
>>>>>> new file mode 100644
>>>>>> index 0000000..215347e
>>>>>> --- /dev/null
>>>>>> +++ b/s390x/sclp.c
>>>>>> @@ -0,0 +1,474 @@
>>>>>> +/*
>>>>>> + * Service Call tests
>>>>>> + *
>>>>>> + * Copyright (c) 2019 IBM Corp
>>>>>> + *
>>>>>> + * Authors:
>>>>>> + *  Claudio Imbrenda <imbrenda@linux.ibm.com>
>>>>>> + *
>>>>>> + * This code is free software; you can redistribute it and/or modify it
>>>>>> + * under the terms of the GNU General Public License version 2.
>>>>>> + */
>>>>>> +
>>>>>> +#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 */
>>>>>> +static uint8_t sccb_template[PAGE_SIZE];				/* SCCB template to be used */
>>>>>> +static uint32_t valid_code;						/* valid command code for READ SCP INFO */
>>>>>> +static struct lowcore *lc;
>>>>>> +
>>>>>> +/**
>>>>>> + * 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 (lc->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, 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, 0, sizeof(sccb_template));
>>>>>> +	((SCCBHeader *)sccb_template)->length = sccb_len;
>>>>>> +	return test_one_sccb(cmd, addr, buf_len, exp_pgm, exp_rc);
>>>>>
>>>>> Doing a fresh ./configure + make on RHEL7 gives me
>>>>>
>>>>> [linux1@rhkvm01 kvm-unit-tests]$ make
>>>>> gcc  -std=gnu99 -ffreestanding -I /home/linux1/git/kvm-unit-tests/lib -I /home/linux1/git/kvm-unit-tests/lib/s390x -I lib -O2 -march=zEC12 -fno-delete-null-pointer-checks -g -MMD -MF s390x/.sclp.d -Wall -Wwrite-strings -Wempty-body -Wuninitialized -Wignored-qualifiers -Werror  -fomit-frame-pointer    -Wno-frame-address   -fno-pic    -Wclobbered  -Wunused-but-set-parameter  -Wmissing-parameter-type  -Wold-style-declaration -Woverride-init -Wmissing-prototypes -Wstrict-prototypes   -c -o s390x/sclp.o s390x/sclp.c
>>>>> s390x/sclp.c: In function 'test_one_simple':
>>>>> s390x/sclp.c:121:2: error: dereferencing type-punned pointer will break strict-aliasing rules [-Werror=strict-aliasing]
>>>>>   ((SCCBHeader *)sccb_template)->length = sccb_len;
>>>>>   ^
>>>>> s390x/sclp.c: At top level:
>>>>> cc1: error: unrecognized command line option "-Wno-frame-address" [-Werror]
>>>>> cc1: all warnings being treated as errors
>>>>> make: *** [s390x/sclp.o] Error 1
>>>>
>>>> The following makes it work:
>>>>
>>>>
>>>> diff --git a/s390x/sclp.c b/s390x/sclp.c
>>>> index c13fa60..0b8117a 100644
>>>> --- a/s390x/sclp.c
>>>> +++ b/s390x/sclp.c
>>>> @@ -117,8 +117,10 @@ static bool test_one_ro(uint32_t cmd, uint8_t *addr, uint64_t exp_pgm, uint16_t
>>>>  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)
>>>>  {
>>>> +       SCCBHeader *header = (void *)sccb_template;
>>>> +
>>>>         memset(sccb_template, 0, sizeof(sccb_template));
>>>> -       ((SCCBHeader *)sccb_template)->length = sccb_len;
>>>> +       header->length = sccb_len;
>>>
>>> While that might silence the compiler warning, we still might get
>>> aliasing problems here, I think.
>>> The right way to solve this problem is to turn sccb_template into a
>>> union of the various structs/arrays that you want to use and then access
>>> the fields through the union instead ("type-punning through union").
>>
>> We do have the exact same thing in lib/s390x/sclp.c already, no?
> 
> Maybe we should carefully check that code, too...
> 
>> Especially, new compilers don't seem to care?
> 
> I've seen horrible bugs due to these aliasing problems in the past -
> without compiler warnings showing up! Certain versions of GCC assume
> that they can re-order code with pointers that point to types of
> different sizes, i.e. in the above example, I think they could assume
> that they could re-order the memset() and the header->length = ... line.
> I'd feel better if we play safe and use a union here.

Should we simply allow type-punning?
David Hildenbrand Jan. 22, 2020, 11:20 a.m. UTC | #8
On 22.01.20 11:40, David Hildenbrand wrote:
> On 22.01.20 11:39, Thomas Huth wrote:
>> On 22/01/2020 11.32, David Hildenbrand wrote:
>>> On 22.01.20 11:31, Thomas Huth wrote:
>>>> On 22/01/2020 11.22, David Hildenbrand wrote:
>>>>> On 22.01.20 11:10, David Hildenbrand wrote:
>>>>>> On 20.01.20 19:42, Claudio Imbrenda wrote:
>>>>>>> SCLP unit test. Testing the following:
>>>>>>>
>>>>>>> * Correctly ignoring instruction bits that should be ignored
>>>>>>> * Privileged instruction check
>>>>>>> * Check for addressing exceptions
>>>>>>> * Specification exceptions:
>>>>>>>   - SCCB size less than 8
>>>>>>>   - SCCB unaligned
>>>>>>>   - SCCB overlaps prefix or lowcore
>>>>>>>   - SCCB address higher than 2GB
>>>>>>> * Return codes for
>>>>>>>   - Invalid command
>>>>>>>   - SCCB too short (but at least 8)
>>>>>>>   - SCCB page boundary violation
>>>>>>>
>>>>>>> Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
>>>>>>> Reviewed-by: Thomas Huth <thuth@redhat.com>
>>>>>>> Acked-by: Janosch Frank <frankja@linux.ibm.com>
>>>>>>> ---
>>>>>>>  s390x/Makefile      |   1 +
>>>>>>>  s390x/sclp.c        | 474 ++++++++++++++++++++++++++++++++++++++++++++
>>>>>>>  s390x/unittests.cfg |   8 +
>>>>>>>  3 files changed, 483 insertions(+)
>>>>>>>  create mode 100644 s390x/sclp.c
>>>>>>>
>>>>>>> diff --git a/s390x/Makefile b/s390x/Makefile
>>>>>>> index 3744372..ddb4b48 100644
>>>>>>> --- a/s390x/Makefile
>>>>>>> +++ b/s390x/Makefile
>>>>>>> @@ -16,6 +16,7 @@ tests += $(TEST_DIR)/diag288.elf
>>>>>>>  tests += $(TEST_DIR)/stsi.elf
>>>>>>>  tests += $(TEST_DIR)/skrf.elf
>>>>>>>  tests += $(TEST_DIR)/smp.elf
>>>>>>> +tests += $(TEST_DIR)/sclp.elf
>>>>>>>  tests_binary = $(patsubst %.elf,%.bin,$(tests))
>>>>>>>  
>>>>>>>  all: directories test_cases test_cases_binary
>>>>>>> diff --git a/s390x/sclp.c b/s390x/sclp.c
>>>>>>> new file mode 100644
>>>>>>> index 0000000..215347e
>>>>>>> --- /dev/null
>>>>>>> +++ b/s390x/sclp.c
>>>>>>> @@ -0,0 +1,474 @@
>>>>>>> +/*
>>>>>>> + * Service Call tests
>>>>>>> + *
>>>>>>> + * Copyright (c) 2019 IBM Corp
>>>>>>> + *
>>>>>>> + * Authors:
>>>>>>> + *  Claudio Imbrenda <imbrenda@linux.ibm.com>
>>>>>>> + *
>>>>>>> + * This code is free software; you can redistribute it and/or modify it
>>>>>>> + * under the terms of the GNU General Public License version 2.
>>>>>>> + */
>>>>>>> +
>>>>>>> +#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 */
>>>>>>> +static uint8_t sccb_template[PAGE_SIZE];				/* SCCB template to be used */
>>>>>>> +static uint32_t valid_code;						/* valid command code for READ SCP INFO */
>>>>>>> +static struct lowcore *lc;
>>>>>>> +
>>>>>>> +/**
>>>>>>> + * 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 (lc->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, 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, 0, sizeof(sccb_template));
>>>>>>> +	((SCCBHeader *)sccb_template)->length = sccb_len;
>>>>>>> +	return test_one_sccb(cmd, addr, buf_len, exp_pgm, exp_rc);
>>>>>>
>>>>>> Doing a fresh ./configure + make on RHEL7 gives me
>>>>>>
>>>>>> [linux1@rhkvm01 kvm-unit-tests]$ make
>>>>>> gcc  -std=gnu99 -ffreestanding -I /home/linux1/git/kvm-unit-tests/lib -I /home/linux1/git/kvm-unit-tests/lib/s390x -I lib -O2 -march=zEC12 -fno-delete-null-pointer-checks -g -MMD -MF s390x/.sclp.d -Wall -Wwrite-strings -Wempty-body -Wuninitialized -Wignored-qualifiers -Werror  -fomit-frame-pointer    -Wno-frame-address   -fno-pic    -Wclobbered  -Wunused-but-set-parameter  -Wmissing-parameter-type  -Wold-style-declaration -Woverride-init -Wmissing-prototypes -Wstrict-prototypes   -c -o s390x/sclp.o s390x/sclp.c
>>>>>> s390x/sclp.c: In function 'test_one_simple':
>>>>>> s390x/sclp.c:121:2: error: dereferencing type-punned pointer will break strict-aliasing rules [-Werror=strict-aliasing]
>>>>>>   ((SCCBHeader *)sccb_template)->length = sccb_len;
>>>>>>   ^
>>>>>> s390x/sclp.c: At top level:
>>>>>> cc1: error: unrecognized command line option "-Wno-frame-address" [-Werror]
>>>>>> cc1: all warnings being treated as errors
>>>>>> make: *** [s390x/sclp.o] Error 1
>>>>>
>>>>> The following makes it work:
>>>>>
>>>>>
>>>>> diff --git a/s390x/sclp.c b/s390x/sclp.c
>>>>> index c13fa60..0b8117a 100644
>>>>> --- a/s390x/sclp.c
>>>>> +++ b/s390x/sclp.c
>>>>> @@ -117,8 +117,10 @@ static bool test_one_ro(uint32_t cmd, uint8_t *addr, uint64_t exp_pgm, uint16_t
>>>>>  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)
>>>>>  {
>>>>> +       SCCBHeader *header = (void *)sccb_template;
>>>>> +
>>>>>         memset(sccb_template, 0, sizeof(sccb_template));
>>>>> -       ((SCCBHeader *)sccb_template)->length = sccb_len;
>>>>> +       header->length = sccb_len;
>>>>
>>>> While that might silence the compiler warning, we still might get
>>>> aliasing problems here, I think.
>>>> The right way to solve this problem is to turn sccb_template into a
>>>> union of the various structs/arrays that you want to use and then access
>>>> the fields through the union instead ("type-punning through union").
>>>
>>> We do have the exact same thing in lib/s390x/sclp.c already, no?
>>
>> Maybe we should carefully check that code, too...
>>
>>> Especially, new compilers don't seem to care?
>>
>> I've seen horrible bugs due to these aliasing problems in the past -
>> without compiler warnings showing up! Certain versions of GCC assume
>> that they can re-order code with pointers that point to types of
>> different sizes, i.e. in the above example, I think they could assume
>> that they could re-order the memset() and the header->length = ... line.
>> I'd feel better if we play safe and use a union here.
> 
> Should we simply allow type-punning?

For now I squashed

diff --git a/s390x/sclp.c b/s390x/sclp.c
index c13fa60..7d92bf3 100644
--- a/s390x/sclp.c
+++ b/s390x/sclp.c
@@ -26,7 +26,12 @@

 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 */
-static uint8_t sccb_template[PAGE_SIZE];				/* SCCB template to be used */
+/* 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 */
 static struct lowcore *lc;

@@ -69,7 +74,7 @@ static bool test_one_sccb(uint32_t cmd, uint8_t *addr,
uint16_t buf_len, uint64_

 	/* Copy the template to the test address if needed */
 	if (buf_len)
-		memcpy(addr, sccb_template, buf_len);
+		memcpy(addr, sccb_template.raw, buf_len);
 	if (exp_pgm != PGM_NONE)
 		expect_pgm_int();
 	/* perform the actual call */
@@ -117,8 +122,8 @@ static bool test_one_ro(uint32_t cmd, uint8_t *addr,
uint64_t exp_pgm, uint16_t
 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, 0, sizeof(sccb_template));
-	((SCCBHeader *)sccb_template)->length = sccb_len;
+	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);
 }

@@ -299,10 +304,10 @@ 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 = (WriteEventData *)sccb_template;
+	WriteEventData *sccb = &sccb_template.data;
 	int len, offset;

-	memset(sccb_template, 0, sizeof(sccb_template));
+	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;
@@ -323,10 +328,10 @@ 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 = (WriteEventData *)sccb_template;
+	WriteEventData *sccb = &sccb_template.data;
 	int len;

-	memset(sccb_template, 0, sizeof(sccb_template));
+	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;
Thomas Huth Jan. 22, 2020, 12:13 p.m. UTC | #9
On 22/01/2020 12.20, David Hildenbrand wrote:
> On 22.01.20 11:40, David Hildenbrand wrote:
>> On 22.01.20 11:39, Thomas Huth wrote:
>>> On 22/01/2020 11.32, David Hildenbrand wrote:
>>>> On 22.01.20 11:31, Thomas Huth wrote:
>>>>> On 22/01/2020 11.22, David Hildenbrand wrote:
>>>>>> On 22.01.20 11:10, David Hildenbrand wrote:
>>>>>>> On 20.01.20 19:42, Claudio Imbrenda wrote:
>>>>>>>> SCLP unit test. Testing the following:
>>>>>>>>
>>>>>>>> * Correctly ignoring instruction bits that should be ignored
>>>>>>>> * Privileged instruction check
>>>>>>>> * Check for addressing exceptions
>>>>>>>> * Specification exceptions:
>>>>>>>>   - SCCB size less than 8
>>>>>>>>   - SCCB unaligned
>>>>>>>>   - SCCB overlaps prefix or lowcore
>>>>>>>>   - SCCB address higher than 2GB
>>>>>>>> * Return codes for
>>>>>>>>   - Invalid command
>>>>>>>>   - SCCB too short (but at least 8)
>>>>>>>>   - SCCB page boundary violation
>>>>>>>>
>>>>>>>> Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
>>>>>>>> Reviewed-by: Thomas Huth <thuth@redhat.com>
>>>>>>>> Acked-by: Janosch Frank <frankja@linux.ibm.com>
>>>>>>>> ---
>>>>>>>>  s390x/Makefile      |   1 +
>>>>>>>>  s390x/sclp.c        | 474 ++++++++++++++++++++++++++++++++++++++++++++
>>>>>>>>  s390x/unittests.cfg |   8 +
>>>>>>>>  3 files changed, 483 insertions(+)
>>>>>>>>  create mode 100644 s390x/sclp.c
>>>>>>>>
>>>>>>>> diff --git a/s390x/Makefile b/s390x/Makefile
>>>>>>>> index 3744372..ddb4b48 100644
>>>>>>>> --- a/s390x/Makefile
>>>>>>>> +++ b/s390x/Makefile
>>>>>>>> @@ -16,6 +16,7 @@ tests += $(TEST_DIR)/diag288.elf
>>>>>>>>  tests += $(TEST_DIR)/stsi.elf
>>>>>>>>  tests += $(TEST_DIR)/skrf.elf
>>>>>>>>  tests += $(TEST_DIR)/smp.elf
>>>>>>>> +tests += $(TEST_DIR)/sclp.elf
>>>>>>>>  tests_binary = $(patsubst %.elf,%.bin,$(tests))
>>>>>>>>  
>>>>>>>>  all: directories test_cases test_cases_binary
>>>>>>>> diff --git a/s390x/sclp.c b/s390x/sclp.c
>>>>>>>> new file mode 100644
>>>>>>>> index 0000000..215347e
>>>>>>>> --- /dev/null
>>>>>>>> +++ b/s390x/sclp.c
>>>>>>>> @@ -0,0 +1,474 @@
>>>>>>>> +/*
>>>>>>>> + * Service Call tests
>>>>>>>> + *
>>>>>>>> + * Copyright (c) 2019 IBM Corp
>>>>>>>> + *
>>>>>>>> + * Authors:
>>>>>>>> + *  Claudio Imbrenda <imbrenda@linux.ibm.com>
>>>>>>>> + *
>>>>>>>> + * This code is free software; you can redistribute it and/or modify it
>>>>>>>> + * under the terms of the GNU General Public License version 2.
>>>>>>>> + */
>>>>>>>> +
>>>>>>>> +#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 */
>>>>>>>> +static uint8_t sccb_template[PAGE_SIZE];				/* SCCB template to be used */
>>>>>>>> +static uint32_t valid_code;						/* valid command code for READ SCP INFO */
>>>>>>>> +static struct lowcore *lc;
>>>>>>>> +
>>>>>>>> +/**
>>>>>>>> + * 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 (lc->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, 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, 0, sizeof(sccb_template));
>>>>>>>> +	((SCCBHeader *)sccb_template)->length = sccb_len;
>>>>>>>> +	return test_one_sccb(cmd, addr, buf_len, exp_pgm, exp_rc);
>>>>>>>
>>>>>>> Doing a fresh ./configure + make on RHEL7 gives me
>>>>>>>
>>>>>>> [linux1@rhkvm01 kvm-unit-tests]$ make
>>>>>>> gcc  -std=gnu99 -ffreestanding -I /home/linux1/git/kvm-unit-tests/lib -I /home/linux1/git/kvm-unit-tests/lib/s390x -I lib -O2 -march=zEC12 -fno-delete-null-pointer-checks -g -MMD -MF s390x/.sclp.d -Wall -Wwrite-strings -Wempty-body -Wuninitialized -Wignored-qualifiers -Werror  -fomit-frame-pointer    -Wno-frame-address   -fno-pic    -Wclobbered  -Wunused-but-set-parameter  -Wmissing-parameter-type  -Wold-style-declaration -Woverride-init -Wmissing-prototypes -Wstrict-prototypes   -c -o s390x/sclp.o s390x/sclp.c
>>>>>>> s390x/sclp.c: In function 'test_one_simple':
>>>>>>> s390x/sclp.c:121:2: error: dereferencing type-punned pointer will break strict-aliasing rules [-Werror=strict-aliasing]
>>>>>>>   ((SCCBHeader *)sccb_template)->length = sccb_len;
>>>>>>>   ^
>>>>>>> s390x/sclp.c: At top level:
>>>>>>> cc1: error: unrecognized command line option "-Wno-frame-address" [-Werror]
>>>>>>> cc1: all warnings being treated as errors
>>>>>>> make: *** [s390x/sclp.o] Error 1
>>>>>>
>>>>>> The following makes it work:
>>>>>>
>>>>>>
>>>>>> diff --git a/s390x/sclp.c b/s390x/sclp.c
>>>>>> index c13fa60..0b8117a 100644
>>>>>> --- a/s390x/sclp.c
>>>>>> +++ b/s390x/sclp.c
>>>>>> @@ -117,8 +117,10 @@ static bool test_one_ro(uint32_t cmd, uint8_t *addr, uint64_t exp_pgm, uint16_t
>>>>>>  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)
>>>>>>  {
>>>>>> +       SCCBHeader *header = (void *)sccb_template;
>>>>>> +
>>>>>>         memset(sccb_template, 0, sizeof(sccb_template));
>>>>>> -       ((SCCBHeader *)sccb_template)->length = sccb_len;
>>>>>> +       header->length = sccb_len;
>>>>>
>>>>> While that might silence the compiler warning, we still might get
>>>>> aliasing problems here, I think.
>>>>> The right way to solve this problem is to turn sccb_template into a
>>>>> union of the various structs/arrays that you want to use and then access
>>>>> the fields through the union instead ("type-punning through union").
>>>>
>>>> We do have the exact same thing in lib/s390x/sclp.c already, no?
>>>
>>> Maybe we should carefully check that code, too...
>>>
>>>> Especially, new compilers don't seem to care?
>>>
>>> I've seen horrible bugs due to these aliasing problems in the past -
>>> without compiler warnings showing up! Certain versions of GCC assume
>>> that they can re-order code with pointers that point to types of
>>> different sizes, i.e. in the above example, I think they could assume
>>> that they could re-order the memset() and the header->length = ... line.
>>> I'd feel better if we play safe and use a union here.
>>
>> Should we simply allow type-punning?
> 
> For now I squashed
> 
> diff --git a/s390x/sclp.c b/s390x/sclp.c
> index c13fa60..7d92bf3 100644
> --- a/s390x/sclp.c
> +++ b/s390x/sclp.c
> @@ -26,7 +26,12 @@
> 
>  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 */
> -static uint8_t sccb_template[PAGE_SIZE];				/* SCCB template to be used */
> +/* 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 */
>  static struct lowcore *lc;
> 
> @@ -69,7 +74,7 @@ static bool test_one_sccb(uint32_t cmd, uint8_t *addr,
> uint16_t buf_len, uint64_
> 
>  	/* Copy the template to the test address if needed */
>  	if (buf_len)
> -		memcpy(addr, sccb_template, buf_len);
> +		memcpy(addr, sccb_template.raw, buf_len);
>  	if (exp_pgm != PGM_NONE)
>  		expect_pgm_int();
>  	/* perform the actual call */
> @@ -117,8 +122,8 @@ static bool test_one_ro(uint32_t cmd, uint8_t *addr,
> uint64_t exp_pgm, uint16_t
>  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, 0, sizeof(sccb_template));
> -	((SCCBHeader *)sccb_template)->length = sccb_len;
> +	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);
>  }
> 
> @@ -299,10 +304,10 @@ 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 = (WriteEventData *)sccb_template;
> +	WriteEventData *sccb = &sccb_template.data;
>  	int len, offset;
> 
> -	memset(sccb_template, 0, sizeof(sccb_template));
> +	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;
> @@ -323,10 +328,10 @@ 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 = (WriteEventData *)sccb_template;
> +	WriteEventData *sccb = &sccb_template.data;
>  	int len;
> 
> -	memset(sccb_template, 0, sizeof(sccb_template));
> +	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;

Thanks, that looks better now!

 Thomas
Thomas Huth Jan. 22, 2020, 12:16 p.m. UTC | #10
On 22/01/2020 11.40, David Hildenbrand wrote:
> On 22.01.20 11:39, Thomas Huth wrote:
>> On 22/01/2020 11.32, David Hildenbrand wrote:
>>> On 22.01.20 11:31, Thomas Huth wrote:
>>>> On 22/01/2020 11.22, David Hildenbrand wrote:
>>>>> On 22.01.20 11:10, David Hildenbrand wrote:
[...]
>>>>>> Doing a fresh ./configure + make on RHEL7 gives me
>>>>>>
>>>>>> [linux1@rhkvm01 kvm-unit-tests]$ make
>>>>>> gcc  -std=gnu99 -ffreestanding -I /home/linux1/git/kvm-unit-tests/lib -I /home/linux1/git/kvm-unit-tests/lib/s390x -I lib -O2 -march=zEC12 -fno-delete-null-pointer-checks -g -MMD -MF s390x/.sclp.d -Wall -Wwrite-strings -Wempty-body -Wuninitialized -Wignored-qualifiers -Werror  -fomit-frame-pointer    -Wno-frame-address   -fno-pic    -Wclobbered  -Wunused-but-set-parameter  -Wmissing-parameter-type  -Wold-style-declaration -Woverride-init -Wmissing-prototypes -Wstrict-prototypes   -c -o s390x/sclp.o s390x/sclp.c
>>>>>> s390x/sclp.c: In function 'test_one_simple':
>>>>>> s390x/sclp.c:121:2: error: dereferencing type-punned pointer will break strict-aliasing rules [-Werror=strict-aliasing]
>>>>>>   ((SCCBHeader *)sccb_template)->length = sccb_len;
>>>>>>   ^
>>>>>> s390x/sclp.c: At top level:
>>>>>> cc1: error: unrecognized command line option "-Wno-frame-address" [-Werror]
>>>>>> cc1: all warnings being treated as errors
>>>>>> make: *** [s390x/sclp.o] Error 1
>>>>>
>>>>> The following makes it work:
>>>>>
>>>>>
>>>>> diff --git a/s390x/sclp.c b/s390x/sclp.c
>>>>> index c13fa60..0b8117a 100644
>>>>> --- a/s390x/sclp.c
>>>>> +++ b/s390x/sclp.c
>>>>> @@ -117,8 +117,10 @@ static bool test_one_ro(uint32_t cmd, uint8_t *addr, uint64_t exp_pgm, uint16_t
>>>>>  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)
>>>>>  {
>>>>> +       SCCBHeader *header = (void *)sccb_template;
>>>>> +
>>>>>         memset(sccb_template, 0, sizeof(sccb_template));
>>>>> -       ((SCCBHeader *)sccb_template)->length = sccb_len;
>>>>> +       header->length = sccb_len;
>>>>
>>>> While that might silence the compiler warning, we still might get
>>>> aliasing problems here, I think.
>>>> The right way to solve this problem is to turn sccb_template into a
>>>> union of the various structs/arrays that you want to use and then access
>>>> the fields through the union instead ("type-punning through union").
>>>
>>> We do have the exact same thing in lib/s390x/sclp.c already, no?
>>
>> Maybe we should carefully check that code, too...
>>
>>> Especially, new compilers don't seem to care?
>>
>> I've seen horrible bugs due to these aliasing problems in the past -
>> without compiler warnings showing up! Certain versions of GCC assume
>> that they can re-order code with pointers that point to types of
>> different sizes, i.e. in the above example, I think they could assume
>> that they could re-order the memset() and the header->length = ... line.
>> I'd feel better if we play safe and use a union here.
> 
> Should we simply allow type-punning?

Maybe yes. The kernel also compiles with "-fno-strict-aliasing", and
since kvm-unit-tests is mainly a "playground" for people who do kernel
development, too, we should maybe also compile the unit tests with
"-fno-strict-aliasing".

Paolo, Andrew, Laurent, what do you think?

 Thomas
Thomas Huth Jan. 22, 2020, 2:15 p.m. UTC | #11
On 22/01/2020 13.16, Thomas Huth wrote:
> On 22/01/2020 11.40, David Hildenbrand wrote:
>> On 22.01.20 11:39, Thomas Huth wrote:
>>> On 22/01/2020 11.32, David Hildenbrand wrote:
>>>> On 22.01.20 11:31, Thomas Huth wrote:
>>>>> On 22/01/2020 11.22, David Hildenbrand wrote:
>>>>>> On 22.01.20 11:10, David Hildenbrand wrote:
> [...]
>>>>>>> Doing a fresh ./configure + make on RHEL7 gives me
>>>>>>>
>>>>>>> [linux1@rhkvm01 kvm-unit-tests]$ make
>>>>>>> gcc  -std=gnu99 -ffreestanding -I /home/linux1/git/kvm-unit-tests/lib -I /home/linux1/git/kvm-unit-tests/lib/s390x -I lib -O2 -march=zEC12 -fno-delete-null-pointer-checks -g -MMD -MF s390x/.sclp.d -Wall -Wwrite-strings -Wempty-body -Wuninitialized -Wignored-qualifiers -Werror  -fomit-frame-pointer    -Wno-frame-address   -fno-pic    -Wclobbered  -Wunused-but-set-parameter  -Wmissing-parameter-type  -Wold-style-declaration -Woverride-init -Wmissing-prototypes -Wstrict-prototypes   -c -o s390x/sclp.o s390x/sclp.c
>>>>>>> s390x/sclp.c: In function 'test_one_simple':
>>>>>>> s390x/sclp.c:121:2: error: dereferencing type-punned pointer will break strict-aliasing rules [-Werror=strict-aliasing]
>>>>>>>   ((SCCBHeader *)sccb_template)->length = sccb_len;
>>>>>>>   ^
>>>>>>> s390x/sclp.c: At top level:
>>>>>>> cc1: error: unrecognized command line option "-Wno-frame-address" [-Werror]
>>>>>>> cc1: all warnings being treated as errors
>>>>>>> make: *** [s390x/sclp.o] Error 1
>>>>>>
>>>>>> The following makes it work:
>>>>>>
>>>>>>
>>>>>> diff --git a/s390x/sclp.c b/s390x/sclp.c
>>>>>> index c13fa60..0b8117a 100644
>>>>>> --- a/s390x/sclp.c
>>>>>> +++ b/s390x/sclp.c
>>>>>> @@ -117,8 +117,10 @@ static bool test_one_ro(uint32_t cmd, uint8_t *addr, uint64_t exp_pgm, uint16_t
>>>>>>  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)
>>>>>>  {
>>>>>> +       SCCBHeader *header = (void *)sccb_template;
>>>>>> +
>>>>>>         memset(sccb_template, 0, sizeof(sccb_template));
>>>>>> -       ((SCCBHeader *)sccb_template)->length = sccb_len;
>>>>>> +       header->length = sccb_len;
>>>>>
>>>>> While that might silence the compiler warning, we still might get
>>>>> aliasing problems here, I think.
>>>>> The right way to solve this problem is to turn sccb_template into a
>>>>> union of the various structs/arrays that you want to use and then access
>>>>> the fields through the union instead ("type-punning through union").
>>>>
>>>> We do have the exact same thing in lib/s390x/sclp.c already, no?
>>>
>>> Maybe we should carefully check that code, too...
>>>
>>>> Especially, new compilers don't seem to care?
>>>
>>> I've seen horrible bugs due to these aliasing problems in the past -
>>> without compiler warnings showing up! Certain versions of GCC assume
>>> that they can re-order code with pointers that point to types of
>>> different sizes, i.e. in the above example, I think they could assume
>>> that they could re-order the memset() and the header->length = ... line.
>>> I'd feel better if we play safe and use a union here.
>>
>> Should we simply allow type-punning?
> 
> Maybe yes. The kernel also compiles with "-fno-strict-aliasing", and
> since kvm-unit-tests is mainly a "playground" for people who do kernel
> development, too, we should maybe also compile the unit tests with
> "-fno-strict-aliasing".
> 
> Paolo, Andrew, Laurent, what do you think?

By the way, when compiling the x86 code with -O2 instead of -O1, I also get:

lib/x86/intel-iommu.c: In function ‘vtd_setup_msi’:
lib/x86/intel-iommu.c:324:4: error: dereferencing type-punned pointer
will break strict-aliasing rules [-Werror=strict-aliasing]
   *(uint64_t *)&msi_addr, *(uint32_t *)&msi_data);
    ^~~~~~~~~~~~~~~~~~~~~
lib/x86/intel-iommu.c:324:28: error: dereferencing type-punned pointer
will break strict-aliasing rules [-Werror=strict-aliasing]
   *(uint64_t *)&msi_addr, *(uint32_t *)&msi_data);
                            ^~~~~~~~~~~~~~~~~~~~~
lib/x86/intel-iommu.c:326:29: error: dereferencing type-punned pointer
will break strict-aliasing rules [-Werror=strict-aliasing]
  return pci_setup_msi(dev, *(uint64_t *)&msi_addr,
                             ^~~~~~~~~~~~~~~~~~~~~
lib/x86/intel-iommu.c:327:10: error: dereferencing type-punned pointer
will break strict-aliasing rules [-Werror=strict-aliasing]
         *(uint32_t *)&msi_data);
          ^~~~~~~~~~~~~~~~~~~~~
cc1: all warnings being treated as errors

... so maybe -fno-strict-aliasing would be a good idea for that, too?

 Thomas
Paolo Bonzini Jan. 22, 2020, 2:42 p.m. UTC | #12
On 22/01/20 15:15, Thomas Huth wrote:
> On 22/01/2020 13.16, Thomas Huth wrote:
>> On 22/01/2020 11.40, David Hildenbrand wrote:
>>> On 22.01.20 11:39, Thomas Huth wrote:
>>>> On 22/01/2020 11.32, David Hildenbrand wrote:
>>>>> On 22.01.20 11:31, Thomas Huth wrote:
>>>>>> On 22/01/2020 11.22, David Hildenbrand wrote:
>>>>>>> On 22.01.20 11:10, David Hildenbrand wrote:
>> [...]
>>>>>>>> Doing a fresh ./configure + make on RHEL7 gives me
>>>>>>>>
>>>>>>>> [linux1@rhkvm01 kvm-unit-tests]$ make
>>>>>>>> gcc  -std=gnu99 -ffreestanding -I /home/linux1/git/kvm-unit-tests/lib -I /home/linux1/git/kvm-unit-tests/lib/s390x -I lib -O2 -march=zEC12 -fno-delete-null-pointer-checks -g -MMD -MF s390x/.sclp.d -Wall -Wwrite-strings -Wempty-body -Wuninitialized -Wignored-qualifiers -Werror  -fomit-frame-pointer    -Wno-frame-address   -fno-pic    -Wclobbered  -Wunused-but-set-parameter  -Wmissing-parameter-type  -Wold-style-declaration -Woverride-init -Wmissing-prototypes -Wstrict-prototypes   -c -o s390x/sclp.o s390x/sclp.c
>>>>>>>> s390x/sclp.c: In function 'test_one_simple':
>>>>>>>> s390x/sclp.c:121:2: error: dereferencing type-punned pointer will break strict-aliasing rules [-Werror=strict-aliasing]
>>>>>>>>   ((SCCBHeader *)sccb_template)->length = sccb_len;
>>>>>>>>   ^
>>>>>>>> s390x/sclp.c: At top level:
>>>>>>>> cc1: error: unrecognized command line option "-Wno-frame-address" [-Werror]
>>>>>>>> cc1: all warnings being treated as errors
>>>>>>>> make: *** [s390x/sclp.o] Error 1
>>>>>>>
>>>>>>> The following makes it work:
>>>>>>>
>>>>>>>
>>>>>>> diff --git a/s390x/sclp.c b/s390x/sclp.c
>>>>>>> index c13fa60..0b8117a 100644
>>>>>>> --- a/s390x/sclp.c
>>>>>>> +++ b/s390x/sclp.c
>>>>>>> @@ -117,8 +117,10 @@ static bool test_one_ro(uint32_t cmd, uint8_t *addr, uint64_t exp_pgm, uint16_t
>>>>>>>  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)
>>>>>>>  {
>>>>>>> +       SCCBHeader *header = (void *)sccb_template;
>>>>>>> +
>>>>>>>         memset(sccb_template, 0, sizeof(sccb_template));
>>>>>>> -       ((SCCBHeader *)sccb_template)->length = sccb_len;
>>>>>>> +       header->length = sccb_len;
>>>>>>
>>>>>> While that might silence the compiler warning, we still might get
>>>>>> aliasing problems here, I think.
>>>>>> The right way to solve this problem is to turn sccb_template into a
>>>>>> union of the various structs/arrays that you want to use and then access
>>>>>> the fields through the union instead ("type-punning through union").
>>>>>
>>>>> We do have the exact same thing in lib/s390x/sclp.c already, no?
>>>>
>>>> Maybe we should carefully check that code, too...
>>>>
>>>>> Especially, new compilers don't seem to care?
>>>>
>>>> I've seen horrible bugs due to these aliasing problems in the past -
>>>> without compiler warnings showing up! Certain versions of GCC assume
>>>> that they can re-order code with pointers that point to types of
>>>> different sizes, i.e. in the above example, I think they could assume
>>>> that they could re-order the memset() and the header->length = ... line.
>>>> I'd feel better if we play safe and use a union here.
>>>
>>> Should we simply allow type-punning?
>>
>> Maybe yes. The kernel also compiles with "-fno-strict-aliasing", and
>> since kvm-unit-tests is mainly a "playground" for people who do kernel
>> development, too, we should maybe also compile the unit tests with
>> "-fno-strict-aliasing".
>>
>> Paolo, Andrew, Laurent, what do you think?

I think enabling -fno-strict-aliasing is a good idea.

Paolo
Laurent Vivier Jan. 22, 2020, 3:01 p.m. UTC | #13
On 22/01/2020 15:42, Paolo Bonzini wrote:
> On 22/01/20 15:15, Thomas Huth wrote:
>> On 22/01/2020 13.16, Thomas Huth wrote:
>>> On 22/01/2020 11.40, David Hildenbrand wrote:
>>>> On 22.01.20 11:39, Thomas Huth wrote:
>>>>> On 22/01/2020 11.32, David Hildenbrand wrote:
>>>>>> On 22.01.20 11:31, Thomas Huth wrote:
>>>>>>> On 22/01/2020 11.22, David Hildenbrand wrote:
>>>>>>>> On 22.01.20 11:10, David Hildenbrand wrote:
>>> [...]
>>>>>>>>> Doing a fresh ./configure + make on RHEL7 gives me
>>>>>>>>>
>>>>>>>>> [linux1@rhkvm01 kvm-unit-tests]$ make
>>>>>>>>> gcc  -std=gnu99 -ffreestanding -I /home/linux1/git/kvm-unit-tests/lib -I /home/linux1/git/kvm-unit-tests/lib/s390x -I lib -O2 -march=zEC12 -fno-delete-null-pointer-checks -g -MMD -MF s390x/.sclp.d -Wall -Wwrite-strings -Wempty-body -Wuninitialized -Wignored-qualifiers -Werror  -fomit-frame-pointer    -Wno-frame-address   -fno-pic    -Wclobbered  -Wunused-but-set-parameter  -Wmissing-parameter-type  -Wold-style-declaration -Woverride-init -Wmissing-prototypes -Wstrict-prototypes   -c -o s390x/sclp.o s390x/sclp.c
>>>>>>>>> s390x/sclp.c: In function 'test_one_simple':
>>>>>>>>> s390x/sclp.c:121:2: error: dereferencing type-punned pointer will break strict-aliasing rules [-Werror=strict-aliasing]
>>>>>>>>>   ((SCCBHeader *)sccb_template)->length = sccb_len;
>>>>>>>>>   ^
>>>>>>>>> s390x/sclp.c: At top level:
>>>>>>>>> cc1: error: unrecognized command line option "-Wno-frame-address" [-Werror]
>>>>>>>>> cc1: all warnings being treated as errors
>>>>>>>>> make: *** [s390x/sclp.o] Error 1
>>>>>>>>
>>>>>>>> The following makes it work:
>>>>>>>>
>>>>>>>>
>>>>>>>> diff --git a/s390x/sclp.c b/s390x/sclp.c
>>>>>>>> index c13fa60..0b8117a 100644
>>>>>>>> --- a/s390x/sclp.c
>>>>>>>> +++ b/s390x/sclp.c
>>>>>>>> @@ -117,8 +117,10 @@ static bool test_one_ro(uint32_t cmd, uint8_t *addr, uint64_t exp_pgm, uint16_t
>>>>>>>>  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)
>>>>>>>>  {
>>>>>>>> +       SCCBHeader *header = (void *)sccb_template;
>>>>>>>> +
>>>>>>>>         memset(sccb_template, 0, sizeof(sccb_template));
>>>>>>>> -       ((SCCBHeader *)sccb_template)->length = sccb_len;
>>>>>>>> +       header->length = sccb_len;
>>>>>>>
>>>>>>> While that might silence the compiler warning, we still might get
>>>>>>> aliasing problems here, I think.
>>>>>>> The right way to solve this problem is to turn sccb_template into a
>>>>>>> union of the various structs/arrays that you want to use and then access
>>>>>>> the fields through the union instead ("type-punning through union").
>>>>>>
>>>>>> We do have the exact same thing in lib/s390x/sclp.c already, no?
>>>>>
>>>>> Maybe we should carefully check that code, too...
>>>>>
>>>>>> Especially, new compilers don't seem to care?
>>>>>
>>>>> I've seen horrible bugs due to these aliasing problems in the past -
>>>>> without compiler warnings showing up! Certain versions of GCC assume
>>>>> that they can re-order code with pointers that point to types of
>>>>> different sizes, i.e. in the above example, I think they could assume
>>>>> that they could re-order the memset() and the header->length = ... line.
>>>>> I'd feel better if we play safe and use a union here.
>>>>
>>>> Should we simply allow type-punning?
>>>
>>> Maybe yes. The kernel also compiles with "-fno-strict-aliasing", and
>>> since kvm-unit-tests is mainly a "playground" for people who do kernel
>>> development, too, we should maybe also compile the unit tests with
>>> "-fno-strict-aliasing".
>>>
>>> Paolo, Andrew, Laurent, what do you think?
> 
> I think enabling -fno-strict-aliasing is a good idea.

I agree too

Laurent
diff mbox series

Patch

diff --git a/s390x/Makefile b/s390x/Makefile
index 3744372..ddb4b48 100644
--- a/s390x/Makefile
+++ b/s390x/Makefile
@@ -16,6 +16,7 @@  tests += $(TEST_DIR)/diag288.elf
 tests += $(TEST_DIR)/stsi.elf
 tests += $(TEST_DIR)/skrf.elf
 tests += $(TEST_DIR)/smp.elf
+tests += $(TEST_DIR)/sclp.elf
 tests_binary = $(patsubst %.elf,%.bin,$(tests))
 
 all: directories test_cases test_cases_binary
diff --git a/s390x/sclp.c b/s390x/sclp.c
new file mode 100644
index 0000000..215347e
--- /dev/null
+++ b/s390x/sclp.c
@@ -0,0 +1,474 @@ 
+/*
+ * Service Call tests
+ *
+ * Copyright (c) 2019 IBM Corp
+ *
+ * Authors:
+ *  Claudio Imbrenda <imbrenda@linux.ibm.com>
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2.
+ */
+
+#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 */
+static uint8_t sccb_template[PAGE_SIZE];				/* SCCB template to be used */
+static uint32_t valid_code;						/* valid command code for READ SCP INFO */
+static struct lowcore *lc;
+
+/**
+ * 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 (lc->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, 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, 0, sizeof(sccb_template));
+	((SCCBHeader *)sccb_template)->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 = (WriteEventData *)sccb_template;
+	int len, offset;
+
+	memset(sccb_template, 0, sizeof(sccb_template));
+	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 = (WriteEventData *)sccb_template;
+	int len;
+
+	memset(sccb_template, 0, sizeof(sccb_template));
+	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();
+}
diff --git a/s390x/unittests.cfg b/s390x/unittests.cfg
index f1b07cd..07013b2 100644
--- a/s390x/unittests.cfg
+++ b/s390x/unittests.cfg
@@ -75,3 +75,11 @@  file = stsi.elf
 [smp]
 file = smp.elf
 extra_params =-smp 2
+
+[sclp-1g]
+file = sclp.elf
+extra_params = -m 1G
+
+[sclp-3g]
+file = sclp.elf
+extra_params = -m 3G