| 1 | /* |
| 2 | * Channel IO definitions |
| 3 | * |
| 4 | * Copyright (c) 2013 Alexander Graf <agraf@suse.de> |
| 5 | * |
| 6 | * Inspired by various s390 headers in Linux 3.9. |
| 7 | * |
| 8 | * This work is licensed under the terms of the GNU GPL, version 2 or (at |
| 9 | * your option) any later version. See the COPYING file in the top-level |
| 10 | * directory. |
| 11 | */ |
| 12 | |
| 13 | #ifndef CIO_H |
| 14 | #define CIO_H |
| 15 | |
| 16 | /* |
| 17 | * path management control word |
| 18 | */ |
| 19 | struct pmcw { |
| 20 | u32 intparm; /* interruption parameter */ |
| 21 | u32 qf:1; /* qdio facility */ |
| 22 | u32 w:1; |
| 23 | u32 isc:3; /* interruption subclass */ |
| 24 | u32 res5:3; /* reserved zeros */ |
| 25 | u32 ena:1; /* enabled */ |
| 26 | u32 lm:2; /* limit mode */ |
| 27 | u32 mme:2; /* measurement-mode enable */ |
| 28 | u32 mp:1; /* multipath mode */ |
| 29 | u32 tf:1; /* timing facility */ |
| 30 | u32 dnv:1; /* device number valid */ |
| 31 | u32 dev:16; /* device number */ |
| 32 | u8 lpm; /* logical path mask */ |
| 33 | u8 pnom; /* path not operational mask */ |
| 34 | u8 lpum; /* last path used mask */ |
| 35 | u8 pim; /* path installed mask */ |
| 36 | u16 mbi; /* measurement-block index */ |
| 37 | u8 pom; /* path operational mask */ |
| 38 | u8 pam; /* path available mask */ |
| 39 | u8 chpid[8]; /* CHPID 0-7 (if available) */ |
| 40 | u32 unused1:8; /* reserved zeros */ |
| 41 | u32 st:3; /* subchannel type */ |
| 42 | u32 unused2:18; /* reserved zeros */ |
| 43 | u32 mbfc:1; /* measurement block format control */ |
| 44 | u32 xmwme:1; /* extended measurement word mode enable */ |
| 45 | u32 csense:1; /* concurrent sense; can be enabled ...*/ |
| 46 | /* ... per MSCH, however, if facility */ |
| 47 | /* ... is not installed, this results */ |
| 48 | /* ... in an operand exception. */ |
| 49 | } __attribute__ ((packed)); |
| 50 | |
| 51 | /* Target SCHIB configuration. */ |
| 52 | struct schib_config { |
| 53 | u64 mba; |
| 54 | u32 intparm; |
| 55 | u16 mbi; |
| 56 | u32 isc:3; |
| 57 | u32 ena:1; |
| 58 | u32 mme:2; |
| 59 | u32 mp:1; |
| 60 | u32 csense:1; |
| 61 | u32 mbfc:1; |
| 62 | } __attribute__ ((packed)); |
| 63 | |
| 64 | struct scsw { |
| 65 | u16 flags; |
| 66 | u16 ctrl; |
| 67 | u32 cpa; |
| 68 | u8 dstat; |
| 69 | u8 cstat; |
| 70 | u16 count; |
| 71 | } __attribute__ ((packed)); |
| 72 | |
| 73 | /* Function Control */ |
| 74 | #define SCSW_FCTL_START_FUNC 0x4000 |
| 75 | #define SCSW_FCTL_HALT_FUNC 0x2000 |
| 76 | #define SCSW_FCTL_CLEAR_FUNC 0x1000 |
| 77 | |
| 78 | /* Activity Control */ |
| 79 | #define SCSW_ACTL_RESUME_PEND 0x0800 |
| 80 | #define SCSW_ACTL_START_PEND 0x0400 |
| 81 | #define SCSW_ACTL_HALT_PEND 0x0200 |
| 82 | #define SCSW_ACTL_CLEAR_PEND 0x0100 |
| 83 | #define SCSW_ACTL_CH_ACTIVE 0x0080 |
| 84 | #define SCSW_ACTL_DEV_ACTIVE 0x0040 |
| 85 | #define SCSW_ACTL_SUSPENDED 0x0020 |
| 86 | |
| 87 | /* Status Control */ |
| 88 | #define SCSW_SCTL_ALERT 0x0010 |
| 89 | #define SCSW_SCTL_INTERMED 0x0008 |
| 90 | #define SCSW_SCTL_PRIMARY 0x0004 |
| 91 | #define SCSW_SCTL_SECONDARY 0x0002 |
| 92 | #define SCSW_SCTL_STATUS_PEND 0x0001 |
| 93 | |
| 94 | /* SCSW Device Status Flags */ |
| 95 | #define SCSW_DSTAT_ATTN 0x80 |
| 96 | #define SCSW_DSTAT_STATMOD 0x40 |
| 97 | #define SCSW_DSTAT_CUEND 0x20 |
| 98 | #define SCSW_DSTAT_BUSY 0x10 |
| 99 | #define SCSW_DSTAT_CHEND 0x08 |
| 100 | #define SCSW_DSTAT_DEVEND 0x04 |
| 101 | #define SCSW_DSTAT_UCHK 0x02 |
| 102 | #define SCSW_DSTAT_UEXCP 0x01 |
| 103 | |
| 104 | /* SCSW Subchannel Status Flags */ |
| 105 | #define SCSW_CSTAT_PCINT 0x80 |
| 106 | #define SCSW_CSTAT_BADLEN 0x40 |
| 107 | #define SCSW_CSTAT_PROGCHK 0x20 |
| 108 | #define SCSW_CSTAT_PROTCHK 0x10 |
| 109 | #define SCSW_CSTAT_CHDCHK 0x08 |
| 110 | #define SCSW_CSTAT_CHCCHK 0x04 |
| 111 | #define SCSW_CSTAT_ICCHK 0x02 |
| 112 | #define SCSW_CSTAT_CHAINCHK 0x01 |
| 113 | |
| 114 | /* |
| 115 | * subchannel information block |
| 116 | */ |
| 117 | typedef struct schib { |
| 118 | struct pmcw pmcw; /* path management control word */ |
| 119 | struct scsw scsw; /* subchannel status word */ |
| 120 | u64 mba; /* measurement block address */ |
| 121 | u8 mda[4]; /* model dependent area */ |
| 122 | } __attribute__ ((packed, aligned(4))) Schib; |
| 123 | |
| 124 | typedef struct subchannel_id { |
| 125 | union { |
| 126 | struct { |
| 127 | u16 cssid:8; |
| 128 | u16 reserved:4; |
| 129 | u16 m:1; |
| 130 | u16 ssid:2; |
| 131 | u16 one:1; |
| 132 | }; |
| 133 | u16 sch_id; |
| 134 | }; |
| 135 | u16 sch_no; |
| 136 | } __attribute__ ((packed, aligned(4))) SubChannelId; |
| 137 | |
| 138 | struct chsc_header { |
| 139 | u16 length; |
| 140 | u16 code; |
| 141 | } __attribute__((packed)); |
| 142 | |
| 143 | typedef struct chsc_area_sda { |
| 144 | struct chsc_header request; |
| 145 | u8 reserved1:4; |
| 146 | u8 format:4; |
| 147 | u8 reserved2; |
| 148 | u16 operation_code; |
| 149 | u32 reserved3; |
| 150 | u32 reserved4; |
| 151 | u32 operation_data_area[252]; |
| 152 | struct chsc_header response; |
| 153 | u32 reserved5:4; |
| 154 | u32 format2:4; |
| 155 | u32 reserved6:24; |
| 156 | } __attribute__((packed)) ChscAreaSda; |
| 157 | |
| 158 | /* |
| 159 | * TPI info structure |
| 160 | */ |
| 161 | struct tpi_info { |
| 162 | struct subchannel_id schid; |
| 163 | u32 intparm; /* interruption parameter */ |
| 164 | u32 adapter_IO:1; |
| 165 | u32 reserved2:1; |
| 166 | u32 isc:3; |
| 167 | u32 reserved3:12; |
| 168 | u32 int_type:3; |
| 169 | u32 reserved4:12; |
| 170 | } __attribute__ ((packed, aligned(4))); |
| 171 | |
| 172 | /* channel command word (format 0) */ |
| 173 | typedef struct ccw0 { |
| 174 | u8 cmd_code; |
| 175 | u32 cda:24; |
| 176 | u32 chainData:1; |
| 177 | u32 chain:1; |
| 178 | u32 sli:1; |
| 179 | u32 skip:1; |
| 180 | u32 pci:1; |
| 181 | u32 ida:1; |
| 182 | u32 suspend:1; |
| 183 | u32 mida:1; |
| 184 | u8 reserved; |
| 185 | u16 count; |
| 186 | } __attribute__ ((packed, aligned(8))) Ccw0; |
| 187 | |
| 188 | /* channel command word (format 1) */ |
| 189 | typedef struct ccw1 { |
| 190 | u8 cmd_code; |
| 191 | u8 flags; |
| 192 | u16 count; |
| 193 | u32 cda; |
| 194 | } __attribute__ ((packed, aligned(8))) Ccw1; |
| 195 | |
| 196 | /* do_cio() CCW formats */ |
| 197 | #define CCW_FMT0 0x00 |
| 198 | #define CCW_FMT1 0x01 |
| 199 | |
| 200 | #define CCW_FLAG_DC 0x80 |
| 201 | #define CCW_FLAG_CC 0x40 |
| 202 | #define CCW_FLAG_SLI 0x20 |
| 203 | #define CCW_FLAG_SKIP 0x10 |
| 204 | #define CCW_FLAG_PCI 0x08 |
| 205 | #define CCW_FLAG_IDA 0x04 |
| 206 | #define CCW_FLAG_SUSPEND 0x02 |
| 207 | |
| 208 | /* Common CCW commands */ |
| 209 | #define CCW_CMD_READ_IPL 0x02 |
| 210 | #define CCW_CMD_NOOP 0x03 |
| 211 | #define CCW_CMD_BASIC_SENSE 0x04 |
| 212 | #define CCW_CMD_TIC 0x08 |
| 213 | #define CCW_CMD_SENSE_ID 0xe4 |
| 214 | |
| 215 | /* Virtio CCW commands */ |
| 216 | #define CCW_CMD_SET_VQ 0x13 |
| 217 | #define CCW_CMD_VDEV_RESET 0x33 |
| 218 | #define CCW_CMD_READ_FEAT 0x12 |
| 219 | #define CCW_CMD_WRITE_FEAT 0x11 |
| 220 | #define CCW_CMD_READ_CONF 0x22 |
| 221 | #define CCW_CMD_WRITE_CONF 0x21 |
| 222 | #define CCW_CMD_WRITE_STATUS 0x31 |
| 223 | #define CCW_CMD_SET_IND 0x43 |
| 224 | #define CCW_CMD_SET_CONF_IND 0x53 |
| 225 | #define CCW_CMD_READ_VQ_CONF 0x32 |
| 226 | |
| 227 | /* DASD CCW commands */ |
| 228 | #define CCW_CMD_DASD_READ 0x06 |
| 229 | #define CCW_CMD_DASD_SEEK 0x07 |
| 230 | #define CCW_CMD_DASD_SEARCH_ID_EQ 0x31 |
| 231 | #define CCW_CMD_DASD_READ_MT 0x86 |
| 232 | |
| 233 | /* |
| 234 | * Command-mode operation request block |
| 235 | */ |
| 236 | typedef struct cmd_orb { |
| 237 | u32 intparm; /* interruption parameter */ |
| 238 | u32 key:4; /* flags, like key, suspend control, etc. */ |
| 239 | u32 spnd:1; /* suspend control */ |
| 240 | u32 res1:1; /* reserved */ |
| 241 | u32 mod:1; /* modification control */ |
| 242 | u32 sync:1; /* synchronize control */ |
| 243 | u32 fmt:1; /* format control */ |
| 244 | u32 pfch:1; /* prefetch control */ |
| 245 | u32 isic:1; /* initial-status-interruption control */ |
| 246 | u32 alcc:1; /* address-limit-checking control */ |
| 247 | u32 ssic:1; /* suppress-suspended-interr. control */ |
| 248 | u32 res2:1; /* reserved */ |
| 249 | u32 c64:1; /* IDAW/QDIO 64 bit control */ |
| 250 | u32 i2k:1; /* IDAW 2/4kB block size control */ |
| 251 | u32 lpm:8; /* logical path mask */ |
| 252 | u32 ils:1; /* incorrect length */ |
| 253 | u32 zero:6; /* reserved zeros */ |
| 254 | u32 orbx:1; /* ORB extension control */ |
| 255 | u32 cpa; /* channel program address */ |
| 256 | } __attribute__ ((packed, aligned(4))) CmdOrb; |
| 257 | |
| 258 | struct ciw { |
| 259 | u8 type; |
| 260 | u8 command; |
| 261 | u16 count; |
| 262 | }; |
| 263 | |
| 264 | #define CU_TYPE_UNKNOWN 0x0000 |
| 265 | #define CU_TYPE_DASD_2107 0x2107 |
| 266 | #define CU_TYPE_VIRTIO 0x3832 |
| 267 | #define CU_TYPE_DASD_3990 0x3990 |
| 268 | |
| 269 | /* |
| 270 | * sense-id response buffer layout |
| 271 | */ |
| 272 | typedef struct senseid { |
| 273 | /* common part */ |
| 274 | u8 reserved; /* always 0x'FF' */ |
| 275 | u16 cu_type; /* control unit type */ |
| 276 | u8 cu_model; /* control unit model */ |
| 277 | u16 dev_type; /* device type */ |
| 278 | u8 dev_model; /* device model */ |
| 279 | u8 unused; /* padding byte */ |
| 280 | /* extended part */ |
| 281 | struct ciw ciw[62]; |
| 282 | } __attribute__ ((packed, aligned(4))) SenseId; |
| 283 | |
| 284 | /* |
| 285 | * architected values for first sense byte - common_status. Bits 0-5 of this |
| 286 | * field are common to all device types. |
| 287 | */ |
| 288 | #define SNS_STAT0_CMD_REJECT 0x80 |
| 289 | #define SNS_STAT0_INTERVENTION_REQ 0x40 |
| 290 | #define SNS_STAT0_BUS_OUT_CHECK 0x20 |
| 291 | #define SNS_STAT0_EQUIPMENT_CHECK 0x10 |
| 292 | #define SNS_STAT0_DATA_CHECK 0x08 |
| 293 | #define SNS_STAT0_OVERRUN 0x04 |
| 294 | #define SNS_STAT0_INCOMPL_DOMAIN 0x01 |
| 295 | |
| 296 | /* ECKD DASD status[0] byte */ |
| 297 | #define SNS_STAT1_PERM_ERR 0x80 |
| 298 | #define SNS_STAT1_INV_TRACK_FORMAT 0x40 |
| 299 | #define SNS_STAT1_EOC 0x20 |
| 300 | #define SNS_STAT1_MESSAGE_TO_OPER 0x10 |
| 301 | #define SNS_STAT1_NO_REC_FOUND 0x08 |
| 302 | #define SNS_STAT1_FILE_PROTECTED 0x04 |
| 303 | #define SNS_STAT1_WRITE_INHIBITED 0x02 |
| 304 | #define SNS_STAT1_IMPRECISE_END 0x01 |
| 305 | |
| 306 | /* ECKD DASD status[1] byte */ |
| 307 | #define SNS_STAT2_REQ_INH_WRITE 0x80 |
| 308 | #define SNS_STAT2_CORRECTABLE 0x40 |
| 309 | #define SNS_STAT2_FIRST_LOG_ERR 0x20 |
| 310 | #define SNS_STAT2_ENV_DATA_PRESENT 0x10 |
| 311 | #define SNS_STAT2_IMPRECISE_END 0x04 |
| 312 | |
| 313 | /* ECKD DASD 24-byte Sense fmt_msg codes */ |
| 314 | #define SENSE24_FMT_PROG_SYS 0x0 |
| 315 | #define SENSE24_FMT_EQUIPMENT 0x2 |
| 316 | #define SENSE24_FMT_CONTROLLER 0x3 |
| 317 | #define SENSE24_FMT_MISC 0xF |
| 318 | |
| 319 | /* basic sense response buffer layout */ |
| 320 | typedef struct SenseDataEckdDasd { |
| 321 | uint8_t common_status; |
| 322 | uint8_t status[2]; |
| 323 | uint8_t res_count; |
| 324 | uint8_t phys_drive_id; |
| 325 | uint8_t low_cyl_addr; |
| 326 | uint8_t head_high_cyl_addr; |
| 327 | uint8_t fmt_msg; |
| 328 | uint64_t fmt_dependent_info[2]; |
| 329 | uint8_t reserved; |
| 330 | uint8_t program_action_code; |
| 331 | uint16_t config_info; |
| 332 | uint8_t mcode_hicyl; |
| 333 | uint8_t cyl_head_addr[3]; |
| 334 | } __attribute__ ((packed, aligned(4))) SenseDataEckdDasd; |
| 335 | |
| 336 | #define ECKD_SENSE24_GET_FMT(sd) (sd->fmt_msg & 0xF0 >> 4) |
| 337 | #define ECKD_SENSE24_GET_MSG(sd) (sd->fmt_msg & 0x0F) |
| 338 | |
| 339 | #define unit_check(irb) ((irb)->scsw.dstat & SCSW_DSTAT_UCHK) |
| 340 | #define iface_ctrl_check(irb) ((irb)->scsw.cstat & SCSW_CSTAT_ICCHK) |
| 341 | |
| 342 | /* interruption response block */ |
| 343 | typedef struct irb { |
| 344 | struct scsw scsw; |
| 345 | u32 esw[5]; |
| 346 | u32 ecw[8]; |
| 347 | u32 emw[8]; |
| 348 | } __attribute__ ((packed, aligned(4))) Irb; |
| 349 | |
| 350 | /* Used for SEEK ccw commands */ |
| 351 | typedef struct CcwSeekData { |
| 352 | uint16_t reserved; |
| 353 | uint16_t cyl; |
| 354 | uint16_t head; |
| 355 | } __attribute__((packed)) CcwSeekData; |
| 356 | |
| 357 | /* Used for SEARCH ID ccw commands */ |
| 358 | typedef struct CcwSearchIdData { |
| 359 | uint16_t cyl; |
| 360 | uint16_t head; |
| 361 | uint8_t record; |
| 362 | } __attribute__((packed)) CcwSearchIdData; |
| 363 | |
| 364 | extern SubChannelId net_schid; |
| 365 | |
| 366 | int enable_mss_facility(void); |
| 367 | void enable_subchannel(SubChannelId schid); |
| 368 | uint16_t cu_type(SubChannelId schid); |
| 369 | int basic_sense(SubChannelId schid, uint16_t cutype, void *sense_data, |
| 370 | uint16_t data_size); |
| 371 | int do_cio(SubChannelId schid, uint16_t cutype, uint32_t ccw_addr, int fmt); |
| 372 | |
| 373 | /* |
| 374 | * Some S390 specific IO instructions as inline |
| 375 | */ |
| 376 | |
| 377 | static inline int stsch_err(struct subchannel_id schid, struct schib *addr) |
| 378 | { |
| 379 | register struct subchannel_id reg1 asm ("1") = schid; |
| 380 | int ccode = -EIO; |
| 381 | |
| 382 | asm volatile( |
| 383 | " stsch 0(%3)\n" |
| 384 | "0: ipm %0\n" |
| 385 | " srl %0,28\n" |
| 386 | "1:\n" |
| 387 | : "+d" (ccode), "=m" (*addr) |
| 388 | : "d" (reg1), "a" (addr) |
| 389 | : "cc"); |
| 390 | return ccode; |
| 391 | } |
| 392 | |
| 393 | static inline int msch(struct subchannel_id schid, struct schib *addr) |
| 394 | { |
| 395 | register struct subchannel_id reg1 asm ("1") = schid; |
| 396 | int ccode; |
| 397 | |
| 398 | asm volatile( |
| 399 | " msch 0(%2)\n" |
| 400 | " ipm %0\n" |
| 401 | " srl %0,28" |
| 402 | : "=d" (ccode) |
| 403 | : "d" (reg1), "a" (addr), "m" (*addr) |
| 404 | : "cc"); |
| 405 | return ccode; |
| 406 | } |
| 407 | |
| 408 | static inline int msch_err(struct subchannel_id schid, struct schib *addr) |
| 409 | { |
| 410 | register struct subchannel_id reg1 asm ("1") = schid; |
| 411 | int ccode = -EIO; |
| 412 | |
| 413 | asm volatile( |
| 414 | " msch 0(%2)\n" |
| 415 | "0: ipm %0\n" |
| 416 | " srl %0,28\n" |
| 417 | "1:\n" |
| 418 | : "+d" (ccode) |
| 419 | : "d" (reg1), "a" (addr), "m" (*addr) |
| 420 | : "cc"); |
| 421 | return ccode; |
| 422 | } |
| 423 | |
| 424 | static inline int tsch(struct subchannel_id schid, struct irb *addr) |
| 425 | { |
| 426 | register struct subchannel_id reg1 asm ("1") = schid; |
| 427 | int ccode; |
| 428 | |
| 429 | asm volatile( |
| 430 | " tsch 0(%3)\n" |
| 431 | " ipm %0\n" |
| 432 | " srl %0,28" |
| 433 | : "=d" (ccode), "=m" (*addr) |
| 434 | : "d" (reg1), "a" (addr) |
| 435 | : "cc"); |
| 436 | return ccode; |
| 437 | } |
| 438 | |
| 439 | static inline int ssch(struct subchannel_id schid, struct cmd_orb *addr) |
| 440 | { |
| 441 | register struct subchannel_id reg1 asm("1") = schid; |
| 442 | int ccode = -EIO; |
| 443 | |
| 444 | asm volatile( |
| 445 | " ssch 0(%2)\n" |
| 446 | "0: ipm %0\n" |
| 447 | " srl %0,28\n" |
| 448 | "1:\n" |
| 449 | : "+d" (ccode) |
| 450 | : "d" (reg1), "a" (addr), "m" (*addr) |
| 451 | : "cc", "memory"); |
| 452 | return ccode; |
| 453 | } |
| 454 | |
| 455 | static inline int csch(struct subchannel_id schid) |
| 456 | { |
| 457 | register struct subchannel_id reg1 asm("1") = schid; |
| 458 | int ccode; |
| 459 | |
| 460 | asm volatile( |
| 461 | " csch\n" |
| 462 | " ipm %0\n" |
| 463 | " srl %0,28" |
| 464 | : "=d" (ccode) |
| 465 | : "d" (reg1) |
| 466 | : "cc"); |
| 467 | return ccode; |
| 468 | } |
| 469 | |
| 470 | static inline int tpi(struct tpi_info *addr) |
| 471 | { |
| 472 | int ccode; |
| 473 | |
| 474 | asm volatile( |
| 475 | " tpi 0(%2)\n" |
| 476 | " ipm %0\n" |
| 477 | " srl %0,28" |
| 478 | : "=d" (ccode), "=m" (*addr) |
| 479 | : "a" (addr) |
| 480 | : "cc"); |
| 481 | return ccode; |
| 482 | } |
| 483 | |
| 484 | static inline int chsc(void *chsc_area) |
| 485 | { |
| 486 | typedef struct { char _[4096]; } addr_type; |
| 487 | int cc; |
| 488 | |
| 489 | asm volatile( |
| 490 | " .insn rre,0xb25f0000,%2,0\n" |
| 491 | " ipm %0\n" |
| 492 | " srl %0,28\n" |
| 493 | : "=d" (cc), "=m" (*(addr_type *) chsc_area) |
| 494 | : "d" (chsc_area), "m" (*(addr_type *) chsc_area) |
| 495 | : "cc"); |
| 496 | return cc; |
| 497 | } |
| 498 | |
| 499 | #endif /* CIO_H */ |