mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
- Move zipl/boot/error.h to include/boot - Adjust include statements in zipb/boot and genprotimg/boot - Remove error.h from tunedasd/src/tunedasd.c as not needed - Fix tunedasd/src/Makefile Signed-off-by: Mikhail Zaslonko <zaslonko@linux.ibm.com> Reviewed-by: Marc Hartmayer <mhartmay@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
428 lines
9.2 KiB
C
428 lines
9.2 KiB
C
/*
|
|
* 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 "libc.h"
|
|
#include "boot/error.h"
|
|
#include "boot/s390.h"
|
|
#include "sclp.h"
|
|
#include "ebcdic.h"
|
|
#ifdef ENABLE_SCLP_ASCII
|
|
# include "ebcdic_conv.h"
|
|
#endif /* ENABLE_SCLP_ASCII */
|
|
|
|
/* 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 = SCLP_EVENT_MASK_OPCMD;
|
|
sccb->send_mask = SCLP_EVENT_MASK_MSG;
|
|
break;
|
|
case SCLP_DISABLE:
|
|
sccb->receive_mask = SCLP_EVENT_MASK_DISABLE;
|
|
sccb->send_mask = SCLP_EVENT_MASK_DISABLE;
|
|
break;
|
|
case SCLP_LINE_ASCII_INIT:
|
|
sccb->receive_mask = SCLP_EVENT_MASK_DISABLE;
|
|
sccb->send_mask = SCLP_EVENT_MASK_MSG | SCLP_EVENT_MASK_ASCII;
|
|
break;
|
|
case SCLP_HSA_INIT:
|
|
sccb->receive_mask = SCLP_EVENT_MASK_DISABLE;
|
|
sccb->send_mask = SCLP_EVENT_MASK_MSG | SCLP_EVENT_MASK_SDIAS;
|
|
break;
|
|
case SCLP_HSA_INIT_ASYNC:
|
|
sccb->receive_mask = SCLP_EVENT_MASK_SDIAS;
|
|
sccb->send_mask = SCLP_EVENT_MASK_MSG | SCLP_EVENT_MASK_SDIAS;
|
|
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;
|
|
}
|
|
|
|
#ifdef ENABLE_SCLP_ASCII
|
|
/* Content of @buffer must be EBCDIC encoded. The function used for
|
|
* the conversion `ebcdic_to_ascii` differentiates whether the code
|
|
* runs on z/VM or not and then selects the appropriate EBCDIC
|
|
* coding.
|
|
*/
|
|
int sclp_print_ascii(const char *buffer)
|
|
{
|
|
struct write_sccb *sccb = NULL;
|
|
int rc, str_len = strlen(buffer);
|
|
unsigned long data_len = str_len + 1;
|
|
|
|
/* don't overflow the sccb buffer */
|
|
if (data_len > SCCB_MAX_DATA_LEN)
|
|
data_len = SCCB_MAX_DATA_LEN;
|
|
|
|
sccb = (void *)get_zeroed_page();
|
|
sccb->header.length = sizeof(struct write_sccb) - sizeof(struct mdb)
|
|
+ data_len;
|
|
sccb->header.function_code = SCLP_FC_NORMAL_WRITE;
|
|
sccb->msg_buf.header.length = sizeof(struct msg_buf) - sizeof(struct mdb)
|
|
+ data_len;
|
|
sccb->msg_buf.header.type = SCLP_EVENT_DATA_ASCII;
|
|
sccb->msg_buf.header.flags = 0;
|
|
ebcdic_to_ascii(sccb->msg_buf.data,
|
|
(const unsigned char *)buffer,
|
|
data_len - 1);
|
|
sccb->msg_buf.data[data_len - 1] = '\0';
|
|
|
|
/* SCLP command for write data */
|
|
rc = start_sclp(SCLP_CMD_WRITE_DATA, 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;
|
|
}
|
|
#endif /* ENABLE_SCLP_ASCII */
|
|
|
|
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;
|
|
}
|