mirror of
https://github.com/torvalds/linux
synced 2024-11-05 18:23:50 +00:00
s390/sclp: make early sclp code readable
This patch - unifies the old sclp early code and the sclp early printk code, so they can use common functions - makes sure all sclp early functions and variables have the same "sclp_early" prefix - converts the sclp early printk code into readable code by using existing data structures instead of hard coded magic arrays - splits the early sclp code into two files: sclp_early.c and sclp_early_core.c. The core file contains everything that is required by the kernel decompressor and may not call functions not contained within the core file. Otherwise the result would be a link error. - changes interrupt handling to be completely synchronous. The old early sclp code had a small window which allowed to receive several interrupts instead of exactly the single expected interrupt. This did hide a subtle potential bug, which is fixed with this large rework. - contains a couple of small cleanups. Reviewed-by: Peter Oberparleiter <oberpar@linux.vnet.ibm.com> Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
This commit is contained in:
parent
76fdf1416e
commit
d5ab7a34f9
10 changed files with 245 additions and 296 deletions
|
@ -66,7 +66,7 @@ static unsigned long free_mem_end_ptr;
|
|||
|
||||
static int puts(const char *s)
|
||||
{
|
||||
_sclp_print_early(s);
|
||||
sclp_early_printk(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -101,7 +101,12 @@ struct zpci_report_error_header {
|
|||
u8 data[0]; /* Subsequent Data passed verbatim to SCLP ET 24 */
|
||||
} __packed;
|
||||
|
||||
int _sclp_get_core_info_early(struct sclp_core_info *info);
|
||||
int sclp_early_get_core_info(struct sclp_core_info *info);
|
||||
void sclp_early_get_ipl_info(struct sclp_ipl_info *info);
|
||||
void sclp_early_detect(void);
|
||||
void sclp_early_printk(const char *s);
|
||||
void __sclp_early_printk(const char *s, unsigned int len);
|
||||
|
||||
int _sclp_get_core_info(struct sclp_core_info *info);
|
||||
int sclp_core_configure(u8 core);
|
||||
int sclp_core_deconfigure(u8 core);
|
||||
|
@ -110,21 +115,17 @@ int sclp_sdias_copy(void *dest, int blk_num, int nr_blks);
|
|||
int sclp_chp_configure(struct chp_id chpid);
|
||||
int sclp_chp_deconfigure(struct chp_id chpid);
|
||||
int sclp_chp_read_info(struct sclp_chp_info *info);
|
||||
void sclp_get_ipl_info(struct sclp_ipl_info *info);
|
||||
int sclp_pci_configure(u32 fid);
|
||||
int sclp_pci_deconfigure(u32 fid);
|
||||
int sclp_pci_report(struct zpci_report_error_header *report, u32 fh, u32 fid);
|
||||
int memcpy_hsa_kernel(void *dest, unsigned long src, size_t count);
|
||||
int memcpy_hsa_user(void __user *dest, unsigned long src, size_t count);
|
||||
void sclp_early_detect(void);
|
||||
void _sclp_print_early(const char *);
|
||||
void __sclp_print_early(const char *s, unsigned int len);
|
||||
void sclp_ocf_cpc_name_copy(char *dst);
|
||||
|
||||
static inline int sclp_get_core_info(struct sclp_core_info *info, int early)
|
||||
{
|
||||
if (early)
|
||||
return _sclp_get_core_info_early(info);
|
||||
return sclp_early_get_core_info(info);
|
||||
return _sclp_get_core_info(info);
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ static void __init print_machine_type(void)
|
|||
get_cpu_id(&id);
|
||||
u16_to_hex(type_str, id.machine);
|
||||
strcat(mach_str, type_str);
|
||||
_sclp_print_early(mach_str);
|
||||
sclp_early_printk(mach_str);
|
||||
}
|
||||
|
||||
static void __init u16_to_decimal(char *str, u16 val)
|
||||
|
@ -79,7 +79,7 @@ static void __init print_missing_facilities(void)
|
|||
* z/VM adds a four character prefix.
|
||||
*/
|
||||
if (strlen(als_str) > 70) {
|
||||
_sclp_print_early(als_str);
|
||||
sclp_early_printk(als_str);
|
||||
*als_str = '\0';
|
||||
}
|
||||
u16_to_decimal(val_str, i * BITS_PER_LONG + j);
|
||||
|
@ -87,13 +87,13 @@ static void __init print_missing_facilities(void)
|
|||
first = 0;
|
||||
}
|
||||
}
|
||||
_sclp_print_early(als_str);
|
||||
_sclp_print_early("See Principles of Operations for facility bits");
|
||||
sclp_early_printk(als_str);
|
||||
sclp_early_printk("See Principles of Operations for facility bits");
|
||||
}
|
||||
|
||||
static void __init facility_mismatch(void)
|
||||
{
|
||||
_sclp_print_early("The Linux kernel requires more recent processor hardware");
|
||||
sclp_early_printk("The Linux kernel requires more recent processor hardware");
|
||||
print_machine_type();
|
||||
print_missing_facilities();
|
||||
disabled_wait(0x8badcccc);
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
static void sclp_early_write(struct console *con, const char *s, unsigned int len)
|
||||
{
|
||||
__sclp_print_early(s, len);
|
||||
__sclp_early_printk(s, len);
|
||||
}
|
||||
|
||||
static struct console sclp_early_console = {
|
||||
|
|
|
@ -1864,7 +1864,7 @@ static int __init s390_ipl_init(void)
|
|||
{
|
||||
char str[8] = {0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40};
|
||||
|
||||
sclp_get_ipl_info(&sclp_ipl_info);
|
||||
sclp_early_get_ipl_info(&sclp_ipl_info);
|
||||
/*
|
||||
* Fix loadparm: There are systems where the (SCSI) LOADPARM
|
||||
* returned by read SCP info is invalid (contains EBCDIC blanks)
|
||||
|
|
|
@ -196,7 +196,7 @@ pgm_check_entry:
|
|||
larl %r15,init_thread_union
|
||||
ahi %r15,1<<(PAGE_SHIFT+THREAD_SIZE_ORDER)
|
||||
larl %r2,.Lpanic_string
|
||||
larl %r3,_sclp_print_early
|
||||
larl %r3,sclp_early_printk
|
||||
lghi %r1,0
|
||||
sam31
|
||||
sigp %r1,%r0,SIGP_SET_ARCHITECTURE
|
||||
|
|
|
@ -140,31 +140,6 @@ static void __sclp_make_read_req(void);
|
|||
static int sclp_init_mask(int calculate);
|
||||
static int sclp_init(void);
|
||||
|
||||
/* Perform service call. Return 0 on success, non-zero otherwise. */
|
||||
int
|
||||
sclp_service_call(sclp_cmdw_t command, void *sccb)
|
||||
{
|
||||
int cc = 4; /* Initialize for program check handling */
|
||||
|
||||
asm volatile(
|
||||
"0: .insn rre,0xb2200000,%1,%2\n" /* servc %1,%2 */
|
||||
"1: ipm %0\n"
|
||||
" srl %0,28\n"
|
||||
"2:\n"
|
||||
EX_TABLE(0b, 2b)
|
||||
EX_TABLE(1b, 2b)
|
||||
: "+&d" (cc) : "d" (command), "a" (__pa(sccb))
|
||||
: "cc", "memory");
|
||||
if (cc == 4)
|
||||
return -EINVAL;
|
||||
if (cc == 3)
|
||||
return -EIO;
|
||||
if (cc == 2)
|
||||
return -EBUSY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
__sclp_queue_read_req(void)
|
||||
{
|
||||
|
|
|
@ -204,7 +204,6 @@ void sclp_unregister(struct sclp_register *reg);
|
|||
int sclp_remove_processed(struct sccb_header *sccb);
|
||||
int sclp_deactivate(void);
|
||||
int sclp_reactivate(void);
|
||||
int sclp_service_call(sclp_cmdw_t command, void *sccb);
|
||||
int sclp_sync_request(sclp_cmdw_t command, void *sccb);
|
||||
int sclp_sync_request_timeout(sclp_cmdw_t command, void *sccb, int timeout);
|
||||
|
||||
|
@ -222,8 +221,41 @@ extern int sclp_console_pages;
|
|||
extern int sclp_console_drop;
|
||||
extern unsigned long sclp_console_full;
|
||||
|
||||
extern char sclp_early_sccb[PAGE_SIZE];
|
||||
|
||||
void sclp_early_wait_irq(void);
|
||||
int sclp_early_cmd_sync(sclp_cmdw_t cmd, void *sccb);
|
||||
int sclp_early_cmd(sclp_cmdw_t cmd, void *sccb);
|
||||
unsigned int sclp_early_con_check_linemode(struct init_sccb *sccb);
|
||||
int sclp_early_set_event_mask(struct init_sccb *sccb,
|
||||
unsigned long receive_mask,
|
||||
unsigned long send_mask);
|
||||
|
||||
/* useful inlines */
|
||||
|
||||
/* Perform service call. Return 0 on success, non-zero otherwise. */
|
||||
static inline int sclp_service_call(sclp_cmdw_t command, void *sccb)
|
||||
{
|
||||
int cc = 4; /* Initialize for program check handling */
|
||||
|
||||
asm volatile(
|
||||
"0: .insn rre,0xb2200000,%1,%2\n" /* servc %1,%2 */
|
||||
"1: ipm %0\n"
|
||||
" srl %0,28\n"
|
||||
"2:\n"
|
||||
EX_TABLE(0b, 2b)
|
||||
EX_TABLE(1b, 2b)
|
||||
: "+&d" (cc) : "d" (command), "a" ((unsigned long)sccb)
|
||||
: "cc", "memory");
|
||||
if (cc == 4)
|
||||
return -EINVAL;
|
||||
if (cc == 3)
|
||||
return -EIO;
|
||||
if (cc == 2)
|
||||
return -EBUSY;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* VM uses EBCDIC 037, LPAR+native(SE+HMC) use EBCDIC 500 */
|
||||
/* translate single character from ASCII to EBCDIC */
|
||||
static inline unsigned char
|
||||
|
|
|
@ -55,31 +55,12 @@ struct read_info_sccb {
|
|||
u8 _pad_128[4096 - 128]; /* 128-4095 */
|
||||
} __packed __aligned(PAGE_SIZE);
|
||||
|
||||
static char sccb_early[PAGE_SIZE] __aligned(PAGE_SIZE) __initdata;
|
||||
static struct sclp_ipl_info sclp_ipl_info;
|
||||
|
||||
struct sclp_info sclp;
|
||||
EXPORT_SYMBOL(sclp);
|
||||
|
||||
static int __init sclp_cmd_sync_early(sclp_cmdw_t cmd, void *sccb)
|
||||
{
|
||||
int rc;
|
||||
|
||||
__ctl_set_bit(0, 9);
|
||||
rc = sclp_service_call(cmd, sccb);
|
||||
if (rc)
|
||||
goto out;
|
||||
__load_psw_mask(PSW_DEFAULT_KEY | PSW_MASK_BASE | PSW_MASK_EA |
|
||||
PSW_MASK_BA | PSW_MASK_EXT | PSW_MASK_WAIT);
|
||||
local_irq_disable();
|
||||
out:
|
||||
/* Contents of the sccb might have changed. */
|
||||
barrier();
|
||||
__ctl_clear_bit(0, 9);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int __init sclp_read_info_early(struct read_info_sccb *sccb)
|
||||
static int __init sclp_early_read_info(struct read_info_sccb *sccb)
|
||||
{
|
||||
int rc, i;
|
||||
sclp_cmdw_t commands[] = {SCLP_CMDW_READ_SCP_INFO_FORCED,
|
||||
|
@ -91,7 +72,7 @@ static int __init sclp_read_info_early(struct read_info_sccb *sccb)
|
|||
sccb->header.length = sizeof(*sccb);
|
||||
sccb->header.function_code = 0x80;
|
||||
sccb->header.control_mask[2] = 0x80;
|
||||
rc = sclp_cmd_sync_early(commands[i], sccb);
|
||||
rc = sclp_early_cmd_sync(commands[i], sccb);
|
||||
} while (rc == -EBUSY);
|
||||
|
||||
if (rc)
|
||||
|
@ -104,12 +85,12 @@ static int __init sclp_read_info_early(struct read_info_sccb *sccb)
|
|||
return -EIO;
|
||||
}
|
||||
|
||||
static void __init sclp_facilities_detect(struct read_info_sccb *sccb)
|
||||
static void __init sclp_early_facilities_detect(struct read_info_sccb *sccb)
|
||||
{
|
||||
struct sclp_core_entry *cpue;
|
||||
u16 boot_cpu_address, cpu;
|
||||
|
||||
if (sclp_read_info_early(sccb))
|
||||
if (sclp_early_read_info(sccb))
|
||||
return;
|
||||
|
||||
sclp.facilities = sccb->facilities;
|
||||
|
@ -172,59 +153,19 @@ static void __init sclp_facilities_detect(struct read_info_sccb *sccb)
|
|||
}
|
||||
|
||||
/*
|
||||
* This function will be called after sclp_facilities_detect(), which gets
|
||||
* called from early.c code. The sclp_facilities_detect() function retrieves
|
||||
* This function will be called after sclp_early_facilities_detect(), which gets
|
||||
* called from early.c code. The sclp_early_facilities_detect() function retrieves
|
||||
* and saves the IPL information.
|
||||
*/
|
||||
void __init sclp_get_ipl_info(struct sclp_ipl_info *info)
|
||||
void __init sclp_early_get_ipl_info(struct sclp_ipl_info *info)
|
||||
{
|
||||
*info = sclp_ipl_info;
|
||||
}
|
||||
|
||||
static int __init sclp_cmd_early(sclp_cmdw_t cmd, void *sccb)
|
||||
{
|
||||
int rc;
|
||||
static struct sclp_core_info sclp_early_core_info __initdata;
|
||||
static int sclp_early_core_info_valid __initdata;
|
||||
|
||||
do {
|
||||
rc = sclp_cmd_sync_early(cmd, sccb);
|
||||
} while (rc == -EBUSY);
|
||||
|
||||
if (rc)
|
||||
return -EIO;
|
||||
if (((struct sccb_header *) sccb)->response_code != 0x0020)
|
||||
return -EIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __init sccb_init_eq_size(struct sdias_sccb *sccb)
|
||||
{
|
||||
memset(sccb, 0, sizeof(*sccb));
|
||||
|
||||
sccb->hdr.length = sizeof(*sccb);
|
||||
sccb->evbuf.hdr.length = sizeof(struct sdias_evbuf);
|
||||
sccb->evbuf.hdr.type = EVTYP_SDIAS;
|
||||
sccb->evbuf.event_qual = SDIAS_EQ_SIZE;
|
||||
sccb->evbuf.data_id = SDIAS_DI_FCP_DUMP;
|
||||
sccb->evbuf.event_id = 4712;
|
||||
sccb->evbuf.dbs = 1;
|
||||
}
|
||||
|
||||
static int __init sclp_set_event_mask(struct init_sccb *sccb,
|
||||
unsigned long receive_mask,
|
||||
unsigned long send_mask)
|
||||
{
|
||||
memset(sccb, 0, sizeof(*sccb));
|
||||
sccb->header.length = sizeof(*sccb);
|
||||
sccb->mask_length = sizeof(sccb_mask_t);
|
||||
sccb->receive_mask = receive_mask;
|
||||
sccb->send_mask = send_mask;
|
||||
return sclp_cmd_early(SCLP_CMDW_WRITE_EVENT_MASK, sccb);
|
||||
}
|
||||
|
||||
static struct sclp_core_info sclp_core_info_early __initdata;
|
||||
static int sclp_core_info_early_valid __initdata;
|
||||
|
||||
static void __init sclp_init_core_info_early(struct read_cpu_info_sccb *sccb)
|
||||
static void __init sclp_early_init_core_info(struct read_cpu_info_sccb *sccb)
|
||||
{
|
||||
int rc;
|
||||
|
||||
|
@ -233,80 +174,76 @@ static void __init sclp_init_core_info_early(struct read_cpu_info_sccb *sccb)
|
|||
memset(sccb, 0, sizeof(*sccb));
|
||||
sccb->header.length = sizeof(*sccb);
|
||||
do {
|
||||
rc = sclp_cmd_sync_early(SCLP_CMDW_READ_CPU_INFO, sccb);
|
||||
rc = sclp_early_cmd_sync(SCLP_CMDW_READ_CPU_INFO, sccb);
|
||||
} while (rc == -EBUSY);
|
||||
if (rc)
|
||||
return;
|
||||
if (sccb->header.response_code != 0x0010)
|
||||
return;
|
||||
sclp_fill_core_info(&sclp_core_info_early, sccb);
|
||||
sclp_core_info_early_valid = 1;
|
||||
sclp_fill_core_info(&sclp_early_core_info, sccb);
|
||||
sclp_early_core_info_valid = 1;
|
||||
}
|
||||
|
||||
int __init _sclp_get_core_info_early(struct sclp_core_info *info)
|
||||
int __init sclp_early_get_core_info(struct sclp_core_info *info)
|
||||
{
|
||||
if (!sclp_core_info_early_valid)
|
||||
if (!sclp_early_core_info_valid)
|
||||
return -EIO;
|
||||
*info = sclp_core_info_early;
|
||||
*info = sclp_early_core_info;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long __init sclp_hsa_size_init(struct sdias_sccb *sccb)
|
||||
static long __init sclp_early_hsa_size_init(struct sdias_sccb *sccb)
|
||||
{
|
||||
sccb_init_eq_size(sccb);
|
||||
if (sclp_cmd_early(SCLP_CMDW_WRITE_EVENT_DATA, sccb))
|
||||
memset(sccb, 0, sizeof(*sccb));
|
||||
sccb->hdr.length = sizeof(*sccb);
|
||||
sccb->evbuf.hdr.length = sizeof(struct sdias_evbuf);
|
||||
sccb->evbuf.hdr.type = EVTYP_SDIAS;
|
||||
sccb->evbuf.event_qual = SDIAS_EQ_SIZE;
|
||||
sccb->evbuf.data_id = SDIAS_DI_FCP_DUMP;
|
||||
sccb->evbuf.event_id = 4712;
|
||||
sccb->evbuf.dbs = 1;
|
||||
if (sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_DATA, sccb))
|
||||
return -EIO;
|
||||
if (sccb->evbuf.blk_cnt == 0)
|
||||
return 0;
|
||||
return (sccb->evbuf.blk_cnt - 1) * PAGE_SIZE;
|
||||
}
|
||||
|
||||
static long __init sclp_hsa_copy_wait(struct sccb_header *sccb)
|
||||
static long __init sclp_early_hsa_copy_wait(struct sdias_sccb *sccb)
|
||||
{
|
||||
memset(sccb, 0, PAGE_SIZE);
|
||||
sccb->length = PAGE_SIZE;
|
||||
if (sclp_cmd_early(SCLP_CMDW_READ_EVENT_DATA, sccb))
|
||||
sccb->hdr.length = PAGE_SIZE;
|
||||
if (sclp_early_cmd(SCLP_CMDW_READ_EVENT_DATA, sccb))
|
||||
return -EIO;
|
||||
if (((struct sdias_sccb *) sccb)->evbuf.blk_cnt == 0)
|
||||
if (sccb->evbuf.blk_cnt == 0)
|
||||
return 0;
|
||||
return (((struct sdias_sccb *) sccb)->evbuf.blk_cnt - 1) * PAGE_SIZE;
|
||||
return (sccb->evbuf.blk_cnt - 1) * PAGE_SIZE;
|
||||
}
|
||||
|
||||
static void __init sclp_hsa_size_detect(void *sccb)
|
||||
static void __init sclp_early_hsa_size_detect(void *sccb)
|
||||
{
|
||||
long size;
|
||||
unsigned long flags;
|
||||
long size = -EIO;
|
||||
|
||||
/* First try synchronous interface (LPAR) */
|
||||
if (sclp_set_event_mask(sccb, 0, 0x40000010))
|
||||
return;
|
||||
size = sclp_hsa_size_init(sccb);
|
||||
if (size < 0)
|
||||
return;
|
||||
if (size != 0)
|
||||
raw_local_irq_save(flags);
|
||||
if (sclp_early_set_event_mask(sccb, EVTYP_SDIAS_MASK, EVTYP_SDIAS_MASK))
|
||||
goto out;
|
||||
/* Then try asynchronous interface (z/VM) */
|
||||
if (sclp_set_event_mask(sccb, 0x00000010, 0x40000010))
|
||||
return;
|
||||
size = sclp_hsa_size_init(sccb);
|
||||
if (size < 0)
|
||||
return;
|
||||
size = sclp_hsa_copy_wait(sccb);
|
||||
if (size < 0)
|
||||
return;
|
||||
size = sclp_early_hsa_size_init(sccb);
|
||||
/* First check for synchronous response (LPAR) */
|
||||
if (size)
|
||||
goto out_mask;
|
||||
if (!(S390_lowcore.ext_params & 1))
|
||||
sclp_early_wait_irq();
|
||||
size = sclp_early_hsa_copy_wait(sccb);
|
||||
out_mask:
|
||||
sclp_early_set_event_mask(sccb, 0, 0);
|
||||
out:
|
||||
sclp.hsa_size = size;
|
||||
raw_local_irq_restore(flags);
|
||||
if (size > 0)
|
||||
sclp.hsa_size = size;
|
||||
}
|
||||
|
||||
static unsigned int __init sclp_con_check_linemode(struct init_sccb *sccb)
|
||||
{
|
||||
if (!(sccb->sclp_send_mask & EVTYP_OPCMD_MASK))
|
||||
return 0;
|
||||
if (!(sccb->sclp_receive_mask & (EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK)))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void __init sclp_console_detect(struct init_sccb *sccb)
|
||||
static void __init sclp_early_console_detect(struct init_sccb *sccb)
|
||||
{
|
||||
if (sccb->header.response_code != 0x20)
|
||||
return;
|
||||
|
@ -314,21 +251,22 @@ static void __init sclp_console_detect(struct init_sccb *sccb)
|
|||
if (sccb->sclp_send_mask & EVTYP_VT220MSG_MASK)
|
||||
sclp.has_vt220 = 1;
|
||||
|
||||
if (sclp_con_check_linemode(sccb))
|
||||
if (sclp_early_con_check_linemode(sccb))
|
||||
sclp.has_linemode = 1;
|
||||
}
|
||||
|
||||
void __init sclp_early_detect(void)
|
||||
{
|
||||
void *sccb = &sccb_early;
|
||||
void *sccb = &sclp_early_sccb;
|
||||
|
||||
sclp_facilities_detect(sccb);
|
||||
sclp_init_core_info_early(sccb);
|
||||
sclp_hsa_size_detect(sccb);
|
||||
sclp_early_facilities_detect(sccb);
|
||||
sclp_early_init_core_info(sccb);
|
||||
sclp_early_hsa_size_detect(sccb);
|
||||
|
||||
/* Turn off SCLP event notifications. Also save remote masks in the
|
||||
/*
|
||||
* Turn off SCLP event notifications. Also save remote masks in the
|
||||
* sccb. These are sufficient to detect sclp console capabilities.
|
||||
*/
|
||||
sclp_set_event_mask(sccb, 0, 0);
|
||||
sclp_console_detect(sccb);
|
||||
sclp_early_set_event_mask(sccb, 0, 0);
|
||||
sclp_early_console_detect(sccb);
|
||||
}
|
||||
|
|
|
@ -2,29 +2,24 @@
|
|||
* Copyright IBM Corp. 2015
|
||||
* Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/lowcore.h>
|
||||
#include <asm/ebcdic.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/lowcore.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/sclp.h>
|
||||
|
||||
#define EVTYP_VT220MSG_MASK 0x00000040
|
||||
#define EVTYP_MSG_MASK 0x40000000
|
||||
|
||||
static char _sclp_work_area[4096] __aligned(PAGE_SIZE) __section(data);
|
||||
static bool have_vt220 __section(data);
|
||||
static bool have_linemode __section(data);
|
||||
#include "sclp.h"
|
||||
#include "sclp_rw.h"
|
||||
|
||||
char sclp_early_sccb[PAGE_SIZE] __aligned(PAGE_SIZE) __section(data);
|
||||
int sclp_init_state __section(data) = sclp_init_state_uninitialized;
|
||||
|
||||
static void _sclp_wait_int(void)
|
||||
void sclp_early_wait_irq(void)
|
||||
{
|
||||
unsigned long psw_mask, addr, flags;
|
||||
unsigned long psw_mask, addr;
|
||||
psw_t psw_ext_save, psw_wait;
|
||||
union ctlreg0 cr0, cr0_new;
|
||||
|
||||
raw_local_irq_save(flags);
|
||||
__ctl_store(cr0.val, 0, 0);
|
||||
cr0_new.val = cr0.val & ~CR0_IRQ_SUBCLASS_MASK;
|
||||
cr0_new.lap = 0;
|
||||
|
@ -53,165 +48,173 @@ static void _sclp_wait_int(void)
|
|||
|
||||
S390_lowcore.external_new_psw = psw_ext_save;
|
||||
__ctl_load(cr0.val, 0, 0);
|
||||
raw_local_irq_restore(flags);
|
||||
}
|
||||
|
||||
static int _sclp_servc(unsigned int cmd, char *sccb)
|
||||
int sclp_early_cmd_sync(sclp_cmdw_t cmd, void *sccb)
|
||||
{
|
||||
unsigned int cc;
|
||||
|
||||
do {
|
||||
asm volatile(
|
||||
" .insn rre,0xb2200000,%1,%2\n"
|
||||
" ipm %0\n"
|
||||
: "=d" (cc) : "d" (cmd), "a" (sccb)
|
||||
: "cc", "memory");
|
||||
cc >>= 28;
|
||||
if (cc == 3)
|
||||
return -EINVAL;
|
||||
_sclp_wait_int();
|
||||
} while (cc != 0);
|
||||
return (*(unsigned short *)(sccb + 6) == 0x20) ? 0 : -EIO;
|
||||
}
|
||||
|
||||
static int _sclp_setup(int disable)
|
||||
{
|
||||
static unsigned char init_sccb[] = {
|
||||
0x00, 0x1c,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x04,
|
||||
0x80, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x40,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||
};
|
||||
unsigned int *masks;
|
||||
unsigned long flags;
|
||||
int rc;
|
||||
|
||||
memcpy(_sclp_work_area, init_sccb, 28);
|
||||
masks = (unsigned int *)(_sclp_work_area + 12);
|
||||
if (disable)
|
||||
memset(masks, 0, 16);
|
||||
/* SCLP write mask */
|
||||
rc = _sclp_servc(0x00780005, _sclp_work_area);
|
||||
raw_local_irq_save(flags);
|
||||
rc = sclp_service_call(cmd, sccb);
|
||||
if (rc)
|
||||
return rc;
|
||||
have_vt220 = masks[2] & EVTYP_VT220MSG_MASK;
|
||||
have_linemode = masks[2] & EVTYP_MSG_MASK;
|
||||
goto out;
|
||||
sclp_early_wait_irq();
|
||||
out:
|
||||
raw_local_irq_restore(flags);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int sclp_early_cmd(sclp_cmdw_t cmd, void *sccb)
|
||||
{
|
||||
int rc;
|
||||
|
||||
do {
|
||||
rc = sclp_early_cmd_sync(cmd, sccb);
|
||||
} while (rc == -EBUSY);
|
||||
if (rc)
|
||||
return -EIO;
|
||||
if (((struct sccb_header *) sccb)->response_code != 0x0020)
|
||||
return -EIO;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Output multi-line text using SCLP Message interface. */
|
||||
static void _sclp_print_lm(const char *str, unsigned int len)
|
||||
{
|
||||
static unsigned char write_head[] = {
|
||||
/* sccb header */
|
||||
0x00, 0x52, /* 0 */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 2 */
|
||||
/* evbuf */
|
||||
0x00, 0x4a, /* 8 */
|
||||
0x02, 0x00, 0x00, 0x00, /* 10 */
|
||||
/* mdb */
|
||||
0x00, 0x44, /* 14 */
|
||||
0x00, 0x01, /* 16 */
|
||||
0xd4, 0xc4, 0xc2, 0x40, /* 18 */
|
||||
0x00, 0x00, 0x00, 0x01, /* 22 */
|
||||
/* go */
|
||||
0x00, 0x38, /* 26 */
|
||||
0x00, 0x01, /* 28 */
|
||||
0x00, 0x00, 0x00, 0x00, /* 30 */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 34 */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 42 */
|
||||
0x00, 0x00, 0x00, 0x00, /* 50 */
|
||||
0x00, 0x00, /* 54 */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 56 */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 64 */
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 72 */
|
||||
0x00, 0x00, /* 80 */
|
||||
};
|
||||
static unsigned char write_mto[] = {
|
||||
/* mto */
|
||||
0x00, 0x0a, /* 0 */
|
||||
0x00, 0x04, /* 2 */
|
||||
0x10, 0x00, /* 4 */
|
||||
0x00, 0x00, 0x00, 0x00 /* 6 */
|
||||
};
|
||||
unsigned char *ptr, *end_ptr, ch;
|
||||
unsigned int count, num;
|
||||
struct write_sccb {
|
||||
struct sccb_header header;
|
||||
struct msg_buf msg;
|
||||
} __packed;
|
||||
|
||||
num = 0;
|
||||
memcpy(_sclp_work_area, write_head, sizeof(write_head));
|
||||
ptr = _sclp_work_area + sizeof(write_head);
|
||||
end_ptr = _sclp_work_area + sizeof(_sclp_work_area) - 1;
|
||||
/* Output multi-line text using SCLP Message interface. */
|
||||
static void sclp_early_print_lm(const char *str, unsigned int len)
|
||||
{
|
||||
unsigned char *ptr, *end, ch;
|
||||
unsigned int count, offset;
|
||||
struct write_sccb *sccb;
|
||||
struct msg_buf *msg;
|
||||
struct mdb *mdb;
|
||||
struct mto *mto;
|
||||
struct go *go;
|
||||
|
||||
sccb = (struct write_sccb *) &sclp_early_sccb;
|
||||
end = (unsigned char *) sccb + sizeof(sclp_early_sccb) - 1;
|
||||
memset(sccb, 0, sizeof(*sccb));
|
||||
ptr = (unsigned char *) &sccb->msg.mdb.mto;
|
||||
offset = 0;
|
||||
do {
|
||||
if (ptr + sizeof(write_mto) > end_ptr)
|
||||
break;
|
||||
memcpy(ptr, write_mto, sizeof(write_mto));
|
||||
for (count = sizeof(write_mto); num < len; count++) {
|
||||
num++;
|
||||
ch = *str++;
|
||||
if (ch == 0x0a)
|
||||
break;
|
||||
if (ptr > end_ptr)
|
||||
for (count = sizeof(*mto); offset < len; count++) {
|
||||
ch = str[offset++];
|
||||
if ((ch == 0x0a) || (ptr + count > end))
|
||||
break;
|
||||
ptr[count] = _ascebc[ch];
|
||||
}
|
||||
/* Update length fields in mto, mdb, evbuf and sccb */
|
||||
*(unsigned short *) ptr = count;
|
||||
*(unsigned short *)(_sclp_work_area + 14) += count;
|
||||
*(unsigned short *)(_sclp_work_area + 8) += count;
|
||||
*(unsigned short *)(_sclp_work_area + 0) += count;
|
||||
mto = (struct mto *) ptr;
|
||||
memset(mto, 0, sizeof(*mto));
|
||||
mto->length = count;
|
||||
mto->type = 4;
|
||||
mto->line_type_flags = LNTPFLGS_ENDTEXT;
|
||||
ptr += count;
|
||||
} while (num < len);
|
||||
|
||||
/* SCLP write data */
|
||||
_sclp_servc(0x00760005, _sclp_work_area);
|
||||
} while ((offset < len) && (ptr + sizeof(*mto) <= end));
|
||||
len = ptr - (unsigned char *) sccb;
|
||||
sccb->header.length = len - offsetof(struct write_sccb, header);
|
||||
msg = &sccb->msg;
|
||||
msg->header.type = EVTYP_MSG;
|
||||
msg->header.length = len - offsetof(struct write_sccb, msg.header);
|
||||
mdb = &msg->mdb;
|
||||
mdb->header.type = 1;
|
||||
mdb->header.tag = 0xD4C4C240;
|
||||
mdb->header.revision_code = 1;
|
||||
mdb->header.length = len - offsetof(struct write_sccb, msg.mdb.header);
|
||||
go = &mdb->go;
|
||||
go->length = sizeof(*go);
|
||||
go->type = 1;
|
||||
sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_DATA, sccb);
|
||||
}
|
||||
|
||||
struct vt220_sccb {
|
||||
struct sccb_header header;
|
||||
struct {
|
||||
struct evbuf_header header;
|
||||
char data[];
|
||||
} msg;
|
||||
} __packed;
|
||||
|
||||
/* Output multi-line text (plus a newline) using SCLP VT220
|
||||
* interface.
|
||||
*/
|
||||
static void _sclp_print_vt220(const char *str, unsigned int len)
|
||||
static void sclp_early_print_vt220(const char *str, unsigned int len)
|
||||
{
|
||||
static unsigned char const write_head[] = {
|
||||
/* sccb header */
|
||||
0x00, 0x0e,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
/* evbuf header */
|
||||
0x00, 0x06,
|
||||
0x1a, 0x00, 0x00, 0x00,
|
||||
};
|
||||
struct vt220_sccb *sccb;
|
||||
|
||||
if (sizeof(write_head) + len >= sizeof(_sclp_work_area))
|
||||
len = sizeof(_sclp_work_area) - sizeof(write_head) - 1;
|
||||
sccb = (struct vt220_sccb *) &sclp_early_sccb;
|
||||
if (sizeof(*sccb) + len >= sizeof(sclp_early_sccb))
|
||||
len = sizeof(sclp_early_sccb) - sizeof(*sccb) - 1;
|
||||
memset(sccb, 0, sizeof(*sccb));
|
||||
memcpy(&sccb->msg.data, str, len);
|
||||
sccb->msg.data[len] = '\n';
|
||||
sccb->header.length = sizeof(*sccb) + len + 1;
|
||||
sccb->msg.header.length = sizeof(sccb->msg) + len + 1;
|
||||
sccb->msg.header.type = EVTYP_VT220MSG;
|
||||
sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_DATA, sccb);
|
||||
}
|
||||
|
||||
memcpy(_sclp_work_area, write_head, sizeof(write_head));
|
||||
memcpy(_sclp_work_area + sizeof(write_head), str, len);
|
||||
_sclp_work_area[sizeof(write_head) + len] = '\n';
|
||||
int sclp_early_set_event_mask(struct init_sccb *sccb,
|
||||
unsigned long receive_mask,
|
||||
unsigned long send_mask)
|
||||
{
|
||||
memset(sccb, 0, sizeof(*sccb));
|
||||
sccb->header.length = sizeof(*sccb);
|
||||
sccb->mask_length = sizeof(sccb_mask_t);
|
||||
sccb->receive_mask = receive_mask;
|
||||
sccb->send_mask = send_mask;
|
||||
return sclp_early_cmd(SCLP_CMDW_WRITE_EVENT_MASK, sccb);
|
||||
}
|
||||
|
||||
/* Update length fields in evbuf and sccb headers */
|
||||
*(unsigned short *)(_sclp_work_area + 8) += len + 1;
|
||||
*(unsigned short *)(_sclp_work_area + 0) += len + 1;
|
||||
unsigned int sclp_early_con_check_linemode(struct init_sccb *sccb)
|
||||
{
|
||||
if (!(sccb->sclp_send_mask & EVTYP_OPCMD_MASK))
|
||||
return 0;
|
||||
if (!(sccb->sclp_receive_mask & (EVTYP_MSG_MASK | EVTYP_PMSGCMD_MASK)))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* SCLP write data */
|
||||
(void)_sclp_servc(0x00760005, _sclp_work_area);
|
||||
static int sclp_early_setup(int disable, int *have_linemode, int *have_vt220)
|
||||
{
|
||||
unsigned long receive_mask, send_mask;
|
||||
struct init_sccb *sccb;
|
||||
int rc;
|
||||
|
||||
*have_linemode = *have_vt220 = 0;
|
||||
sccb = (struct init_sccb *) &sclp_early_sccb;
|
||||
receive_mask = disable ? 0 : EVTYP_OPCMD_MASK;
|
||||
send_mask = disable ? 0 : EVTYP_VT220MSG_MASK | EVTYP_MSG_MASK;
|
||||
rc = sclp_early_set_event_mask(sccb, receive_mask, send_mask);
|
||||
if (rc)
|
||||
return rc;
|
||||
*have_linemode = sclp_early_con_check_linemode(sccb);
|
||||
*have_vt220 = sccb->send_mask & EVTYP_VT220MSG_MASK;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Output one or more lines of text on the SCLP console (VT220 and /
|
||||
* or line-mode). All lines get terminated; no need for a trailing LF.
|
||||
*/
|
||||
void __sclp_print_early(const char *str, unsigned int len)
|
||||
void __sclp_early_printk(const char *str, unsigned int len)
|
||||
{
|
||||
int have_linemode, have_vt220;
|
||||
|
||||
if (sclp_init_state != sclp_init_state_uninitialized)
|
||||
return;
|
||||
if (_sclp_setup(0) != 0)
|
||||
if (sclp_early_setup(0, &have_linemode, &have_vt220) != 0)
|
||||
return;
|
||||
if (have_linemode)
|
||||
_sclp_print_lm(str, len);
|
||||
sclp_early_print_lm(str, len);
|
||||
if (have_vt220)
|
||||
_sclp_print_vt220(str, len);
|
||||
_sclp_setup(1);
|
||||
sclp_early_print_vt220(str, len);
|
||||
sclp_early_setup(1, &have_linemode, &have_vt220);
|
||||
}
|
||||
|
||||
void _sclp_print_early(const char *str)
|
||||
void sclp_early_printk(const char *str)
|
||||
{
|
||||
__sclp_print_early(str, strlen(str));
|
||||
__sclp_early_printk(str, strlen(str));
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue