/* * zipl - zSeries Initial Program Loader tool * * Functions for console input and output * * Copyright IBM Corp. 2013, 2017 * * s390-tools is free software; you can redistribute it and/or modify * it under the terms of the MIT license. See LICENSE for details. */ #include "error.h" #include "s390.h" #include "sclp.h" /* Perform service call. Return 0 on success, non-zero otherwise. */ static int sclp_service_call(unsigned int command, void *sccb) { int cc; asm volatile( " .insn rre,0xb2200000,%1,%2\n" /* servc %1,%2 */ " ipm %0\n" " srl %0,28" : "=&d" (cc) : "d" (command), "a" (sccb) : "cc", "memory"); if (cc == 3) return 3; if (cc == 2) return 2; return 0; } static inline int wait(void) { struct psw_t ext_old_psw_save; uint16_t ext_int_code_save; int rc = 0; ext_old_psw_save = S390_lowcore.external_old_psw; ext_int_code_save = S390_lowcore.ext_int_code; S390_lowcore.ext_int_code = 0; do { load_wait_psw(0x0102000180000000ULL, &S390_lowcore.external_new_psw); if (S390_lowcore.ext_int_code == 0x1004) { rc = ETIMEOUT; break; } } while (S390_lowcore.ext_int_code != 0x2401); S390_lowcore.ext_int_code = ext_int_code_save; S390_lowcore.external_old_psw = ext_old_psw_save; return rc; } int sclp_wait_for_int(unsigned long long timeout) { unsigned long old_ctl, ctl; uint64_t time; int rc; if (timeout) { timeout <<= 32; time = get_tod_clock(); time += timeout; set_clock_comparator(time); } /* get control register 0 */ __ctl_store(old_ctl, 0, 0); ctl = old_ctl; /* set new mask */ ctl |= CTL_SERVICE_SIGNAL; if (timeout) ctl |= CTL_CLOCK_COMPARATOR; else ctl &= ~CTL_CLOCK_COMPARATOR; /* write control register 0 */ __ctl_load(ctl, 0, 0); rc = wait(); /* restore old control register 0 */ __ctl_load(old_ctl, 0, 0); return rc; } int start_sclp(unsigned int cmd, void *sccb) { int rc; while (1) { rc = sclp_service_call(cmd, sccb); if (rc == 3) return 1; if (rc == 2) sclp_wait_for_int(0); else break; } sclp_wait_for_int(0); return 0; } static int mask_is_set(sccb_mask_t mask_to_set, sccb_mask_t mask_set) { return (mask_to_set & mask_set) == mask_to_set; } int sclp_setup(int initialise) { struct init_sccb *sccb; unsigned int cmd; int rc; sccb = (void *)get_zeroed_page(); sccb->header.length = sizeof(struct init_sccb); sccb->mask_length = 4; switch (initialise) { case SCLP_INIT: sccb->receive_mask = 0x80000000; sccb->send_mask = 0x40000000; break; case SCLP_DISABLE: sccb->receive_mask = 0x0; sccb->send_mask = 0x0; break; case SCLP_HSA_INIT: sccb->receive_mask = 0x0; sccb->send_mask = 0x40000010; break; case SCLP_HSA_INIT_ASYNC: sccb->receive_mask = 0x00000010; sccb->send_mask = 0x40000010; break; } cmd = SCLP_CMD_WRITE_MASK; rc = start_sclp(cmd, sccb); if (rc || sccb->header.response_code != 0x20) { rc = 1; goto out_free_page; } if (!mask_is_set(sccb->send_mask, sccb->sclp_send_mask) || !mask_is_set(sccb->receive_mask, sccb->sclp_receive_mask)) { rc = 1; goto out_free_page; } rc = 0; out_free_page: free_page((unsigned long) sccb); return rc; } int sclp_print(char *buffer) { struct write_sccb *sccb; struct mto *mto; char *data; unsigned int cmd; int rc, i = 0; sccb = (void *)get_zeroed_page(); mto = (void *)((char *)sccb) + sizeof(struct write_sccb); data = (char *)mto + sizeof(struct mto); sccb->header.length = sizeof(struct write_sccb); sccb->msg_buf.header.length = sizeof(struct msg_buf); sccb->msg_buf.header.type = 0x02; sccb->msg_buf.mdb.header.length = sizeof(struct mdb); sccb->msg_buf.mdb.header.type = 1; sccb->msg_buf.mdb.header.tag = EBC_MDB; sccb->msg_buf.mdb.header.revision_code = 1; sccb->msg_buf.mdb.go.length = sizeof(struct go); sccb->msg_buf.mdb.go.type = 1; while (buffer[i] != 0) { memset(mto, 0, sizeof(struct mto)); mto->length = sizeof(struct mto); mto->type = 4; mto->line_type_flags = 0x1000; /* * while not end of string and not end of line * copy characterwise */ while (buffer[i] != 0 && buffer[i] != 0x15) { *data = buffer[i]; mto->length++; data++; i++; } /* update lengths */ sccb->msg_buf.mdb.header.length += mto->length; sccb->msg_buf.header.length += mto->length; sccb->header.length += mto->length; /* if another line is remaining build a new mto */ if (buffer[i] != 0) { mto = (struct mto *)data; data = (char *)mto + sizeof(struct mto); i++; } } /* SCLP command for write data */ cmd = SCLP_CMD_WRITE_DATA; rc = start_sclp(cmd, sccb); if (rc || sccb->header.response_code != 0x20) { rc = 1; goto out_free_page; } rc = 0; out_free_page: free_page((unsigned long) sccb); return rc; } int sclp_read_info(struct read_info_sccb *sccb) { unsigned int cmd; int rc; sccb->header.length = sizeof(struct read_info_sccb); cmd = SCLP_CMD_READ_INFO; rc = start_sclp(cmd, sccb); if (rc) return 1; if (sccb->header.response_code != 0x10) { cmd = SCLP_CMD_READ_INFO2; rc = start_sclp(cmd, sccb); } if (rc || sccb->header.response_code != 0x10) return 2; return 0; } int sclp_param(char *loadparm) { struct read_info_sccb *sccb; int rc; sccb = (void *)get_zeroed_page(); rc = sclp_read_info(sccb); if (rc == 0) memcpy(loadparm, sccb->loadparm, 8); free_page((unsigned long) sccb); return rc; } static struct gds_vector *sclp_find_gds_vector(void *start, void *end, uint16_t id) { struct gds_vector *v; for (v = start; (void *)v < end; v = (void *)v + v->length) if (v->gds_id == id) return v; return NULL; } static struct gds_subvector *sclp_eval_selfdeftextmsg(struct gds_subvector *sv) { void *end; end = (void *)sv + sv->length; for (sv = sv + 1; (void *)sv < end; sv = (void *)sv + sv->length) if (sv->key == 0x30) return sv; return NULL; } static struct gds_subvector *sclp_eval_textcmd(struct gds_vector *v) { struct gds_subvector *sv; void *end; end = (void *)v + v->length; for (sv = (struct gds_subvector *)(v + 1); (void *)sv < end; sv = (void *)sv + sv->length) if (sv->key == GDS_KEY_SELFDEFTEXTMSG) return sclp_eval_selfdeftextmsg(sv); return NULL; } static struct gds_subvector *sclp_eval_cpmsu(struct gds_vector *v) { void *end; end = (void *)v + v->length; for (v = v + 1; (void *)v < end; v = (void *)v + v->length) if (v->gds_id == GDS_ID_TEXTCMD) return sclp_eval_textcmd(v); return NULL; } static struct gds_subvector *sclp_eval_mdsmu(struct gds_vector *v) { v = sclp_find_gds_vector(v + 1, (void *)v + v->length, GDS_ID_CPMSU); if (v) return sclp_eval_cpmsu(v); return NULL; } int sclp_read(unsigned long timeout, void *target, int *count) { struct read_sccb *sccb; struct evbuf_header *evbuf; struct gds_subvector *sv; struct gds_vector *v; unsigned int cmd; int rc; sccb = (void *)get_zeroed_page(); evbuf = (struct evbuf_header *)sccb->data; retry: memset(sccb, 0, sizeof(struct read_sccb)); sccb->header.length = 0x1000; cmd = SCLP_CMD_READ_DATA; rc = start_sclp(cmd, sccb); if (rc) { rc = 1; goto out_free_page; } if (sccb->header.response_code == 0x20) { v = sclp_find_gds_vector((void *)evbuf + sizeof(*evbuf), (void *)evbuf + evbuf->length, GDS_ID_MDSMU); if (!v) { rc = -EIO; goto out_free_page; } sv = sclp_eval_mdsmu(v); if (!sv) { rc = -EIO; goto out_free_page; } *count = sv->length - (sizeof(*sv)); memcpy(target, sv + 1, *count); rc = 0; goto out_free_page; } if (sccb->header.response_code == 0x60f0) { rc = sclp_wait_for_int(timeout); if (rc) { rc = 2; goto out_free_page; } goto retry; } else { rc = 1; } out_free_page: free_page((unsigned long) sccb); return rc; }