s390/sclp: 32 bit event mask compatibility mode
authorClaudio Imbrenda <imbrenda@linux.vnet.ibm.com>
Tue, 23 Jan 2018 15:50:43 +0000 (16:50 +0100)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Thu, 22 Feb 2018 14:31:24 +0000 (15:31 +0100)
Qemu before version 2.11 does not implement the architecture correctly,
and does not allow for a mask size of size different than 4.

This patch introduces a compatibility mode for such systems, forcing
the mask sizes to 4.

Since the mask size is currently still 4 anyway, this patch should have
no impact whatsoever by itself, but it will be needed when the mask size
is increased to 64 bits in the next patch.

Reviewed-by: Heiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: Claudio Imbrenda <imbrenda@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
drivers/s390/char/sclp.c
drivers/s390/char/sclp.h
drivers/s390/char/sclp_early_core.c

index 59e3219..e9aa71c 100644 (file)
@@ -765,7 +765,10 @@ __sclp_make_init_req(sccb_mask_t receive_mask, sccb_mask_t send_mask)
        sclp_init_req.callback_data = NULL;
        sclp_init_req.sccb = sccb;
        sccb->header.length = sizeof(*sccb);
-       sccb->mask_length = sizeof(sccb_mask_t);
+       if (sclp_mask_compat_mode)
+               sccb->mask_length = SCLP_MASK_SIZE_COMPAT;
+       else
+               sccb->mask_length = sizeof(sccb_mask_t);
        sccb_set_recv_mask(sccb, receive_mask);
        sccb_set_send_mask(sccb, send_mask);
        sccb_set_sclp_recv_mask(sccb, 0);
@@ -977,12 +980,18 @@ sclp_check_interface(void)
                irq_subclass_unregister(IRQ_SUBCLASS_SERVICE_SIGNAL);
                spin_lock_irqsave(&sclp_lock, flags);
                del_timer(&sclp_request_timer);
-               if (sclp_init_req.status == SCLP_REQ_DONE &&
-                   sccb->header.response_code == 0x20) {
-                       rc = 0;
-                       break;
-               } else
-                       rc = -EBUSY;
+               rc = -EBUSY;
+               if (sclp_init_req.status == SCLP_REQ_DONE) {
+                       if (sccb->header.response_code == 0x20) {
+                               rc = 0;
+                               break;
+                       } else if (sccb->header.response_code == 0x74f0) {
+                               if (!sclp_mask_compat_mode) {
+                                       sclp_mask_compat_mode = true;
+                                       retry = 0;
+                               }
+                       }
+               }
        }
        unregister_external_irq(EXT_IRQ_SERVICE_SIG, sclp_check_handler);
        spin_unlock_irqrestore(&sclp_lock, flags);
index ee44d16..45e6ffd 100644 (file)
@@ -109,6 +109,8 @@ struct init_sccb {
         */
 } __attribute__((packed));
 
+#define SCLP_MASK_SIZE_COMPAT 4
+
 static inline sccb_mask_t sccb_get_mask(u8 *masks, size_t len, int i)
 {
        sccb_mask_t res = 0;
@@ -262,6 +264,7 @@ extern int sclp_init_state;
 extern int sclp_console_pages;
 extern int sclp_console_drop;
 extern unsigned long sclp_console_full;
+extern bool sclp_mask_compat_mode;
 
 extern char sclp_early_sccb[PAGE_SIZE];
 
index c8c5326..5f8d9ea 100644 (file)
 
 char sclp_early_sccb[PAGE_SIZE] __aligned(PAGE_SIZE) __section(.data);
 int sclp_init_state __section(.data) = sclp_init_state_uninitialized;
+/*
+ * Used to keep track of the size of the event masks. Qemu until version 2.11
+ * only supports 4 and needs a workaround.
+ */
+bool sclp_mask_compat_mode;
 
 void sclp_early_wait_irq(void)
 {
@@ -145,13 +150,21 @@ int sclp_early_set_event_mask(struct init_sccb *sccb,
                              sccb_mask_t receive_mask,
                              sccb_mask_t send_mask)
 {
+retry:
        memset(sccb, 0, sizeof(*sccb));
        sccb->header.length = sizeof(*sccb);
-       sccb->mask_length = sizeof(sccb_mask_t);
+       if (sclp_mask_compat_mode)
+               sccb->mask_length = SCLP_MASK_SIZE_COMPAT;
+       else
+               sccb->mask_length = sizeof(sccb_mask_t);
        sccb_set_recv_mask(sccb, receive_mask);
        sccb_set_send_mask(sccb, send_mask);
        if (sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_MASK, sccb))
                return -EIO;
+       if ((sccb->header.response_code == 0x74f0) && !sclp_mask_compat_mode) {
+               sclp_mask_compat_mode = true;
+               goto retry;
+       }
        if (sccb->header.response_code != 0x20)
                return -EIO;
        return 0;