Blame view
drivers/s390/char/sclp_early.c
7.17 KB
e657d8fe2
|
1 2 3 4 5 6 7 8 |
/* * SCLP early driver * * Copyright IBM Corp. 2013 */ #define KMSG_COMPONENT "sclp_early" #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt |
acf6a004e
|
9 |
#include <asm/ctl_reg.h> |
e657d8fe2
|
10 11 12 13 |
#include <asm/sclp.h> #include <asm/ipl.h> #include "sclp_sdias.h" #include "sclp.h" |
acf6a004e
|
14 15 16 17 18 19 20 |
#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 */ |
cf813db0b
|
21 22 |
u8 _reserved0[16 - 11]; /* 11-15 */ u16 ncpurl; /* 16-17 */ |
217a44068
|
23 24 |
u16 cpuoff; /* 18-19 */ u8 _reserved7[24 - 20]; /* 20-23 */ |
acf6a004e
|
25 26 27 |
u8 loadparm[8]; /* 24-31 */ u8 _reserved1[48 - 32]; /* 32-47 */ u64 facilities; /* 48-55 */ |
570126d37
|
28 29 30 |
u8 _reserved2a[76 - 56]; /* 56-75 */ u32 ibc; /* 76-79 */ u8 _reserved2b[84 - 80]; /* 80-83 */ |
acf6a004e
|
31 32 33 34 35 36 37 |
u8 fac84; /* 84 */ u8 fac85; /* 85 */ u8 _reserved3[91 - 86]; /* 86-90 */ u8 flags; /* 91 */ u8 _reserved4[100 - 92]; /* 92-99 */ u32 rnsize2; /* 100-103 */ u64 rnmax2; /* 104-111 */ |
cf813db0b
|
38 39 40 |
u8 _reserved5[120 - 112]; /* 112-119 */ u16 hcpua; /* 120-121 */ u8 _reserved6[4096 - 122]; /* 122-4095 */ |
acf6a004e
|
41 |
} __packed __aligned(PAGE_SIZE); |
56e57a84a
|
42 |
static char sccb_early[PAGE_SIZE] __aligned(PAGE_SIZE) __initdata; |
52733e015
|
43 44 |
static unsigned int sclp_con_has_vt220 __initdata; static unsigned int sclp_con_has_linemode __initdata; |
e657d8fe2
|
45 |
static unsigned long sclp_hsa_size; |
cf813db0b
|
46 |
static unsigned int sclp_max_cpu; |
333cce91f
|
47 |
static struct sclp_ipl_info sclp_ipl_info; |
217a44068
|
48 |
static unsigned char sclp_siif; |
570126d37
|
49 |
static u32 sclp_ibc; |
e657d8fe2
|
50 |
|
acf6a004e
|
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
u64 sclp_facilities; u8 sclp_fac84; unsigned long long sclp_rzm; unsigned long long sclp_rnmax; 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; } |
56e57a84a
|
73 |
static int __init sclp_read_info_early(struct read_info_sccb *sccb) |
acf6a004e
|
74 |
{ |
333cce91f
|
75 |
int rc, i; |
acf6a004e
|
76 77 |
sclp_cmdw_t commands[] = {SCLP_CMDW_READ_SCP_INFO_FORCED, SCLP_CMDW_READ_SCP_INFO}; |
acf6a004e
|
78 79 80 81 82 83 84 85 86 87 88 |
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; |
333cce91f
|
89 90 |
if (sccb->header.response_code == 0x10) return 0; |
acf6a004e
|
91 92 93 |
if (sccb->header.response_code != 0x1f0) break; } |
333cce91f
|
94 |
return -EIO; |
acf6a004e
|
95 |
} |
5d5de1a06
|
96 |
static void __init sclp_facilities_detect(struct read_info_sccb *sccb) |
acf6a004e
|
97 |
{ |
217a44068
|
98 99 |
struct sclp_cpu_entry *cpue; u16 boot_cpu_address, cpu; |
56e57a84a
|
100 |
if (sclp_read_info_early(sccb)) |
acf6a004e
|
101 |
return; |
acf6a004e
|
102 103 104 105 106 107 108 |
sclp_facilities = sccb->facilities; sclp_fac84 = sccb->fac84; 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; |
570126d37
|
109 |
sclp_ibc = sccb->ibc; |
333cce91f
|
110 |
|
cf813db0b
|
111 112 113 114 115 116 117 118 |
if (!sccb->hcpua) { if (MACHINE_IS_VM) sclp_max_cpu = 64; else sclp_max_cpu = sccb->ncpurl; } else { sclp_max_cpu = sccb->hcpua + 1; } |
217a44068
|
119 120 121 122 123 124 125 126 |
boot_cpu_address = stap(); cpue = (void *)sccb + sccb->cpuoff; for (cpu = 0; cpu < sccb->ncpurl; cpue++, cpu++) { if (boot_cpu_address != cpue->address) continue; sclp_siif = cpue->siif; break; } |
333cce91f
|
127 128 129 130 131 |
/* 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); |
acf6a004e
|
132 133 134 135 |
} bool __init sclp_has_linemode(void) { |
52733e015
|
136 |
return !!sclp_con_has_linemode; |
acf6a004e
|
137 138 139 140 |
} bool __init sclp_has_vt220(void) { |
52733e015
|
141 |
return !!sclp_con_has_vt220; |
acf6a004e
|
142 143 144 145 146 147 148 149 150 151 152 |
} unsigned long long sclp_get_rnmax(void) { return sclp_rnmax; } unsigned long long sclp_get_rzm(void) { return sclp_rzm; } |
cf813db0b
|
153 154 155 156 |
unsigned int sclp_get_max_cpu(void) { return sclp_max_cpu; } |
217a44068
|
157 158 159 160 161 |
int sclp_has_siif(void) { return sclp_siif; } EXPORT_SYMBOL(sclp_has_siif); |
570126d37
|
162 163 164 165 166 |
unsigned int sclp_get_ibc(void) { return sclp_ibc; } EXPORT_SYMBOL(sclp_get_ibc); |
acf6a004e
|
167 168 |
/* * This function will be called after sclp_facilities_detect(), which gets |
333cce91f
|
169 170 |
* called from early.c code. The sclp_facilities_detect() function retrieves * and saves the IPL information. |
acf6a004e
|
171 172 173 |
*/ void __init sclp_get_ipl_info(struct sclp_ipl_info *info) { |
333cce91f
|
174 |
*info = sclp_ipl_info; |
acf6a004e
|
175 |
} |
e657d8fe2
|
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 |
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; } |
5d5de1a06
|
203 204 |
static int __init sclp_set_event_mask(struct init_sccb *sccb, unsigned long receive_mask, |
e657d8fe2
|
205 206 |
unsigned long send_mask) { |
e657d8fe2
|
207 208 209 210 211 212 213 |
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); } |
5d5de1a06
|
214 |
static long __init sclp_hsa_size_init(struct sdias_sccb *sccb) |
e657d8fe2
|
215 |
{ |
e657d8fe2
|
216 217 218 |
sccb_init_eq_size(sccb); if (sclp_cmd_early(SCLP_CMDW_WRITE_EVENT_DATA, sccb)) return -EIO; |
9499934f7
|
219 220 221 |
if (sccb->evbuf.blk_cnt == 0) return 0; return (sccb->evbuf.blk_cnt - 1) * PAGE_SIZE; |
e657d8fe2
|
222 |
} |
5d5de1a06
|
223 |
static long __init sclp_hsa_copy_wait(struct sccb_header *sccb) |
e657d8fe2
|
224 |
{ |
e657d8fe2
|
225 226 227 228 |
memset(sccb, 0, PAGE_SIZE); sccb->length = PAGE_SIZE; if (sclp_cmd_early(SCLP_CMDW_READ_EVENT_DATA, sccb)) return -EIO; |
9499934f7
|
229 230 |
if (((struct sdias_sccb *) sccb)->evbuf.blk_cnt == 0) return 0; |
e657d8fe2
|
231 232 233 234 235 236 237 |
return (((struct sdias_sccb *) sccb)->evbuf.blk_cnt - 1) * PAGE_SIZE; } unsigned long sclp_get_hsa_size(void) { return sclp_hsa_size; } |
5d5de1a06
|
238 |
static void __init sclp_hsa_size_detect(void *sccb) |
e657d8fe2
|
239 240 241 242 |
{ long size; /* First try synchronous interface (LPAR) */ |
5d5de1a06
|
243 |
if (sclp_set_event_mask(sccb, 0, 0x40000010)) |
e657d8fe2
|
244 |
return; |
5d5de1a06
|
245 |
size = sclp_hsa_size_init(sccb); |
e657d8fe2
|
246 247 248 249 250 |
if (size < 0) return; if (size != 0) goto out; /* Then try asynchronous interface (z/VM) */ |
5d5de1a06
|
251 |
if (sclp_set_event_mask(sccb, 0x00000010, 0x40000010)) |
e657d8fe2
|
252 |
return; |
5d5de1a06
|
253 |
size = sclp_hsa_size_init(sccb); |
e657d8fe2
|
254 255 |
if (size < 0) return; |
5d5de1a06
|
256 |
size = sclp_hsa_copy_wait(sccb); |
e657d8fe2
|
257 258 259 |
if (size < 0) return; out: |
e657d8fe2
|
260 261 |
sclp_hsa_size = size; } |
7b50da53f
|
262 |
|
52733e015
|
263 264 |
static unsigned int __init sclp_con_check_linemode(struct init_sccb *sccb) { |
ea61a579a
|
265 |
if (!(sccb->sclp_send_mask & EVTYP_OPCMD_MASK)) |
52733e015
|
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 |
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_con_has_vt220 = 1; if (sclp_con_check_linemode(sccb)) sclp_con_has_linemode = 1; } |
7b50da53f
|
283 284 |
void __init sclp_early_detect(void) { |
5d5de1a06
|
285 286 287 288 |
void *sccb = &sccb_early; sclp_facilities_detect(sccb); sclp_hsa_size_detect(sccb); |
52733e015
|
289 290 291 292 |
/* Turn off SCLP event notifications. Also save remote masks in the * sccb. These are sufficient to detect sclp console capabilities. */ |
5d5de1a06
|
293 |
sclp_set_event_mask(sccb, 0, 0); |
52733e015
|
294 |
sclp_console_detect(sccb); |
7b50da53f
|
295 |
} |