s390/tty3270: add support for diag 8c
authorSven Schnelle <svens@linux.ibm.com>
Thu, 17 Nov 2022 21:02:01 +0000 (22:02 +0100)
committerHeiko Carstens <hca@linux.ibm.com>
Mon, 9 Jan 2023 13:33:56 +0000 (14:33 +0100)
The current code uses diag210 to infer the 3270 geometry from the
model number when running on z/VM. This doesn't work well as almost
all 3270 software clients report as 3279-2 with a custom resolution.
tty3270 assumes it has a 80x24 terminal connected because of the -2
suffix. Use diag 8c to fetch the realy geometry from z/VM.

Note that this doesn't allow dynamic resizing, i.e. reconnecting to
a z/VM session with a different geometry.

Signed-off-by: Sven Schnelle <svens@linux.ibm.com>
Acked-by: Heiko Carstens <hca@linux.ibm.com>
Tested-by: Niklas Schnelle <schnelle@linux.ibm.com>
Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
arch/s390/include/asm/diag.h
arch/s390/kernel/diag.c
arch/s390/kernel/text_amode31.S
drivers/s390/char/raw3270.c

index 56e99c286d12e185bb1d3e8166733983093a62f7..9488b7ffb21cf875e9bd7509fcb4c21e3ca796d0 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/if_ether.h>
 #include <linux/percpu.h>
 #include <asm/asm-extable.h>
