root/drivers/s390/char/sclp_early_core.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. sclp_early_wait_irq
  2. sclp_early_cmd
  3. sclp_early_print_lm
  4. sclp_early_print_vt220
  5. sclp_early_set_event_mask
  6. sclp_early_con_check_linemode
  7. sclp_early_con_check_vt220
  8. sclp_early_setup
  9. __sclp_early_printk
  10. sclp_early_printk
  11. sclp_early_printk_force
  12. sclp_early_read_info
  13. sclp_early_get_info
  14. sclp_early_get_memsize
  15. sclp_early_get_hsa_size
  16. add_mem_detect_block
  17. sclp_early_read_storage_info

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  *    Copyright IBM Corp. 2015
   4  *    Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
   5  */
   6 
   7 #include <linux/kernel.h>
   8 #include <asm/processor.h>
   9 #include <asm/lowcore.h>
  10 #include <asm/ebcdic.h>
  11 #include <asm/irq.h>
  12 #include <asm/sections.h>
  13 #include <asm/mem_detect.h>
  14 #include "sclp.h"
  15 #include "sclp_rw.h"
  16 
  17 static struct read_info_sccb __bootdata(sclp_info_sccb);
  18 static int __bootdata(sclp_info_sccb_valid);
  19 char *sclp_early_sccb = (char *) EARLY_SCCB_OFFSET;
  20 int sclp_init_state __section(.data) = sclp_init_state_uninitialized;
  21 /*
  22  * Used to keep track of the size of the event masks. Qemu until version 2.11
  23  * only supports 4 and needs a workaround.
  24  */
  25 bool sclp_mask_compat_mode __section(.data);
  26 
  27 void sclp_early_wait_irq(void)
  28 {
  29         unsigned long psw_mask, addr;
  30         psw_t psw_ext_save, psw_wait;
  31         union ctlreg0 cr0, cr0_new;
  32 
  33         __ctl_store(cr0.val, 0, 0);
  34         cr0_new.val = cr0.val & ~CR0_IRQ_SUBCLASS_MASK;
  35         cr0_new.lap = 0;
  36         cr0_new.sssm = 1;
  37         __ctl_load(cr0_new.val, 0, 0);
  38 
  39         psw_ext_save = S390_lowcore.external_new_psw;
  40         psw_mask = __extract_psw();
  41         S390_lowcore.external_new_psw.mask = psw_mask;
  42         psw_wait.mask = psw_mask | PSW_MASK_EXT | PSW_MASK_WAIT;
  43         S390_lowcore.ext_int_code = 0;
  44 
  45         do {
  46                 asm volatile(
  47                         "       larl    %[addr],0f\n"
  48                         "       stg     %[addr],%[psw_wait_addr]\n"
  49                         "       stg     %[addr],%[psw_ext_addr]\n"
  50                         "       lpswe   %[psw_wait]\n"
  51                         "0:\n"
  52                         : [addr] "=&d" (addr),
  53                           [psw_wait_addr] "=Q" (psw_wait.addr),
  54                           [psw_ext_addr] "=Q" (S390_lowcore.external_new_psw.addr)
  55                         : [psw_wait] "Q" (psw_wait)
  56                         : "cc", "memory");
  57         } while (S390_lowcore.ext_int_code != EXT_IRQ_SERVICE_SIG);
  58 
  59         S390_lowcore.external_new_psw = psw_ext_save;
  60         __ctl_load(cr0.val, 0, 0);
  61 }
  62 
  63 int sclp_early_cmd(sclp_cmdw_t cmd, void *sccb)
  64 {
  65         unsigned long flags;
  66         int rc;
  67 
  68         raw_local_irq_save(flags);
  69         rc = sclp_service_call(cmd, sccb);
  70         if (rc)
  71                 goto out;
  72         sclp_early_wait_irq();
  73 out:
  74         raw_local_irq_restore(flags);
  75         return rc;
  76 }
  77 
  78 struct write_sccb {
  79         struct sccb_header header;
  80         struct msg_buf msg;
  81 } __packed;
  82 
  83 /* Output multi-line text using SCLP Message interface. */
  84 static void sclp_early_print_lm(const char *str, unsigned int len)
  85 {
  86         unsigned char *ptr, *end, ch;
  87         unsigned int count, offset;
  88         struct write_sccb *sccb;
  89         struct msg_buf *msg;
  90         struct mdb *mdb;
  91         struct mto *mto;
  92         struct go *go;
  93 
  94         sccb = (struct write_sccb *) sclp_early_sccb;
  95         end = (unsigned char *) sccb + EARLY_SCCB_SIZE - 1;
  96         memset(sccb, 0, sizeof(*sccb));
  97         ptr = (unsigned char *) &sccb->msg.mdb.mto;
  98         offset = 0;
  99         do {
 100                 for (count = sizeof(*mto); offset < len; count++) {
 101                         ch = str[offset++];
 102                         if ((ch == 0x0a) || (ptr + count > end))
 103                                 break;
 104                         ptr[count] = _ascebc[ch];
 105                 }
 106                 mto = (struct mto *) ptr;
 107                 memset(mto, 0, sizeof(*mto));
 108                 mto->length = count;
 109                 mto->type = 4;
 110                 mto->line_type_flags = LNTPFLGS_ENDTEXT;
 111                 ptr += count;
 112         } while ((offset < len) && (ptr + sizeof(*mto) <= end));
 113         len = ptr - (unsigned char *) sccb;
 114         sccb->header.length = len - offsetof(struct write_sccb, header);
 115         msg = &sccb->msg;
 116         msg->header.type = EVTYP_MSG;
 117         msg->header.length = len - offsetof(struct write_sccb, msg.header);
 118         mdb = &msg->mdb;
 119         mdb->header.type = 1;
 120         mdb->header.tag = 0xD4C4C240;
 121         mdb->header.revision_code = 1;
 122         mdb->header.length = len - offsetof(struct write_sccb, msg.mdb.header);
 123         go = &mdb->go;
 124         go->length = sizeof(*go);
 125         go->type = 1;
 126         sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_DATA, sccb);
 127 }
 128 
 129 struct vt220_sccb {
 130         struct sccb_header header;
 131         struct {
 132                 struct evbuf_header header;
 133                 char data[];
 134         } msg;
 135 } __packed;
 136 
 137 /* Output multi-line text using SCLP VT220 interface. */
 138 static void sclp_early_print_vt220(const char *str, unsigned int len)
 139 {
 140         struct vt220_sccb *sccb;
 141 
 142         sccb = (struct vt220_sccb *) sclp_early_sccb;
 143         if (sizeof(*sccb) + len >= EARLY_SCCB_SIZE)
 144                 len = EARLY_SCCB_SIZE - sizeof(*sccb);
 145         memset(sccb, 0, sizeof(*sccb));
 146         memcpy(&sccb->msg.data, str, len);
 147         sccb->header.length = sizeof(*sccb) + len;
 148         sccb->msg.header.length = sizeof(sccb->msg) + len;
 149         sccb->msg.header.type = EVTYP_VT220MSG;
 150         sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_DATA, sccb);
 151 }
 152 
 153 int sclp_early_set_event_mask(struct init_sccb *sccb,
 154                               sccb_mask_t receive_mask,
 155                               sccb_mask_t send_mask)
 156 {
 157 retry:
 158         memset(sccb, 0, sizeof(*sccb));
 159         sccb->header.length = sizeof(*sccb);
 160         if (sclp_mask_compat_mode)
 161                 sccb->mask_length = SCLP_MASK_SIZE_COMPAT;
 162         else
 163                 sccb->mask_length = sizeof(sccb_mask_t);
 164         sccb_set_recv_mask(sccb, receive_mask);
 165         sccb_set_send_mask(sccb, send_mask);
 166         if (sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_MASK, sccb))
 167                 return -EIO;
 168         if ((sccb->header.response_code == 0x74f0) && !sclp_mask_compat_mode) {
 169                 sclp_mask_compat_mode = true;
 170                 goto retry;
 171         }
 172         if (sccb->header.response_code != 0x20)
 173                 return -EIO;
 174         return 0;
 175 }
 176 
 177 unsigned int sclp_early_con_check_linemode(struct init_sccb *sccb)
 178 {
 179         if (!(sccb_get_sclp_send_mask(sccb) & EVTYP_OPCMD_MASK))
 180                 return 0;
 181         if (!(sccb_get_sclp_recv_mask(sccb) & (EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK)))
 182                 return 0;
 183         return 1;
 184 }
 185 
 186 unsigned int sclp_early_con_check_vt220(struct init_sccb *sccb)
 187 {
 188         if (sccb_get_sclp_send_mask(sccb) & EVTYP_VT220MSG_MASK)
 189                 return 1;
 190         return 0;
 191 }
 192 
 193 static int sclp_early_setup(int disable, int *have_linemode, int *have_vt220)
 194 {
 195         unsigned long receive_mask, send_mask;
 196         struct init_sccb *sccb;
 197         int rc;
 198 
 199         BUILD_BUG_ON(sizeof(struct init_sccb) > PAGE_SIZE);
 200 
 201         *have_linemode = *have_vt220 = 0;
 202         sccb = (struct init_sccb *) sclp_early_sccb;
 203         receive_mask = disable ? 0 : EVTYP_OPCMD_MASK;
 204         send_mask = disable ? 0 : EVTYP_VT220MSG_MASK | EVTYP_MSG_MASK;
 205         rc = sclp_early_set_event_mask(sccb, receive_mask, send_mask);
 206         if (rc)
 207                 return rc;
 208         *have_linemode = sclp_early_con_check_linemode(sccb);
 209         *have_vt220 = !!(sccb_get_send_mask(sccb) & EVTYP_VT220MSG_MASK);
 210         return rc;
 211 }
 212 
 213 /*
 214  * Output one or more lines of text on the SCLP console (VT220 and /
 215  * or line-mode).
 216  */
 217 void __sclp_early_printk(const char *str, unsigned int len, unsigned int force)
 218 {
 219         int have_linemode, have_vt220;
 220 
 221         if (!force && sclp_init_state != sclp_init_state_uninitialized)
 222                 return;
 223         if (sclp_early_setup(0, &have_linemode, &have_vt220) != 0)
 224                 return;
 225         if (have_linemode)
 226                 sclp_early_print_lm(str, len);
 227         if (have_vt220)
 228                 sclp_early_print_vt220(str, len);
 229         sclp_early_setup(1, &have_linemode, &have_vt220);
 230 }
 231 
 232 void sclp_early_printk(const char *str)
 233 {
 234         __sclp_early_printk(str, strlen(str), 0);
 235 }
 236 
 237 void sclp_early_printk_force(const char *str)
 238 {
 239         __sclp_early_printk(str, strlen(str), 1);
 240 }
 241 
 242 int __init sclp_early_read_info(void)
 243 {
 244         int i;
 245         struct read_info_sccb *sccb = &sclp_info_sccb;
 246         sclp_cmdw_t commands[] = {SCLP_CMDW_READ_SCP_INFO_FORCED,
 247                                   SCLP_CMDW_READ_SCP_INFO};
 248 
 249         for (i = 0; i < ARRAY_SIZE(commands); i++) {
 250                 memset(sccb, 0, sizeof(*sccb));
 251                 sccb->header.length = sizeof(*sccb);
 252                 sccb->header.function_code = 0x80;
 253                 sccb->header.control_mask[2] = 0x80;
 254                 if (sclp_early_cmd(commands[i], sccb))
 255                         break;
 256                 if (sccb->header.response_code == 0x10) {
 257                         sclp_info_sccb_valid = 1;
 258                         return 0;
 259                 }
 260                 if (sccb->header.response_code != 0x1f0)
 261                         break;
 262         }
 263         return -EIO;
 264 }
 265 
 266 int __init sclp_early_get_info(struct read_info_sccb *info)
 267 {
 268         if (!sclp_info_sccb_valid)
 269                 return -EIO;
 270 
 271         *info = sclp_info_sccb;
 272         return 0;
 273 }
 274 
 275 int __init sclp_early_get_memsize(unsigned long *mem)
 276 {
 277         unsigned long rnmax;
 278         unsigned long rnsize;
 279         struct read_info_sccb *sccb = &sclp_info_sccb;
 280 
 281         if (!sclp_info_sccb_valid)
 282                 return -EIO;
 283 
 284         rnmax = sccb->rnmax ? sccb->rnmax : sccb->rnmax2;
 285         rnsize = sccb->rnsize ? sccb->rnsize : sccb->rnsize2;
 286         rnsize <<= 20;
 287         *mem = rnsize * rnmax;
 288         return 0;
 289 }
 290 
 291 int __init sclp_early_get_hsa_size(unsigned long *hsa_size)
 292 {
 293         if (!sclp_info_sccb_valid)
 294                 return -EIO;
 295 
 296         *hsa_size = 0;
 297         if (sclp_info_sccb.hsa_size)
 298                 *hsa_size = (sclp_info_sccb.hsa_size - 1) * PAGE_SIZE;
 299         return 0;
 300 }
 301 
 302 #define SCLP_STORAGE_INFO_FACILITY     0x0000400000000000UL
 303 
 304 void __weak __init add_mem_detect_block(u64 start, u64 end) {}
 305 int __init sclp_early_read_storage_info(void)
 306 {
 307         struct read_storage_sccb *sccb = (struct read_storage_sccb *)sclp_early_sccb;
 308         int rc, id, max_id = 0;
 309         unsigned long rn, rzm;
 310         sclp_cmdw_t command;
 311         u16 sn;
 312 
 313         if (!sclp_info_sccb_valid)
 314                 return -EIO;
 315 
 316         if (!(sclp_info_sccb.facilities & SCLP_STORAGE_INFO_FACILITY))
 317                 return -EOPNOTSUPP;
 318 
 319         rzm = sclp_info_sccb.rnsize ?: sclp_info_sccb.rnsize2;
 320         rzm <<= 20;
 321 
 322         for (id = 0; id <= max_id; id++) {
 323                 memset(sclp_early_sccb, 0, EARLY_SCCB_SIZE);
 324                 sccb->header.length = EARLY_SCCB_SIZE;
 325                 command = SCLP_CMDW_READ_STORAGE_INFO | (id << 8);
 326                 rc = sclp_early_cmd(command, sccb);
 327                 if (rc)
 328                         goto fail;
 329 
 330                 max_id = sccb->max_id;
 331                 switch (sccb->header.response_code) {
 332                 case 0x0010:
 333                         for (sn = 0; sn < sccb->assigned; sn++) {
 334                                 if (!sccb->entries[sn])
 335                                         continue;
 336                                 rn = sccb->entries[sn] >> 16;
 337                                 add_mem_detect_block((rn - 1) * rzm, rn * rzm);
 338                         }
 339                         break;
 340                 case 0x0310:
 341                 case 0x0410:
 342                         break;
 343                 default:
 344                         goto fail;
 345                 }
 346         }
 347 
 348         return 0;
 349 fail:
 350         mem_detect.count = 0;
 351         return -EIO;
 352 }

/* [<][>][^][v][top][bottom][index][help] */