aboutsummaryrefslogtreecommitdiff
path: root/pc-bios/s390-ccw/sclp.c
blob: 90d1bc314788092a0edf9de59ca6db6a6ec85b00 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/*
 * SCLP ASCII access driver
 *
 * Copyright (c) 2013 Alexander Graf <agraf@suse.de>
 *
 * This work is licensed under the terms of the GNU GPL, version 2 or (at
 * your option) any later version. See the COPYING file in the top-level
 * directory.
 */

#include "libc.h"
#include "s390-ccw.h"
#include "sclp.h"

long write(int fd, const void *str, size_t len);

static char _sccb[PAGE_SIZE] __attribute__((__aligned__(4096)));

const unsigned char ebc2asc[256] =
      /* 0123456789abcdef0123456789abcdef */
        "................................" /* 1F */
        "................................" /* 3F */
        " ...........<(+|&.........!$*);." /* 5F first.chr.here.is.real.space */
        "-/.........,%_>?.........`:#@'=\""/* 7F */
        ".abcdefghi.......jklmnopqr......" /* 9F */
        "..stuvwxyz......................" /* BF */
        ".ABCDEFGHI.......JKLMNOPQR......" /* DF */
        "..STUVWXYZ......0123456789......";/* FF */

/* 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" (__pa(sccb))
                : "cc", "memory");
        consume_sclp_int();
        if (cc == 3)
                return -EIO;
        if (cc == 2)
                return -EBUSY;
        return 0;
}

static void sclp_set_write_mask(void)
{
    WriteEventMask *sccb = (void *)_sccb;

    sccb->h.length = sizeof(WriteEventMask);
    sccb->mask_length = sizeof(unsigned int);
    sccb->receive_mask = SCLP_EVENT_MASK_MSG_ASCII;
    sccb->cp_receive_mask = SCLP_EVENT_MASK_MSG_ASCII;
    sccb->send_mask = SCLP_EVENT_MASK_MSG_ASCII;
    sccb->cp_send_mask = SCLP_EVENT_MASK_MSG_ASCII;

    sclp_service_call(SCLP_CMD_WRITE_EVENT_MASK, sccb);
}

void sclp_setup(void)
{
    sclp_set_write_mask();
}

static int _strlen(const char *str)
{
    int i;
    for (i = 0; *str; i++)
        str++;
    return i;
}

long write(int fd, const void *str, size_t len)
{
    WriteEventData *sccb = (void *)_sccb;
    const char *p = str;
    size_t data_len = 0;
    size_t i;

    if (fd != 1 && fd != 2) {
        return -EIO;
    }

    for (i = 0; i < len; i++) {
        if ((data_len + 1) >= SCCB_DATA_LEN) {
            /* We would overflow the sccb buffer, abort early */
            len = i;
            break;
        }

        if (*p == '\n') {
            /* Terminal emulators might need \r\n, so generate it */
            sccb->data[data_len++] = '\r';
        }

        sccb->data[data_len++] = *p;
        p++;
    }

    sccb->h.length = sizeof(WriteEventData) + data_len;
    sccb->h.function_code = SCLP_FC_NORMAL_WRITE;
    sccb->ebh.length = sizeof(EventBufferHeader) + data_len;
    sccb->ebh.type = SCLP_EVENT_ASCII_CONSOLE_DATA;
    sccb->ebh.flags = 0;

    sclp_service_call(SCLP_CMD_WRITE_EVENT_DATA, sccb);

    return len;
}

void sclp_print(const char *str)
{
    write(1, str, _strlen(str));
}

void sclp_get_loadparm_ascii(char *loadparm)
{

    ReadInfo *sccb = (void *)_sccb;

    memset((char *)_sccb, 0, sizeof(ReadInfo));
    sccb->h.length = sizeof(ReadInfo);
    if (!sclp_service_call(SCLP_CMDW_READ_SCP_INFO, sccb)) {
        ebcdic_to_ascii((char *) sccb->loadparm, loadparm, 8);
    }
}