1/* 2 * Author: Martin Peschke <mpeschke@de.ibm.com> 3 * Copyright (C) 2001 IBM Entwicklung GmbH, IBM Corporation 4 * 5 * SCLP Control-Program Identification. 6 */ 7 8#include <linux/version.h> 9#include <linux/kmod.h> 10#include <linux/module.h> 11#include <linux/moduleparam.h> 12#include <linux/init.h> 13#include <linux/timer.h> 14#include <linux/string.h> 15#include <linux/err.h> 16#include <linux/slab.h> 17#include <asm/ebcdic.h> 18#include <asm/semaphore.h> 19 20#include "sclp.h" 21#include "sclp_rw.h" 22 23#define CPI_LENGTH_SYSTEM_TYPE 8 24#define CPI_LENGTH_SYSTEM_NAME 8 25#define CPI_LENGTH_SYSPLEX_NAME 8 26 27struct cpi_evbuf { 28 struct evbuf_header header; 29 u8 id_format; 30 u8 reserved0; 31 u8 system_type[CPI_LENGTH_SYSTEM_TYPE]; 32 u64 reserved1; 33 u8 system_name[CPI_LENGTH_SYSTEM_NAME]; 34 u64 reserved2; 35 u64 system_level; 36 u64 reserved3; 37 u8 sysplex_name[CPI_LENGTH_SYSPLEX_NAME]; 38 u8 reserved4[16]; 39} __attribute__((packed)); 40 41struct cpi_sccb { 42 struct sccb_header header; 43 struct cpi_evbuf cpi_evbuf; 44} __attribute__((packed)); 45 46/* Event type structure for write message and write priority message */ 47static struct sclp_register sclp_cpi_event = 48{ 49 .send_mask = EVTYP_CTLPROGIDENT_MASK 50}; 51 52MODULE_LICENSE("GPL"); 53 54MODULE_AUTHOR( 55 "Martin Peschke, IBM Deutschland Entwicklung GmbH " 56 "<mpeschke@de.ibm.com>"); 57 58MODULE_DESCRIPTION( 59 "identify this operating system instance to the S/390 " 60 "or zSeries hardware"); 61 62static char *system_name = NULL; 63module_param(system_name, charp, 0); 64MODULE_PARM_DESC(system_name, "e.g. hostname - max. 8 characters"); 65 66static char *sysplex_name = NULL; 67#ifdef ALLOW_SYSPLEX_NAME 68module_param(sysplex_name, charp, 0); 69MODULE_PARM_DESC(sysplex_name, "if applicable - max. 8 characters"); 70#endif 71 72/* use default value for this field (as well as for system level) */ 73static char *system_type = "LINUX"; 74 75static int 76cpi_check_parms(void) 77{ 78 /* reject if no system type specified */ 79 if (!system_type) { 80 printk("cpi: bug: no system type specified\n"); 81 return -EINVAL; 82 } 83 84 /* reject if system type larger than 8 characters */ 85 if (strlen(system_type) > CPI_LENGTH_SYSTEM_NAME) { 86 printk("cpi: bug: system type has length of %li characters - " 87 "only %i characters supported\n", 88 strlen(system_type), CPI_LENGTH_SYSTEM_TYPE); 89 return -EINVAL; 90 } 91 92 /* reject if no system name specified */ 93 if (!system_name) { 94 printk("cpi: no system name specified\n"); 95 return -EINVAL; 96 } 97 98 /* reject if system name larger than 8 characters */ 99 if (strlen(system_name) > CPI_LENGTH_SYSTEM_NAME) { 100 printk("cpi: system name has length of %li characters - " 101 "only %i characters supported\n", 102 strlen(system_name), CPI_LENGTH_SYSTEM_NAME); 103 return -EINVAL; 104 } 105 106 /* reject if specified sysplex name larger than 8 characters */ 107 if (sysplex_name && strlen(sysplex_name) > CPI_LENGTH_SYSPLEX_NAME) { 108 printk("cpi: sysplex name has length of %li characters" 109 " - only %i characters supported\n", 110 strlen(sysplex_name), CPI_LENGTH_SYSPLEX_NAME); 111 return -EINVAL; 112 } 113 return 0; 114} 115 116static void 117cpi_callback(struct sclp_req *req, void *data) 118{ 119 struct semaphore *sem; 120 121 sem = (struct semaphore *) data; 122 up(sem); 123} 124 125static struct sclp_req * 126cpi_prepare_req(void) 127{ 128 struct sclp_req *req; 129 struct cpi_sccb *sccb; 130 struct cpi_evbuf *evb; 131 132 req = kmalloc(sizeof(struct sclp_req), GFP_KERNEL); 133 if (req == NULL) 134 return ERR_PTR(-ENOMEM); 135 sccb = (struct cpi_sccb *) __get_free_page(GFP_KERNEL | GFP_DMA); 136 if (sccb == NULL) { 137 kfree(req); 138 return ERR_PTR(-ENOMEM); 139 } 140 memset(sccb, 0, sizeof(struct cpi_sccb)); 141 142 /* setup SCCB for Control-Program Identification */ 143 sccb->header.length = sizeof(struct cpi_sccb); 144 sccb->cpi_evbuf.header.length = sizeof(struct cpi_evbuf); 145 sccb->cpi_evbuf.header.type = 0x0B; 146 evb = &sccb->cpi_evbuf; 147 148 /* set system type */ 149 memset(evb->system_type, ' ', CPI_LENGTH_SYSTEM_TYPE); 150 memcpy(evb->system_type, system_type, strlen(system_type)); 151 sclp_ascebc_str(evb->system_type, CPI_LENGTH_SYSTEM_TYPE); 152 EBC_TOUPPER(evb->system_type, CPI_LENGTH_SYSTEM_TYPE); 153 154 /* set system name */ 155 memset(evb->system_name, ' ', CPI_LENGTH_SYSTEM_NAME); 156 memcpy(evb->system_name, system_name, strlen(system_name)); 157 sclp_ascebc_str(evb->system_name, CPI_LENGTH_SYSTEM_NAME); 158 EBC_TOUPPER(evb->system_name, CPI_LENGTH_SYSTEM_NAME); 159 160 /* set sytem level */ 161 evb->system_level = LINUX_VERSION_CODE; 162 163 /* set sysplex name */ 164 if (sysplex_name) { 165 memset(evb->sysplex_name, ' ', CPI_LENGTH_SYSPLEX_NAME); 166 memcpy(evb->sysplex_name, sysplex_name, strlen(sysplex_name)); 167 sclp_ascebc_str(evb->sysplex_name, CPI_LENGTH_SYSPLEX_NAME); 168 EBC_TOUPPER(evb->sysplex_name, CPI_LENGTH_SYSPLEX_NAME); 169 } 170 171 /* prepare request data structure presented to SCLP driver */ 172 req->command = SCLP_CMDW_WRITE_EVENT_DATA; 173 req->sccb = sccb; 174 req->status = SCLP_REQ_FILLED; 175 req->callback = cpi_callback; 176 return req; 177} 178 179static void 180cpi_free_req(struct sclp_req *req) 181{ 182 free_page((unsigned long) req->sccb); 183 kfree(req); 184} 185 186static int __init 187cpi_module_init(void) 188{ 189 struct semaphore sem; 190 struct sclp_req *req; 191 int rc; 192 193 rc = cpi_check_parms(); 194 if (rc) 195 return rc; 196 197 rc = sclp_register(&sclp_cpi_event); 198 if (rc) { 199 /* could not register sclp event. Die. */ 200 printk(KERN_WARNING "cpi: could not register to hardware " 201 "console.\n"); 202 return -EINVAL; 203 } 204 if (!(sclp_cpi_event.sclp_send_mask & EVTYP_CTLPROGIDENT_MASK)) { 205 printk(KERN_WARNING "cpi: no control program identification " 206 "support\n"); 207 sclp_unregister(&sclp_cpi_event); 208 return -EOPNOTSUPP; 209 } 210 211 req = cpi_prepare_req(); 212 if (IS_ERR(req)) { 213 printk(KERN_WARNING "cpi: couldn't allocate request\n"); 214 sclp_unregister(&sclp_cpi_event); 215 return PTR_ERR(req); 216 } 217 218 /* Prepare semaphore */ 219 sema_init(&sem, 0); 220 req->callback_data = &sem; 221 /* Add request to sclp queue */ 222 rc = sclp_add_request(req); 223 if (rc) { 224 printk(KERN_WARNING "cpi: could not start request\n"); 225 cpi_free_req(req); 226 sclp_unregister(&sclp_cpi_event); 227 return rc; 228 } 229 /* make "insmod" sleep until callback arrives */ 230 down(&sem); 231 232 rc = ((struct cpi_sccb *) req->sccb)->header.response_code; 233 if (rc != 0x0020) { 234 printk(KERN_WARNING "cpi: failed with response code 0x%x\n", 235 rc); 236 rc = -ECOMM; 237 } else 238 rc = 0; 239 240 cpi_free_req(req); 241 sclp_unregister(&sclp_cpi_event); 242 243 return rc; 244} 245 246 247static void __exit cpi_module_exit(void) 248{ 249} 250 251 252/* declare driver module init/cleanup functions */ 253module_init(cpi_module_init); 254module_exit(cpi_module_exit); 255