When a channel is enabled we can start a SENSE command using the SSCH instruction to recognize the control unit and device. This tests the success of SSCH, the I/O interruption and the TSCH instructions. The test expect a device with a control unit type of 0xC0CA. Signed-off-by: Pierre Morel <pmorel@xxxxxxxxxxxxx> --- lib/s390x/css.h | 13 +++++ s390x/css.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 150 insertions(+) diff --git a/lib/s390x/css.h b/lib/s390x/css.h index 95dec72..59114c6 100644 --- a/lib/s390x/css.h +++ b/lib/s390x/css.h @@ -106,6 +106,19 @@ struct irb { uint32_t emw[8]; } __attribute__ ((aligned(4))); +#define CCW_CMD_SENSE_ID 0xe4 +#define PONG_CU 0xc0ca +struct senseid { + /* common part */ + uint8_t reserved; /* always 0x'FF' */ + uint16_t cu_type; /* control unit type */ + uint8_t cu_model; /* control unit model */ + uint16_t dev_type; /* device type */ + uint8_t dev_model; /* device model */ + uint8_t unused; /* padding byte */ + uint8_t padding[256 - 10]; /* Extra padding for CCW */ +} __attribute__ ((aligned(8))); + /* CSS low level access functions */ static inline int ssch(unsigned long schid, struct orb *addr) diff --git a/s390x/css.c b/s390x/css.c index e42dc2f..534864f 100644 --- a/s390x/css.c +++ b/s390x/css.c @@ -11,12 +11,28 @@ */ #include <libcflat.h> +#include <alloc_phys.h> +#include <asm/page.h> +#include <string.h> +#include <asm/interrupt.h> +#include <asm/arch_def.h> +#include <asm/clock.h> #include <css.h> #define SID_ONE 0x00010000 +#define PSW_PRG_MASK (PSW_MASK_IO | PSW_MASK_EA | PSW_MASK_BA) + +struct lowcore *lowcore = (void *)0x0; static struct schib schib; +#define NB_CCW 100 +static struct ccw ccw[NB_CCW]; +#define NB_ORB 100 +static struct orb orb[NB_ORB]; +static struct irb irb; +static char buffer[0x1000] __attribute__ ((aligned(8))); +static struct senseid senseid; static const char *Channel_type[3] = { "I/O", "CHSC", "MSG" @@ -24,6 +40,34 @@ static const char *Channel_type[3] = { static int test_device_sid; +static void delay(unsigned long ms) +{ + unsigned long startclk; + + startclk = get_clock_ms(); + for (;;) { + if (get_clock_ms() - startclk > ms) + break; + } +} + +static void set_io_irq_subclass_mask(uint64_t const new_mask) +{ + asm volatile ( + "lctlg %%c6, %%c6, %[source]\n" + : /* No outputs */ + : [source] "R" (new_mask)); +} + +static void set_system_mask(uint8_t new_mask) +{ + asm volatile ( + "ssm %[source]\n" + : /* No outputs */ + : [source] "R" (new_mask)); +} + + static void test_enumerate(void) { struct pmcw *pmcw = &schib.pmcw; @@ -88,12 +132,105 @@ static void test_enable(void) report("Tested", 1); } +static void enable_io_irq(void) +{ + /* Let's enable all ISCs for I/O interrupt */ + set_io_irq_subclass_mask(0x00000000ff000000); + set_system_mask(PSW_PRG_MASK >> 56); +} + +void handle_io_int(void) +{ + int ret = 0; + char *flags; + + report_prefix_push("Interrupt"); + if (lowcore->io_int_param != 0xcafec0ca) { + report("Bad io_int_param: %x", 0, lowcore->io_int_param); + report_prefix_pop(); + return; + } + report("io_int_param: %x", 1, lowcore->io_int_param); + report_prefix_pop(); + + ret = tsch(lowcore->subsys_id_word, &irb); + dump_irb(&irb); + flags = dump_scsw_flags(irb.scsw.ctrl); + + if (ret) + report("IRB scsw flags: %s", 0, flags); + else + report("IRB scsw flags: %s", 1, flags); + report_prefix_pop(); +} + +static int start_subchannel(int code, char *data, int count) +{ + int ret; + struct pmcw *p = &schib.pmcw; + struct orb *orb_p = &orb[0]; + + if (!test_device_sid) { + report_skip("No device"); + return 0; + } + ret = stsch(test_device_sid, &schib); + if (ret) { + report("Err %d on stsch on sid %08x", 0, ret, test_device_sid); + return 0; + } + if (!(p->flags & PMCW_ENABLE)) { + report_skip("Device (sid %08x) not enabled", test_device_sid); + return 0; + } + ccw[0].code = code ; + ccw[0].flags = CCW_F_PCI; + ccw[0].count = count; + ccw[0].data = (int)(unsigned long)data; + orb_p->intparm = 0xcafec0ca; + orb_p->ctrl = ORB_F_INIT_IRQ|ORB_F_FORMAT|ORB_F_LPM_DFLT; + orb_p->cpa = (unsigned int) (unsigned long)&ccw[0]; + + report_prefix_push("Start Subchannel"); + ret = ssch(test_device_sid, orb_p); + if (ret) { + report("ssch cc=%d", 0, ret); + report_prefix_pop(); + return 0; + } + report_prefix_pop(); + return 1; +} + +static void test_sense(void) +{ + int success; + + enable_io_irq(); + + success = start_subchannel(CCW_CMD_SENSE_ID, buffer, sizeof(senseid)); + if (!success) { + report("start_subchannel failed", 0); + return; + } + + senseid.cu_type = buffer[2] | (buffer[1] << 8); + delay(1000); + + /* Sense ID is non packed cut_type is at offset +1 byte */ + if (senseid.cu_type == PONG_CU) + report("cu_type: expect c0ca, got %04x", 1, senseid.cu_type); + else + report("cu_type: expect c0ca, got %04x", 0, senseid.cu_type); +} + static struct { const char *name; void (*func)(void); } tests[] = { { "enumerate (stsch)", test_enumerate }, { "enable (msch)", test_enable }, + { "sense (ssch/tsch)", test_sense }, { NULL, NULL } }; -- 2.17.0