The sclp console in the s390 bios writes raw data, leading console emulators (such as virsh console) to treat a new line ('\n') as just a new line instead of as a Unix line feed. Because of this, output appears in a "stair case" pattern. Let's print \r\n on every occurrence of a new line in the string passed to write to amend this issue. This is in sync with the guest Linux code in drivers/s390/char/sclp_vt220.c which also does a line feed conversion in the console part of the driver. This fixes the s390-ccw and s390-netboot output like $ virsh start test --console Domain test started Connected to domain test Escape character is ^] Network boot starting... Using MAC address: 02:01:02:03:04:05 Requesting information via DHCP: 010 Signed-off-by: Collin L. Walling <walling@linux.vnet.ibm.com> Message-Id: <1509120893-28054-1-git-send-email-walling@linux.vnet.ibm.com> Reviewed-by: Halil Pasic <pasic@linux.vnet.ibm.com> Acked-by: Cornelia Huck <cohuck@redhat.com> Reviewed-by: Alexander Graf <agraf@suse.de> Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>
130 lines
3.4 KiB
C
130 lines
3.4 KiB
C
/*
|
|
* 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);
|
|
}
|
|
}
|