+#include <asm/cio.h>
 
 enum diag_stat_enum {
        DIAG_STAT_X008,
@@ -20,6 +21,7 @@ enum diag_stat_enum {
        DIAG_STAT_X014,
        DIAG_STAT_X044,
        DIAG_STAT_X064,
+       DIAG_STAT_X08C,
        DIAG_STAT_X09C,
        DIAG_STAT_X0DC,
        DIAG_STAT_X204,
@@ -83,6 +85,16 @@ struct diag210 {
 
 extern int diag210(struct diag210 *addr);
 
+struct diag8c {
+       u8 flags;
+       u8 num_partitions;
+       u16 width;
+       u16 height;
+       u8 data[0];
+} __packed __aligned(4);
+
+extern int diag8c(struct diag8c *out, struct ccw_dev_id *devno);
+
 /* bit is set in flags, when physical cpu info is included in diag 204 data */
 #define DIAG204_LPAR_PHYS_FLG 0x80
 #define DIAG204_LPAR_NAME_LEN 8                /* lpar name len in diag 204 data */
@@ -318,6 +330,7 @@ struct diag_ops {
        int (*diag210)(struct diag210 *addr);
        int (*diag26c)(void *req, void *resp, enum diag26c_sc subcode);
        int (*diag14)(unsigned long rx, unsigned long ry1, unsigned long subcode);
+       int (*diag8c)(struct diag8c *addr, struct ccw_dev_id *devno, size_t len);
        void (*diag0c)(struct hypfs_diag0c_entry *entry);
        void (*diag308_reset)(void);
 };
@@ -330,5 +343,6 @@ int _diag26c_amode31(void *req, void *resp, enum diag26c_sc subcode);
 int _diag14_amode31(unsigned long rx, unsigned long ry1, unsigned long subcode);
 void _diag0c_amode31(struct hypfs_diag0c_entry *entry);
 void _diag308_reset_amode31(void);
+int _diag8c_amode31(struct diag8c *addr, struct ccw_dev_id *devno, size_t len);
 
 #endif /* _ASM_S390_DIAG_H */
index a778714e4d8b511667c858d30f26d37e02c17a11..a6a0222a35cfc1373a068fa96f82517213fcb659 100644 (file)
@@ -35,6 +35,7 @@ static const struct diag_desc diag_map[NR_DIAG_STAT] = {
        [DIAG_STAT_X014] = { .code = 0x014, .name = "Spool File Services" },
        [DIAG_STAT_X044] = { .code = 0x044, .name = "Voluntary Timeslice End" },
        [DIAG_STAT_X064] = { .code = 0x064, .name = "NSS Manipulation" },
+       [DIAG_STAT_X08C] = { .code = 0x08c, .name = "Access 3270 Display Device Information" },
        [DIAG_STAT_X09C] = { .code = 0x09c, .name = "Relinquish Timeslice" },
        [DIAG_STAT_X0DC] = { .code = 0x0dc, .name = "Appldata Control" },
        [DIAG_STAT_X204] = { .code = 0x204, .name = "Logical-CPU Utilization" },
@@ -57,12 +58,16 @@ struct diag_ops __amode31_ref diag_amode31_ops = {
        .diag26c = _diag26c_amode31,
        .diag14 = _diag14_amode31,
        .diag0c = _diag0c_amode31,
+       .diag8c = _diag8c_amode31,
        .diag308_reset = _diag308_reset_amode31
 };
 
 static struct diag210 _diag210_tmp_amode31 __section(".amode31.data");
 struct diag210 __amode31_ref *__diag210_tmp_amode31 = &_diag210_tmp_amode31;
 
+static struct diag8c _diag8c_tmp_amode31 __section(".amode31.data");
+struct diag8c __amode31_ref *__diag8c_tmp_amode31 = &_diag8c_tmp_amode31;
+
 static int show_diag_stat(struct seq_file *m, void *v)
 {
        struct diag_stat *stat;
@@ -194,6 +199,27 @@ int diag210(struct diag210 *addr)
 }
 EXPORT_SYMBOL(diag210);
 
+/*
+ * Diagnose 210: Get information about a virtual device
+ */
+int diag8c(struct diag8c *addr, struct ccw_dev_id *devno)
+{
+       static DEFINE_SPINLOCK(diag8c_lock);
+       unsigned long flags;
+       int ccode;
+
+       spin_lock_irqsave(&diag8c_lock, flags);
+
+       diag_stat_inc(DIAG_STAT_X08C);
+       ccode = diag_amode31_ops.diag8c(__diag8c_tmp_amode31, devno, sizeof(*addr));
+
+       *addr = *__diag8c_tmp_amode31;
+       spin_unlock_irqrestore(&diag8c_lock, flags);
+
+       return ccode;
+}
+EXPORT_SYMBOL(diag8c);
+
 int diag224(void *ptr)
 {
        int rc = -EOPNOTSUPP;
index 2c8b14cc5556135115d15f888c27969c29fee256..e0f01ce251f54249d3661a0e0c6c1077d1199d1c 100644 (file)
@@ -62,6 +62,19 @@ ENTRY(_diag210_amode31)
        EX_TABLE_AMODE31(.Ldiag210_ex, .Ldiag210_fault)
 ENDPROC(_diag210_amode31)
 
+/*
+ * int diag8c(struct diag8c *addr, struct ccw_dev_id *devno, size_t len)
+*/
+ENTRY(_diag8c_amode31)
+       llgf    %r3,0(%r3)
+       sam31
+       diag    %r2,%r4,0x8c
+.Ldiag8c_ex:
+       sam64
+       lgfr    %r2,%r3
+       BR_EX_AMODE31_r14
+       EX_TABLE_AMODE31(.Ldiag8c_ex, .Ldiag8c_ex)
+ENDPROC(_diag8c_amode31)
 /*
  * int _diag26c_amode31(void *req, void *resp, enum diag26c_sc subcode)
  */
index fb3f62ac8be4ba839d7df4011a6c1d7cceaf7e28..8e6742184cc80f38e6f091619e0e306261b014b2 100644 (file)
@@ -421,8 +421,19 @@ raw3270_size_device_vm(struct raw3270 *rp)
        int rc, model;
        struct ccw_dev_id dev_id;
        struct diag210 diag_data;
+       struct diag8c diag8c_data;
 
        ccw_device_get_id(rp->cdev, &dev_id);
+       rc = diag8c(&diag8c_data, &dev_id);
+       if (!rc) {
+               rp->model = 2;
+               rp->rows = diag8c_data.height;
+               rp->cols = diag8c_data.width;
+               if (diag8c_data.flags & 1)
+                       set_bit(RAW3270_FLAGS_14BITADDR, &rp->flags);
+               return;
+       }
+
        diag_data.vrdcdvno = dev_id.devno;
        diag_data.vrdclen = sizeof(struct diag210);
        rc = diag210(&diag_data);