Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  *    Copyright IBM Corp. 2015
0004  *    Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
0005  */
0006 
0007 #include <linux/kernel.h>
0008 #include <asm/processor.h>
0009 #include <asm/lowcore.h>
0010 #include <asm/ebcdic.h>
0011 #include <asm/irq.h>
0012 #include <asm/sections.h>
0013 #include <asm/mem_detect.h>
0014 #include <asm/facility.h>
0015 #include "sclp.h"
0016 #include "sclp_rw.h"
0017 
0018 static struct read_info_sccb __bootdata(sclp_info_sccb);
0019 static int __bootdata(sclp_info_sccb_valid);
0020 char *__bootdata(sclp_early_sccb);
0021 int sclp_init_state = sclp_init_state_uninitialized;
0022 /*
0023  * Used to keep track of the size of the event masks. Qemu until version 2.11
0024  * only supports 4 and needs a workaround.
0025  */
0026 bool sclp_mask_compat_mode;
0027 
0028 void sclp_early_wait_irq(void)
0029 {
0030     unsigned long psw_mask, addr;
0031     psw_t psw_ext_save, psw_wait;
0032     union ctlreg0 cr0, cr0_new;
0033 
0034     __ctl_store(cr0.val, 0, 0);
0035     cr0_new.val = cr0.val & ~CR0_IRQ_SUBCLASS_MASK;
0036     cr0_new.lap = 0;
0037     cr0_new.sssm = 1;
0038     __ctl_load(cr0_new.val, 0, 0);
0039 
0040     psw_ext_save = S390_lowcore.external_new_psw;
0041     psw_mask = __extract_psw();
0042     S390_lowcore.external_new_psw.mask = psw_mask;
0043     psw_wait.mask = psw_mask | PSW_MASK_EXT | PSW_MASK_WAIT;
0044     S390_lowcore.ext_int_code = 0;
0045 
0046     do {
0047         asm volatile(
0048             "   larl    %[addr],0f\n"
0049             "   stg %[addr],%[psw_wait_addr]\n"
0050             "   stg %[addr],%[psw_ext_addr]\n"
0051             "   lpswe   %[psw_wait]\n"
0052             "0:\n"
0053             : [addr] "=&d" (addr),
0054               [psw_wait_addr] "=Q" (psw_wait.addr),
0055               [psw_ext_addr] "=Q" (S390_lowcore.external_new_psw.addr)
0056             : [psw_wait] "Q" (psw_wait)
0057             : "cc", "memory");
0058     } while (S390_lowcore.ext_int_code != EXT_IRQ_SERVICE_SIG);
0059 
0060     S390_lowcore.external_new_psw = psw_ext_save;
0061     __ctl_load(cr0.val, 0, 0);
0062 }
0063 
0064 int sclp_early_cmd(sclp_cmdw_t cmd, void *sccb)
0065 {
0066     unsigned long flags;
0067     int rc;
0068 
0069     flags = arch_local_irq_save();
0070     rc = sclp_service_call(cmd, sccb);
0071     if (rc)
0072         goto out;
0073     sclp_early_wait_irq();
0074 out:
0075     arch_local_irq_restore(flags);
0076     return rc;
0077 }
0078 
0079 struct write_sccb {
0080     struct sccb_header header;
0081     struct msg_buf msg;
0082 } __packed;
0083 
0084 /* Output multi-line text using SCLP Message interface. */
0085 static void sclp_early_print_lm(const char *str, unsigned int len)
0086 {
0087     unsigned char *ptr, *end, ch;
0088     unsigned int count, offset;
0089     struct write_sccb *sccb;
0090     struct msg_buf *msg;
0091     struct mdb *mdb;
0092     struct mto *mto;
0093     struct go *go;
0094 
0095     sccb = (struct write_sccb *) sclp_early_sccb;
0096     end = (unsigned char *) sccb + EARLY_SCCB_SIZE - 1;
0097     memset(sccb, 0, sizeof(*sccb));
0098     ptr = (unsigned char *) &sccb->msg.mdb.mto;
0099     offset = 0;
0100     do {
0101         for (count = sizeof(*mto); offset < len; count++) {
0102             ch = str[offset++];
0103             if ((ch == 0x0a) || (ptr + count > end))
0104                 break;
0105             ptr[count] = _ascebc[ch];
0106         }
0107         mto = (struct mto *) ptr;
0108         memset(mto, 0, sizeof(*mto));
0109         mto->length = count;
0110         mto->type = 4;
0111         mto->line_type_flags = LNTPFLGS_ENDTEXT;
0112         ptr += count;
0113     } while ((offset < len) && (ptr + sizeof(*mto) <= end));
0114     len = ptr - (unsigned char *) sccb;
0115     sccb->header.length = len - offsetof(struct write_sccb, header);
0116     msg = &sccb->msg;
0117     msg->header.type = EVTYP_MSG;
0118     msg->header.length = len - offsetof(struct write_sccb, msg.header);
0119     mdb = &msg->mdb;
0120     mdb->header.type = 1;
0121     mdb->header.tag = 0xD4C4C240;
0122     mdb->header.revision_code = 1;
0123     mdb->header.length = len - offsetof(struct write_sccb, msg.mdb.header);
0124     go = &mdb->go;
0125     go->length = sizeof(*go);
0126     go->type = 1;
0127     sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_DATA, sccb);
0128 }
0129 
0130 struct vt220_sccb {
0131     struct sccb_header header;
0132     struct {
0133         struct evbuf_header header;
0134         char data[];
0135     } msg;
0136 } __packed;
0137 
0138 /* Output multi-line text using SCLP VT220 interface. */
0139 static void sclp_early_print_vt220(const char *str, unsigned int len)
0140 {
0141     struct vt220_sccb *sccb;
0142 
0143     sccb = (struct vt220_sccb *) sclp_early_sccb;
0144     if (sizeof(*sccb) + len >= EARLY_SCCB_SIZE)
0145         len = EARLY_SCCB_SIZE - sizeof(*sccb);
0146     memset(sccb, 0, sizeof(*sccb));
0147     memcpy(&sccb->msg.data, str, len);
0148     sccb->header.length = sizeof(*sccb) + len;
0149     sccb->msg.header.length = sizeof(sccb->msg) + len;
0150     sccb->msg.header.type = EVTYP_VT220MSG;
0151     sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_DATA, sccb);
0152 }
0153 
0154 int sclp_early_set_event_mask(struct init_sccb *sccb,
0155                   sccb_mask_t receive_mask,
0156                   sccb_mask_t send_mask)
0157 {
0158 retry:
0159     memset(sccb, 0, sizeof(*sccb));
0160     sccb->header.length = sizeof(*sccb);
0161     if (sclp_mask_compat_mode)
0162         sccb->mask_length = SCLP_MASK_SIZE_COMPAT;
0163     else
0164         sccb->mask_length = sizeof(sccb_mask_t);
0165     sccb_set_recv_mask(sccb, receive_mask);
0166     sccb_set_send_mask(sccb, send_mask);
0167     if (sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_MASK, sccb))
0168         return -EIO;
0169     if ((sccb->header.response_code == 0x74f0) && !sclp_mask_compat_mode) {
0170         sclp_mask_compat_mode = true;
0171         goto retry;
0172     }
0173     if (sccb->header.response_code != 0x20)
0174         return -EIO;
0175     return 0;
0176 }
0177 
0178 unsigned int sclp_early_con_check_linemode(struct init_sccb *sccb)
0179 {
0180     if (!(sccb_get_sclp_send_mask(sccb) & EVTYP_OPCMD_MASK))
0181         return 0;
0182     if (!(sccb_get_sclp_recv_mask(sccb) & (EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK)))
0183         return 0;
0184     return 1;
0185 }
0186 
0187 unsigned int sclp_early_con_check_vt220(struct init_sccb *sccb)
0188 {
0189     if (sccb_get_sclp_send_mask(sccb) & EVTYP_VT220MSG_MASK)
0190         return 1;
0191     return 0;
0192 }
0193 
0194 static int sclp_early_setup(int disable, int *have_linemode, int *have_vt220)
0195 {
0196     unsigned long receive_mask, send_mask;
0197     struct init_sccb *sccb;
0198     int rc;
0199 
0200     BUILD_BUG_ON(sizeof(struct init_sccb) > PAGE_SIZE);
0201 
0202     *have_linemode = *have_vt220 = 0;
0203     sccb = (struct init_sccb *) sclp_early_sccb;
0204     receive_mask = disable ? 0 : EVTYP_OPCMD_MASK;
0205     send_mask = disable ? 0 : EVTYP_VT220MSG_MASK | EVTYP_MSG_MASK;
0206     rc = sclp_early_set_event_mask(sccb, receive_mask, send_mask);
0207     if (rc)
0208         return rc;
0209     *have_linemode = sclp_early_con_check_linemode(sccb);
0210     *have_vt220 = !!(sccb_get_send_mask(sccb) & EVTYP_VT220MSG_MASK);
0211     return rc;
0212 }
0213 
0214 void sclp_early_set_buffer(void *sccb)
0215 {
0216     sclp_early_sccb = sccb;
0217 }
0218 
0219 /*
0220  * Output one or more lines of text on the SCLP console (VT220 and /
0221  * or line-mode).
0222  */
0223 void __sclp_early_printk(const char *str, unsigned int len)
0224 {
0225     int have_linemode, have_vt220;
0226 
0227     if (sclp_init_state != sclp_init_state_uninitialized)
0228         return;
0229     if (sclp_early_setup(0, &have_linemode, &have_vt220) != 0)
0230         return;
0231     if (have_linemode)
0232         sclp_early_print_lm(str, len);
0233     if (have_vt220)
0234         sclp_early_print_vt220(str, len);
0235     sclp_early_setup(1, &have_linemode, &have_vt220);
0236 }
0237 
0238 void sclp_early_printk(const char *str)
0239 {
0240     __sclp_early_printk(str, strlen(str));
0241 }
0242 
0243 /*
0244  * We can't pass sclp_info_sccb to sclp_early_cmd() here directly,
0245  * because it might not fulfil the requiremets for a SCLP communication buffer:
0246  *   - lie below 2G in memory
0247  *   - be page-aligned
0248  * Therefore, we use the buffer sclp_early_sccb (which fulfils all those
0249  * requirements) temporarily for communication and copy a received response
0250  * back into the buffer sclp_info_sccb upon successful completion.
0251  */
0252 int __init sclp_early_read_info(void)
0253 {
0254     int i;
0255     int length = test_facility(140) ? EXT_SCCB_READ_SCP : PAGE_SIZE;
0256     struct read_info_sccb *sccb = (struct read_info_sccb *)sclp_early_sccb;
0257     sclp_cmdw_t commands[] = {SCLP_CMDW_READ_SCP_INFO_FORCED,
0258                   SCLP_CMDW_READ_SCP_INFO};
0259 
0260     for (i = 0; i < ARRAY_SIZE(commands); i++) {
0261         memset(sccb, 0, length);
0262         sccb->header.length = length;
0263         sccb->header.function_code = 0x80;
0264         sccb->header.control_mask[2] = 0x80;
0265         if (sclp_early_cmd(commands[i], sccb))
0266             break;
0267         if (sccb->header.response_code == 0x10) {
0268             memcpy(&sclp_info_sccb, sccb, length);
0269             sclp_info_sccb_valid = 1;
0270             return 0;
0271         }
0272         if (sccb->header.response_code != 0x1f0)
0273             break;
0274     }
0275     return -EIO;
0276 }
0277 
0278 struct read_info_sccb * __init sclp_early_get_info(void)
0279 {
0280     if (!sclp_info_sccb_valid)
0281         return NULL;
0282 
0283     return &sclp_info_sccb;
0284 }
0285 
0286 int __init sclp_early_get_memsize(unsigned long *mem)
0287 {
0288     unsigned long rnmax;
0289     unsigned long rnsize;
0290     struct read_info_sccb *sccb = &sclp_info_sccb;
0291 
0292     if (!sclp_info_sccb_valid)
0293         return -EIO;
0294 
0295     rnmax = sccb->rnmax ? sccb->rnmax : sccb->rnmax2;
0296     rnsize = sccb->rnsize ? sccb->rnsize : sccb->rnsize2;
0297     rnsize <<= 20;
0298     *mem = rnsize * rnmax;
0299     return 0;
0300 }
0301 
0302 int __init sclp_early_get_hsa_size(unsigned long *hsa_size)
0303 {
0304     if (!sclp_info_sccb_valid)
0305         return -EIO;
0306 
0307     *hsa_size = 0;
0308     if (sclp_info_sccb.hsa_size)
0309         *hsa_size = (sclp_info_sccb.hsa_size - 1) * PAGE_SIZE;
0310     return 0;
0311 }
0312 
0313 #define SCLP_STORAGE_INFO_FACILITY     0x0000400000000000UL
0314 
0315 void __weak __init add_mem_detect_block(u64 start, u64 end) {}
0316 int __init sclp_early_read_storage_info(void)
0317 {
0318     struct read_storage_sccb *sccb = (struct read_storage_sccb *)sclp_early_sccb;
0319     int rc, id, max_id = 0;
0320     unsigned long rn, rzm;
0321     sclp_cmdw_t command;
0322     u16 sn;
0323 
0324     if (!sclp_info_sccb_valid)
0325         return -EIO;
0326 
0327     if (!(sclp_info_sccb.facilities & SCLP_STORAGE_INFO_FACILITY))
0328         return -EOPNOTSUPP;
0329 
0330     rzm = sclp_info_sccb.rnsize ?: sclp_info_sccb.rnsize2;
0331     rzm <<= 20;
0332 
0333     for (id = 0; id <= max_id; id++) {
0334         memset(sclp_early_sccb, 0, EARLY_SCCB_SIZE);
0335         sccb->header.length = EARLY_SCCB_SIZE;
0336         command = SCLP_CMDW_READ_STORAGE_INFO | (id << 8);
0337         rc = sclp_early_cmd(command, sccb);
0338         if (rc)
0339             goto fail;
0340 
0341         max_id = sccb->max_id;
0342         switch (sccb->header.response_code) {
0343         case 0x0010:
0344             for (sn = 0; sn < sccb->assigned; sn++) {
0345                 if (!sccb->entries[sn])
0346                     continue;
0347                 rn = sccb->entries[sn] >> 16;
0348                 add_mem_detect_block((rn - 1) * rzm, rn * rzm);
0349             }
0350             break;
0351         case 0x0310:
0352         case 0x0410:
0353             break;
0354         default:
0355             goto fail;
0356         }
0357     }
0358 
0359     return 0;
0360 fail:
0361     mem_detect.count = 0;
0362     return -EIO;
0363 }