sclp_early_core.c 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright IBM Corp. 2015
  4. * Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
  5. */
  6. #include <linux/kernel.h>
  7. #include <asm/processor.h>
  8. #include <asm/lowcore.h>
  9. #include <asm/ebcdic.h>
  10. #include <asm/irq.h>
  11. #include "sclp.h"
  12. #include "sclp_rw.h"
  13. char sclp_early_sccb[PAGE_SIZE] __aligned(PAGE_SIZE) __section(.data);
  14. int sclp_init_state __section(.data) = sclp_init_state_uninitialized;
  15. /*
  16. * Used to keep track of the size of the event masks. Qemu until version 2.11
  17. * only supports 4 and needs a workaround.
  18. */
  19. bool sclp_mask_compat_mode __section(.data);
  20. void sclp_early_wait_irq(void)
  21. {
  22. unsigned long psw_mask, addr;
  23. psw_t psw_ext_save, psw_wait;
  24. union ctlreg0 cr0, cr0_new;
  25. __ctl_store(cr0.val, 0, 0);
  26. cr0_new.val = cr0.val & ~CR0_IRQ_SUBCLASS_MASK;
  27. cr0_new.lap = 0;
  28. cr0_new.sssm = 1;
  29. __ctl_load(cr0_new.val, 0, 0);
  30. psw_ext_save = S390_lowcore.external_new_psw;
  31. psw_mask = __extract_psw();
  32. S390_lowcore.external_new_psw.mask = psw_mask;
  33. psw_wait.mask = psw_mask | PSW_MASK_EXT | PSW_MASK_WAIT;
  34. S390_lowcore.ext_int_code = 0;
  35. do {
  36. asm volatile(
  37. " larl %[addr],0f\n"
  38. " stg %[addr],%[psw_wait_addr]\n"
  39. " stg %[addr],%[psw_ext_addr]\n"
  40. " lpswe %[psw_wait]\n"
  41. "0:\n"
  42. : [addr] "=&d" (addr),
  43. [psw_wait_addr] "=Q" (psw_wait.addr),
  44. [psw_ext_addr] "=Q" (S390_lowcore.external_new_psw.addr)
  45. : [psw_wait] "Q" (psw_wait)
  46. : "cc", "memory");
  47. } while (S390_lowcore.ext_int_code != EXT_IRQ_SERVICE_SIG);
  48. S390_lowcore.external_new_psw = psw_ext_save;
  49. __ctl_load(cr0.val, 0, 0);
  50. }
  51. int sclp_early_cmd(sclp_cmdw_t cmd, void *sccb)
  52. {
  53. unsigned long flags;
  54. int rc;
  55. raw_local_irq_save(flags);
  56. rc = sclp_service_call(cmd, sccb);
  57. if (rc)
  58. goto out;
  59. sclp_early_wait_irq();
  60. out:
  61. raw_local_irq_restore(flags);
  62. return rc;
  63. }
  64. struct write_sccb {
  65. struct sccb_header header;
  66. struct msg_buf msg;
  67. } __packed;
  68. /* Output multi-line text using SCLP Message interface. */
  69. static void sclp_early_print_lm(const char *str, unsigned int len)
  70. {
  71. unsigned char *ptr, *end, ch;
  72. unsigned int count, offset;
  73. struct write_sccb *sccb;
  74. struct msg_buf *msg;
  75. struct mdb *mdb;
  76. struct mto *mto;
  77. struct go *go;
  78. sccb = (struct write_sccb *) &sclp_early_sccb;
  79. end = (unsigned char *) sccb + sizeof(sclp_early_sccb) - 1;
  80. memset(sccb, 0, sizeof(*sccb));
  81. ptr = (unsigned char *) &sccb->msg.mdb.mto;
  82. offset = 0;
  83. do {
  84. for (count = sizeof(*mto); offset < len; count++) {
  85. ch = str[offset++];
  86. if ((ch == 0x0a) || (ptr + count > end))
  87. break;
  88. ptr[count] = _ascebc[ch];
  89. }
  90. mto = (struct mto *) ptr;
  91. memset(mto, 0, sizeof(*mto));
  92. mto->length = count;
  93. mto->type = 4;
  94. mto->line_type_flags = LNTPFLGS_ENDTEXT;
  95. ptr += count;
  96. } while ((offset < len) && (ptr + sizeof(*mto) <= end));
  97. len = ptr - (unsigned char *) sccb;
  98. sccb->header.length = len - offsetof(struct write_sccb, header);
  99. msg = &sccb->msg;
  100. msg->header.type = EVTYP_MSG;
  101. msg->header.length = len - offsetof(struct write_sccb, msg.header);
  102. mdb = &msg->mdb;
  103. mdb->header.type = 1;
  104. mdb->header.tag = 0xD4C4C240;
  105. mdb->header.revision_code = 1;
  106. mdb->header.length = len - offsetof(struct write_sccb, msg.mdb.header);
  107. go = &mdb->go;
  108. go->length = sizeof(*go);
  109. go->type = 1;
  110. sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_DATA, sccb);
  111. }
  112. struct vt220_sccb {
  113. struct sccb_header header;
  114. struct {
  115. struct evbuf_header header;
  116. char data[];
  117. } msg;
  118. } __packed;
  119. /* Output multi-line text using SCLP VT220 interface. */
  120. static void sclp_early_print_vt220(const char *str, unsigned int len)
  121. {
  122. struct vt220_sccb *sccb;
  123. sccb = (struct vt220_sccb *) &sclp_early_sccb;
  124. if (sizeof(*sccb) + len >= sizeof(sclp_early_sccb))
  125. len = sizeof(sclp_early_sccb) - sizeof(*sccb);
  126. memset(sccb, 0, sizeof(*sccb));
  127. memcpy(&sccb->msg.data, str, len);
  128. sccb->header.length = sizeof(*sccb) + len;
  129. sccb->msg.header.length = sizeof(sccb->msg) + len;
  130. sccb->msg.header.type = EVTYP_VT220MSG;
  131. sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_DATA, sccb);
  132. }
  133. int sclp_early_set_event_mask(struct init_sccb *sccb,
  134. sccb_mask_t receive_mask,
  135. sccb_mask_t send_mask)
  136. {
  137. retry:
  138. memset(sccb, 0, sizeof(*sccb));
  139. sccb->header.length = sizeof(*sccb);
  140. if (sclp_mask_compat_mode)
  141. sccb->mask_length = SCLP_MASK_SIZE_COMPAT;
  142. else
  143. sccb->mask_length = sizeof(sccb_mask_t);
  144. sccb_set_recv_mask(sccb, receive_mask);
  145. sccb_set_send_mask(sccb, send_mask);
  146. if (sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_MASK, sccb))
  147. return -EIO;
  148. if ((sccb->header.response_code == 0x74f0) && !sclp_mask_compat_mode) {
  149. sclp_mask_compat_mode = true;
  150. goto retry;
  151. }
  152. if (sccb->header.response_code != 0x20)
  153. return -EIO;
  154. return 0;
  155. }
  156. unsigned int sclp_early_con_check_linemode(struct init_sccb *sccb)
  157. {
  158. if (!(sccb_get_sclp_send_mask(sccb) & EVTYP_OPCMD_MASK))
  159. return 0;
  160. if (!(sccb_get_sclp_recv_mask(sccb) & (EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK)))
  161. return 0;
  162. return 1;
  163. }
  164. unsigned int sclp_early_con_check_vt220(struct init_sccb *sccb)
  165. {
  166. if (sccb_get_sclp_send_mask(sccb) & EVTYP_VT220MSG_MASK)
  167. return 1;
  168. return 0;
  169. }
  170. static int sclp_early_setup(int disable, int *have_linemode, int *have_vt220)
  171. {
  172. unsigned long receive_mask, send_mask;
  173. struct init_sccb *sccb;
  174. int rc;
  175. BUILD_BUG_ON(sizeof(struct init_sccb) > PAGE_SIZE);
  176. *have_linemode = *have_vt220 = 0;
  177. sccb = (struct init_sccb *) &sclp_early_sccb;
  178. receive_mask = disable ? 0 : EVTYP_OPCMD_MASK;
  179. send_mask = disable ? 0 : EVTYP_VT220MSG_MASK | EVTYP_MSG_MASK;
  180. rc = sclp_early_set_event_mask(sccb, receive_mask, send_mask);
  181. if (rc)
  182. return rc;
  183. *have_linemode = sclp_early_con_check_linemode(sccb);
  184. *have_vt220 = !!(sccb_get_send_mask(sccb) & EVTYP_VT220MSG_MASK);
  185. return rc;
  186. }
  187. /*
  188. * Output one or more lines of text on the SCLP console (VT220 and /
  189. * or line-mode).
  190. */
  191. void __sclp_early_printk(const char *str, unsigned int len)
  192. {
  193. int have_linemode, have_vt220;
  194. if (sclp_init_state != sclp_init_state_uninitialized)
  195. return;
  196. if (sclp_early_setup(0, &have_linemode, &have_vt220) != 0)
  197. return;
  198. if (have_linemode)
  199. sclp_early_print_lm(str, len);
  200. if (have_vt220)
  201. sclp_early_print_vt220(str, len);
  202. sclp_early_setup(1, &have_linemode, &have_vt220);
  203. }
  204. void sclp_early_printk(const char *str)
  205. {
  206. __sclp_early_printk(str, strlen(str));
  207. }