/* * Author: Martin Peschke * Copyright (C) 2001 IBM Entwicklung GmbH, IBM Corporation * * SCLP Control-Program Identification. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "sclp.h" #include "sclp_rw.h" #define CPI_LENGTH_SYSTEM_TYPE 8 #define CPI_LENGTH_SYSTEM_NAME 8 #define CPI_LENGTH_SYSPLEX_NAME 8 struct cpi_evbuf { struct evbuf_header header; u8 id_format; u8 reserved0; u8 system_type[CPI_LENGTH_SYSTEM_TYPE]; u64 reserved1; u8 system_name[CPI_LENGTH_SYSTEM_NAME]; u64 reserved2; u64 system_level; u64 reserved3; u8 sysplex_name[CPI_LENGTH_SYSPLEX_NAME]; u8 reserved4[16]; } __attribute__((packed)); struct cpi_sccb { struct sccb_header header; struct cpi_evbuf cpi_evbuf; } __attribute__((packed)); /* Event type structure for write message and write priority message */ static struct sclp_register sclp_cpi_event = { .send_mask = EvTyp_CtlProgIdent_Mask }; MODULE_AUTHOR( "Martin Peschke, IBM Deutschland Entwicklung GmbH " ""); MODULE_DESCRIPTION( "identify this operating system instance to the S/390 " "or zSeries hardware"); static char *system_name = NULL; module_param(system_name, charp, 0); MODULE_PARM_DESC(system_name, "e.g. hostname - max. 8 characters"); static char *sysplex_name = NULL; #ifdef ALLOW_SYSPLEX_NAME module_param(sysplex_name, charp, 0); MODULE_PARM_DESC(sysplex_name, "if applicable - max. 8 characters"); #endif /* use default value for this field (as well as for system level) */ static char *system_type = "LINUX"; static int cpi_check_parms(void) { /* reject if no system type specified */ if (!system_type) { printk("cpi: bug: no system type specified\n"); return -EINVAL; } /* reject if system type larger than 8 characters */ if (strlen(system_type) > CPI_LENGTH_SYSTEM_NAME) { printk("cpi: bug: system type has length of %li characters - " "only %i characters supported\n", strlen(system_type), CPI_LENGTH_SYSTEM_TYPE); return -EINVAL; } /* reject if no system name specified */ if (!system_name) { printk("cpi: no system name specified\n"); return -EINVAL; } /* reject if system name larger than 8 characters */ if (strlen(system_name) > CPI_LENGTH_SYSTEM_NAME) { printk("cpi: system name has length of %li characters - " "only %i characters supported\n", strlen(system_name), CPI_LENGTH_SYSTEM_NAME); return -EINVAL; } /* reject if specified sysplex name larger than 8 characters */ if (sysplex_name && strlen(sysplex_name) > CPI_LENGTH_SYSPLEX_NAME) { printk("cpi: sysplex name has length of %li characters" " - only %i characters supported\n", strlen(sysplex_name), CPI_LENGTH_SYSPLEX_NAME); return -EINVAL; } return 0; } static void cpi_callback(struct sclp_req *req, void *data) { struct semaphore *sem; sem = (struct semaphore *) data; up(sem); } static struct sclp_req * cpi_prepare_req(void) { struct sclp_req *req; struct cpi_sccb *sccb; struct cpi_evbuf *evb; req = (struct sclp_req *) kmalloc(sizeof(struct sclp_req), GFP_KERNEL); if (req == NULL) return ERR_PTR(-ENOMEM); sccb = (struct cpi_sccb *) __get_free_page(GFP_KERNEL | GFP_DMA); if (sccb == NULL) { kfree(req); return ERR_PTR(-ENOMEM); } memset(sccb, 0, sizeof(struct cpi_sccb)); /* setup SCCB for Control-Program Identification */ sccb->header.length = sizeof(struct cpi_sccb); sccb->cpi_evbuf.header.length = sizeof(struct cpi_evbuf); sccb->cpi_evbuf.header.type = 0x0B; evb = &sccb->cpi_evbuf; /* set system type */ memset(evb->system_type, ' ', CPI_LENGTH_SYSTEM_TYPE); memcpy(evb->system_type, system_type, strlen(system_type)); sclp_ascebc_str(evb->system_type, CPI_LENGTH_SYSTEM_TYPE); EBC_TOUPPER(evb->system_type, CPI_LENGTH_SYSTEM_TYPE); /* set system name */ memset(evb->system_name, ' ', CPI_LENGTH_SYSTEM_NAME); memcpy(evb->system_name, system_name, strlen(system_name)); sclp_ascebc_str(evb->system_name, CPI_LENGTH_SYSTEM_NAME); EBC_TOUPPER(evb->system_name, CPI_LENGTH_SYSTEM_NAME); /* set sytem level */ evb->system_level = LINUX_VERSION_CODE; /* set sysplex name */ if (sysplex_name) { memset(evb->sysplex_name, ' ', CPI_LENGTH_SYSPLEX_NAME); memcpy(evb->sysplex_name, sysplex_name, strlen(sysplex_name)); sclp_ascebc_str(evb->sysplex_name, CPI_LENGTH_SYSPLEX_NAME); EBC_TOUPPER(evb->sysplex_name, CPI_LENGTH_SYSPLEX_NAME); } /* prepare request data structure presented to SCLP driver */ req->command = SCLP_CMDW_WRITEDATA; req->sccb = sccb; req->status = SCLP_REQ_FILLED; req->callback = cpi_callback; return req; } static void cpi_free_req(struct sclp_req *req) { free_page((unsigned long) req->sccb); kfree(req); } static int __init cpi_module_init(void) { struct semaphore sem; struct sclp_req *req; int rc; rc = cpi_check_parms(); if (rc) return rc; rc = sclp_register(&sclp_cpi_event); if (rc) { /* could not register sclp event. Die. */ printk(KERN_WARNING "cpi: could not register to hardware " "console.\n"); return -EINVAL; } if (!(sclp_cpi_event.sclp_send_mask & EvTyp_CtlProgIdent_Mask)) { printk(KERN_WARNING "cpi: no control program identification " "support\n"); sclp_unregister(&sclp_cpi_event); return -EOPNOTSUPP; } req = cpi_prepare_req(); if (IS_ERR(req)) { printk(KERN_WARNING "cpi: couldn't allocate request\n"); sclp_unregister(&sclp_cpi_event); return PTR_ERR(req); } /* Prepare semaphore */ sema_init(&sem, 0); req->callback_data = &sem; /* Add request to sclp queue */ rc = sclp_add_request(req); if (rc) { printk(KERN_WARNING "cpi: could not start request\n"); cpi_free_req(req); sclp_unregister(&sclp_cpi_event); return rc; } /* make "insmod" sleep until callback arrives */ down(&sem); rc = ((struct cpi_sccb *) req->sccb)->header.response_code; if (rc != 0x0020) { printk(KERN_WARNING "cpi: failed with response code 0x%x\n", rc); rc = -ECOMM; } else rc = 0; cpi_free_req(req); sclp_unregister(&sclp_cpi_event); return rc; } static void __exit cpi_module_exit(void) { } /* declare driver module init/cleanup functions */ module_init(cpi_module_init); module_exit(cpi_module_exit);