mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-09-12 13:55:32 +00:00
![Heiko Carstens](/assets/img/avatar_default.png)
In order to be able to setup the cpu to node mappings early it is a prerequisite to know which cpus are present. Therefore cpus must be detected much earlier than before. For sclp based cpu detection this requires yet another early sclp call, since the system is not ready to use the regular interrupt and memory allocations. Reviewed-by: Michael Holzheu <holzheu@linux.vnet.ibm.com> Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
334 lines
8.3 KiB
C
334 lines
8.3 KiB
C
/*
|
|
* SCLP early driver
|
|
*
|
|
* Copyright IBM Corp. 2013
|
|
*/
|
|
|
|
#define KMSG_COMPONENT "sclp_early"
|
|
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
|
|
|
|
#include <linux/errno.h>
|
|
#include <asm/ctl_reg.h>
|
|
#include <asm/sclp.h>
|
|
#include <asm/ipl.h>
|
|
#include "sclp_sdias.h"
|
|
#include "sclp.h"
|
|
|
|
#define SCLP_CMDW_READ_SCP_INFO 0x00020001
|
|
#define SCLP_CMDW_READ_SCP_INFO_FORCED 0x00120001
|
|
|
|
struct read_info_sccb {
|
|
struct sccb_header header; /* 0-7 */
|
|
u16 rnmax; /* 8-9 */
|
|
u8 rnsize; /* 10 */
|
|
u8 _pad_11[16 - 11]; /* 11-15 */
|
|
u16 ncpurl; /* 16-17 */
|
|
u16 cpuoff; /* 18-19 */
|
|
u8 _pad_20[24 - 20]; /* 20-23 */
|
|
u8 loadparm[8]; /* 24-31 */
|
|
u8 _pad_32[42 - 32]; /* 32-41 */
|
|
u8 fac42; /* 42 */
|
|
u8 fac43; /* 43 */
|
|
u8 _pad_44[48 - 44]; /* 44-47 */
|
|
u64 facilities; /* 48-55 */
|
|
u8 _pad_56[66 - 56]; /* 56-65 */
|
|
u8 fac66; /* 66 */
|
|
u8 _pad_67[76 - 67]; /* 67-83 */
|
|
u32 ibc; /* 76-79 */
|
|
u8 _pad80[84 - 80]; /* 80-83 */
|
|
u8 fac84; /* 84 */
|
|
u8 fac85; /* 85 */
|
|
u8 _pad_86[91 - 86]; /* 86-90 */
|
|
u8 flags; /* 91 */
|
|
u8 _pad_92[99 - 92]; /* 92-98 */
|
|
u8 hamaxpow; /* 99 */
|
|
u32 rnsize2; /* 100-103 */
|
|
u64 rnmax2; /* 104-111 */
|
|
u8 _pad_112[116 - 112]; /* 112-115 */
|
|
u8 fac116; /* 116 */
|
|
u8 fac117; /* 117 */
|
|
u8 _pad_118; /* 118 */
|
|
u8 fac119; /* 119 */
|
|
u16 hcpua; /* 120-121 */
|
|
u8 _pad_122[124 - 122]; /* 122-123 */
|
|
u32 hmfai; /* 124-127 */
|
|
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)
|
|
{
|
|
int rc, i;
|
|
sclp_cmdw_t commands[] = {SCLP_CMDW_READ_SCP_INFO_FORCED,
|
|
SCLP_CMDW_READ_SCP_INFO};
|
|
|
|
for (i = 0; i < ARRAY_SIZE(commands); i++) {
|
|
do {
|
|
memset(sccb, 0, sizeof(*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);
|
|
} while (rc == -EBUSY);
|
|
|
|
if (rc)
|
|
break;
|
|
if (sccb->header.response_code == 0x10)
|
|
return 0;
|
|
if (sccb->header.response_code != 0x1f0)
|
|
break;
|
|
}
|
|
return -EIO;
|
|
}
|
|
|
|
static void __init sclp_facilities_detect(struct read_info_sccb *sccb)
|
|
{
|
|
struct sclp_core_entry *cpue;
|
|
u16 boot_cpu_address, cpu;
|
|
|
|
if (sclp_read_info_early(sccb))
|
|
return;
|
|
|
|
sclp.facilities = sccb->facilities;
|
|
sclp.has_sprp = !!(sccb->fac84 & 0x02);
|
|
sclp.has_core_type = !!(sccb->fac84 & 0x01);
|
|
sclp.has_gsls = !!(sccb->fac85 & 0x80);
|
|
sclp.has_64bscao = !!(sccb->fac116 & 0x80);
|
|
sclp.has_cmma = !!(sccb->fac116 & 0x40);
|
|
sclp.has_esca = !!(sccb->fac116 & 0x08);
|
|
sclp.has_pfmfi = !!(sccb->fac117 & 0x40);
|
|
sclp.has_ibs = !!(sccb->fac117 & 0x20);
|
|
sclp.has_hvs = !!(sccb->fac119 & 0x80);
|
|
if (sccb->fac85 & 0x02)
|
|
S390_lowcore.machine_flags |= MACHINE_FLAG_ESOP;
|
|
sclp.rnmax = sccb->rnmax ? sccb->rnmax : sccb->rnmax2;
|
|
sclp.rzm = sccb->rnsize ? sccb->rnsize : sccb->rnsize2;
|
|
sclp.rzm <<= 20;
|
|
sclp.ibc = sccb->ibc;
|
|
|
|
if (sccb->hamaxpow && sccb->hamaxpow < 64)
|
|
sclp.hamax = (1UL << sccb->hamaxpow) - 1;
|
|
else
|
|
sclp.hamax = U64_MAX;
|
|
|
|
if (!sccb->hcpua) {
|
|
if (MACHINE_IS_VM)
|
|
sclp.max_cores = 64;
|
|
else
|
|
sclp.max_cores = sccb->ncpurl;
|
|
} else {
|
|
sclp.max_cores = sccb->hcpua + 1;
|
|
}
|
|
|
|
boot_cpu_address = stap();
|
|
cpue = (void *)sccb + sccb->cpuoff;
|
|
for (cpu = 0; cpu < sccb->ncpurl; cpue++, cpu++) {
|
|
if (boot_cpu_address != cpue->core_id)
|
|
continue;
|
|
sclp.has_siif = cpue->siif;
|
|
sclp.has_sigpif = cpue->sigpif;
|
|
sclp.has_sief2 = cpue->sief2;
|
|
sclp.has_gpere = cpue->gpere;
|
|
sclp.has_ib = cpue->ib;
|
|
sclp.has_cei = cpue->cei;
|
|
sclp.has_skey = cpue->skey;
|
|
break;
|
|
}
|
|
|
|
/* Save IPL information */
|
|
sclp_ipl_info.is_valid = 1;
|
|
if (sccb->flags & 0x2)
|
|
sclp_ipl_info.has_dump = 1;
|
|
memcpy(&sclp_ipl_info.loadparm, &sccb->loadparm, LOADPARM_LEN);
|
|
|
|
sclp.mtid = (sccb->fac42 & 0x80) ? (sccb->fac42 & 31) : 0;
|
|
sclp.mtid_cp = (sccb->fac42 & 0x80) ? (sccb->fac43 & 31) : 0;
|
|
sclp.mtid_prev = (sccb->fac42 & 0x80) ? (sccb->fac66 & 31) : 0;
|
|
|
|
sclp.hmfai = sccb->hmfai;
|
|
}
|
|
|
|
/*
|
|
* This function will be called after sclp_facilities_detect(), which gets
|
|
* called from early.c code. The sclp_facilities_detect() function retrieves
|
|
* and saves the IPL information.
|
|
*/
|
|
void __init sclp_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;
|
|
|
|
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)
|
|
{
|
|
int rc;
|
|
|
|
if (!SCLP_HAS_CPU_INFO)
|
|
return;
|
|
memset(sccb, 0, sizeof(*sccb));
|
|
sccb->header.length = sizeof(*sccb);
|
|
do {
|
|
rc = sclp_cmd_sync_early(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;
|
|
}
|
|
|
|
int __init _sclp_get_core_info_early(struct sclp_core_info *info)
|
|
{
|
|
if (!sclp_core_info_early_valid)
|
|
return -EIO;
|
|
*info = sclp_core_info_early;
|
|
return 0;
|
|
}
|
|
|
|
static long __init sclp_hsa_size_init(struct sdias_sccb *sccb)
|
|
{
|
|
sccb_init_eq_size(sccb);
|
|
if (sclp_cmd_early(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)
|
|
{
|
|
memset(sccb, 0, PAGE_SIZE);
|
|
sccb->length = PAGE_SIZE;
|
|
if (sclp_cmd_early(SCLP_CMDW_READ_EVENT_DATA, sccb))
|
|
return -EIO;
|
|
if (((struct sdias_sccb *) sccb)->evbuf.blk_cnt == 0)
|
|
return 0;
|
|
return (((struct sdias_sccb *) sccb)->evbuf.blk_cnt - 1) * PAGE_SIZE;
|
|
}
|
|
|
|
static void __init sclp_hsa_size_detect(void *sccb)
|
|
{
|
|
long size;
|
|
|
|
/* 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)
|
|
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;
|
|
out:
|
|
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)
|
|
{
|
|
if (sccb->header.response_code != 0x20)
|
|
return;
|
|
|
|
if (sccb->sclp_send_mask & EVTYP_VT220MSG_MASK)
|
|
sclp.has_vt220 = 1;
|
|
|
|
if (sclp_con_check_linemode(sccb))
|
|
sclp.has_linemode = 1;
|
|
}
|
|
|
|
void __init sclp_early_detect(void)
|
|
{
|
|
void *sccb = &sccb_early;
|
|
|
|
sclp_facilities_detect(sccb);
|
|
sclp_init_core_info_early(sccb);
|
|
sclp_hsa_size_detect(sccb);
|
|
|
|
/* 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);
|
|
}
|