Files
s390-tools/zipl/boot/sclp.c
Mikhail Zaslonko eb06ebe245 include/boot: Move zipl/boot/error.h to include/boot
- 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>
2023-07-20 21:45:27 +02:00

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;
}