/* * PowerPC64 LPAR Configuration Information Driver * * Dave Engebretsen engebret@us.ibm.com * Copyright (c) 2003 Dave Engebretsen * Will Schmidt willschm@us.ibm.com * SPLPAR updates, Copyright (c) 2003 Will Schmidt IBM Corporation. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * This driver creates a proc file at /proc/ppc64/lparcfg which contains * keyword - value pairs that specify the configuration of the partition. */ #include #include #include #include #include #include #include #include #include #include #define MODULE_VERSION "1.0" #define MODULE_NAME "lparcfg" static struct proc_dir_entry *proc_ppc64_lparcfg; #define LPARCFG_BUFF_SIZE 4096 #ifdef CONFIG_PPC_ISERIES static unsigned char e2a(unsigned char x) { switch (x) { case 0xF0: return '0'; case 0xF1: return '1'; case 0xF2: return '2'; case 0xF3: return '3'; case 0xF4: return '4'; case 0xF5: return '5'; case 0xF6: return '6'; case 0xF7: return '7'; case 0xF8: return '8'; case 0xF9: return '9'; case 0xC1: return 'A'; case 0xC2: return 'B'; case 0xC3: return 'C'; case 0xC4: return 'D'; case 0xC5: return 'E'; case 0xC6: return 'F'; case 0xC7: return 'G'; case 0xC8: return 'H'; case 0xC9: return 'I'; case 0xD1: return 'J'; case 0xD2: return 'K'; case 0xD3: return 'L'; case 0xD4: return 'M'; case 0xD5: return 'N'; case 0xD6: return 'O'; case 0xD7: return 'P'; case 0xD8: return 'Q'; case 0xD9: return 'R'; case 0xE2: return 'S'; case 0xE3: return 'T'; case 0xE4: return 'U'; case 0xE5: return 'V'; case 0xE6: return 'W'; case 0xE7: return 'X'; case 0xE8: return 'Y'; case 0xE9: return 'Z'; } return ' '; } /* * Methods used to fetch LPAR data when running on an iSeries platform. */ static int lparcfg_data(unsigned char *buf, unsigned long size) { unsigned long n = 0, pool_id, lp_index; int shared, entitled_capacity, max_entitled_capacity; int processors, max_processors; struct paca_struct *lpaca = get_paca(); if((buf == NULL) || (size > LPARCFG_BUFF_SIZE)) { return -EFAULT; } memset(buf, 0, size); shared = (int)(lpaca->xLpPacaPtr->xSharedProc); n += snprintf(buf, LPARCFG_BUFF_SIZE - n, "serial_number=%c%c%c%c%c%c%c\n", e2a(xItExtVpdPanel.mfgID[2]), e2a(xItExtVpdPanel.mfgID[3]), e2a(xItExtVpdPanel.systemSerial[1]), e2a(xItExtVpdPanel.systemSerial[2]), e2a(xItExtVpdPanel.systemSerial[3]), e2a(xItExtVpdPanel.systemSerial[4]), e2a(xItExtVpdPanel.systemSerial[5])); n += snprintf(buf+n, LPARCFG_BUFF_SIZE - n, "system_type=%c%c%c%c\n", e2a(xItExtVpdPanel.machineType[0]), e2a(xItExtVpdPanel.machineType[1]), e2a(xItExtVpdPanel.machineType[2]), e2a(xItExtVpdPanel.machineType[3])); lp_index = HvLpConfig_getLpIndex(); n += snprintf(buf+n, LPARCFG_BUFF_SIZE - n, "partition_id=%d\n", (int)lp_index); n += snprintf(buf+n, LPARCFG_BUFF_SIZE - n, "system_active_processors=%d\n", (int)HvLpConfig_getSystemPhysicalProcessors()); n += snprintf(buf+n, LPARCFG_BUFF_SIZE - n, "system_potential_processors=%d\n", (int)HvLpConfig_getSystemPhysicalProcessors()); processors = (int)HvLpConfig_getPhysicalProcessors(); n += snprintf(buf+n, LPARCFG_BUFF_SIZE - n, "partition_active_processors=%d\n", processors); max_processors = (int)HvLpConfig_getMaxPhysicalProcessors(); n += snprintf(buf+n, LPARCFG_BUFF_SIZE - n, "partition_potential_processors=%d\n", max_processors); if(shared) { entitled_capacity = HvLpConfig_getSharedProcUnits(); max_entitled_capacity = HvLpConfig_getMaxSharedProcUnits(); } else { entitled_capacity = processors * 100; max_entitled_capacity = max_processors * 100; } n += snprintf(buf+n, LPARCFG_BUFF_SIZE - n, "partition_entitled_capacity=%d\n", entitled_capacity); n += snprintf(buf+n, LPARCFG_BUFF_SIZE - n, "partition_max_entitled_capacity=%d\n", max_entitled_capacity); if(shared) { pool_id = HvLpConfig_getSharedPoolIndex(); n += snprintf(buf+n, LPARCFG_BUFF_SIZE - n, "pool=%d\n", (int)pool_id); n += snprintf(buf+n, LPARCFG_BUFF_SIZE - n, "pool_capacity=%d\n", (int)(HvLpConfig_getNumProcsInSharedPool(pool_id)*100)); } n += snprintf(buf+n, LPARCFG_BUFF_SIZE - n, "shared_processor_mode=%d\n", shared); return 0; } #endif /* CONFIG_PPC_ISERIES */ #ifdef CONFIG_PPC_PSERIES /* * Methods used to fetch LPAR data when running on a pSeries platform. */ /* * H_GET_PPP hcall returns info in 4 parms. * entitled_capacity,unallocated_capacity, * aggregation, resource_capability). * * R4 = Entitled Processor Capacity Percentage. * R5 = Unallocated Processor Capacity Percentage. * R6 (AABBCCDDEEFFGGHH). * XXXX - reserved (0) * XXXX - reserved (0) * XXXX - Group Number * XXXX - Pool Number. * R7 (PPOONNMMLLKKJJII) * XX - reserved. (0) * XX - bit 0-6 reserved (0). bit 7 is Capped indicator. * XX - variable processor Capacity Weight * XX - Unallocated Variable Processor Capacity Weight. * XXXX - Active processors in Physical Processor Pool. * XXXX - Processors active on platform. */ unsigned int h_get_ppp(unsigned long *entitled,unsigned long *unallocated,unsigned long *aggregation,unsigned long *resource) { unsigned long rc; rc = plpar_hcall_4out(H_GET_PPP,0,0,0,0,entitled,unallocated,aggregation,resource); return 0; } /* * get_splpar_potential_characteristics(). * Retrieve the potential_processors and max_entitled_capacity values * through the get-system-parameter rtas call. */ #define SPLPAR_CHARACTERISTICS_TOKEN 20 #define SPLPAR_MAXLENGTH 1026*(sizeof(char)) unsigned int get_splpar_potential_characteristics() { /* return 0 for now. Underlying rtas functionality is not yet complete. 12/01/2003*/ return 0; #if 0 long call_status; unsigned long ret[2]; char * buffer = kmalloc(SPLPAR_MAXLENGTH, GFP_KERNEL); printk("token for ibm,get-system-parameter (0x%x)\n",rtas_token("ibm,get-system-parameter")); call_status = rtas_call(rtas_token("ibm,get-system-parameter"), 3, 1, NULL, SPLPAR_CHARACTERISTICS_TOKEN, &buffer, SPLPAR_MAXLENGTH, (void *)&ret); if (call_status!=0) { printk("Error calling get-system-parameter (0x%lx)\n",call_status); kfree(buffer); return -1; } else { printk("get-system-parameter (%s)\n",buffer); kfree(buffer); /* TODO: Add code here to parse out value for system_potential_processors and partition_max_entitled_capacity */ return 1; } #endif } static int lparcfg_data(unsigned char *buf, unsigned long size) { unsigned long n = 0; int shared, max_entitled_capacity; int processors, system_active_processors, system_potential_processors; struct device_node *root; const char *model = ""; const char *system_id = ""; unsigned int *lp_index_ptr, lp_index = 0; struct device_node *rtas_node; int *ip; unsigned long h_entitled,h_unallocated,h_aggregation,h_resource; if((buf == NULL) || (size > LPARCFG_BUFF_SIZE)) { return -EFAULT; } memset(buf, 0, size); root = find_path_device("/"); if (root) { model = get_property(root, "model", NULL); system_id = get_property(root, "system-id", NULL); lp_index_ptr = (unsigned int *)get_property(root, "ibm,partition-no", NULL); if(lp_index_ptr) lp_index = *lp_index_ptr; } n = snprintf(buf, LPARCFG_BUFF_SIZE - n, "serial_number=%s\n", system_id); n += snprintf(buf+n, LPARCFG_BUFF_SIZE - n, "system_type=%s\n", model); n += snprintf(buf+n, LPARCFG_BUFF_SIZE - n, "partition_id=%d\n", (int)lp_index); rtas_node = find_path_device("/rtas"); ip = (int *)get_property(rtas_node, "ibm,lrdr-capacity", NULL); if (ip == NULL) { system_active_processors = systemcfg->processorCount; } else { system_active_processors = *(ip + 4); } if (cur_cpu_spec->firmware_features & FW_FEATURE_SPLPAR) { h_get_ppp(&h_entitled,&h_unallocated,&h_aggregation,&h_resource); #ifdef DEBUG n += snprintf(buf+n, LPARCFG_BUFF_SIZE - n, "R4=0x%lx\n", h_entitled); n += snprintf(buf+n, LPARCFG_BUFF_SIZE - n, "R5=0x%lx\n", h_unallocated); n += snprintf(buf+n, LPARCFG_BUFF_SIZE - n, "R6=0x%lx\n", h_aggregation); n += snprintf(buf+n, LPARCFG_BUFF_SIZE - n, "R7=0x%lx\n", h_resource); #endif /* DEBUG */ } if (cur_cpu_spec->firmware_features & FW_FEATURE_SPLPAR) { system_potential_processors = get_splpar_potential_characteristics(); n += snprintf(buf+n, LPARCFG_BUFF_SIZE - n, "system_active_processors=%d\n", (h_resource >> 2*8) & 0xffff); n += snprintf(buf+n, LPARCFG_BUFF_SIZE - n, "system_potential_processors=%d\n", system_potential_processors); } else { system_potential_processors = system_active_processors; n += snprintf(buf+n, LPARCFG_BUFF_SIZE - n, "system_active_processors=%d\n", system_active_processors); n += snprintf(buf+n, LPARCFG_BUFF_SIZE - n, "system_potential_processors=%d\n", system_potential_processors); } processors = systemcfg->processorCount; n += snprintf(buf+n, LPARCFG_BUFF_SIZE - n, "partition_active_processors=%d\n", processors); n += snprintf(buf+n, LPARCFG_BUFF_SIZE - n, "partition_potential_processors=%d\n", system_active_processors); max_entitled_capacity = system_active_processors * 100; if (cur_cpu_spec->firmware_features & FW_FEATURE_SPLPAR) { n += snprintf(buf+n, LPARCFG_BUFF_SIZE - n, "partition_entitled_capacity=%ld\n", h_entitled); } else { n += snprintf(buf+n, LPARCFG_BUFF_SIZE - n, "partition_entitled_capacity=%d\n", system_active_processors*100); } n += snprintf(buf+n, LPARCFG_BUFF_SIZE - n, "partition_max_entitled_capacity=%d\n", max_entitled_capacity); shared = 0; n += snprintf(buf+n, LPARCFG_BUFF_SIZE - n, "shared_processor_mode=%d\n", shared); if (cur_cpu_spec->firmware_features & FW_FEATURE_SPLPAR) { n += snprintf(buf+n, LPARCFG_BUFF_SIZE - n, "pool=%d\n", (h_aggregation >> 0*8)&0xffff); n += snprintf(buf+n, LPARCFG_BUFF_SIZE - n, "pool_capacity=%d\n", (h_resource >> 3*8) &0xffff); n += snprintf(buf+n, LPARCFG_BUFF_SIZE - n, "group=%d\n", (h_aggregation >> 2*8)&0xffff); n += snprintf(buf+n, LPARCFG_BUFF_SIZE - n, "capped=%d\n", (h_resource >> 6*8)&0x40); n += snprintf(buf+n, LPARCFG_BUFF_SIZE - n, "capacity_weight=%d\n", (int)(h_resource>>5*8)&0xFF); } return 0; } #endif /* CONFIG_PPC_PSERIES */ static ssize_t lparcfg_read(struct file *file, char *buf, size_t count, loff_t *ppos) { struct proc_dir_entry *dp = file->f_dentry->d_inode->u.generic_ip; unsigned long *data = (unsigned long *)dp->data; unsigned long p; ssize_t read; char * pnt; if (!data) { printk(KERN_ERR "lparcfg: read failed no data\n"); return -EIO; } if(ppos) { p = *ppos; } else { return -EFAULT; } if (p >= LPARCFG_BUFF_SIZE) return 0; lparcfg_data((unsigned char *)data, LPARCFG_BUFF_SIZE); if (count > (strlen((char *)data) - p)) count = (strlen((char *)data)) - p; read = 0; pnt = (char *)(data) + p; copy_to_user(buf, (void *)pnt, count); read += count; *ppos = p + read; return read; } static int lparcfg_open(struct inode * inode, struct file * file) { struct proc_dir_entry *dp = file->f_dentry->d_inode->u.generic_ip; unsigned int *data = (unsigned int *)dp->data; if (!data) { printk(KERN_ERR "lparcfg: open failed no data\n"); return -EIO; } return 0; } struct file_operations lparcfg_fops = { owner: THIS_MODULE, read: lparcfg_read, open: lparcfg_open, }; int __init lparcfg_init(void) { struct proc_dir_entry *ent; ent = create_proc_entry("ppc64/lparcfg", S_IRUSR, NULL); if (ent) { ent->proc_fops = &lparcfg_fops; ent->data = kmalloc(LPARCFG_BUFF_SIZE, GFP_KERNEL); if (!ent->data) { printk(KERN_ERR "Failed to allocate buffer for lparcfg\n"); remove_proc_entry("lparcfg", ent->parent); return -ENOMEM; } } else { printk(KERN_ERR "Failed to create ppc64/lparcfg\n"); return -EIO; } proc_ppc64_lparcfg = ent; return 0; } void __exit lparcfg_cleanup(void) { if (proc_ppc64_lparcfg) { if (proc_ppc64_lparcfg->data) { kfree(proc_ppc64_lparcfg->data); } remove_proc_entry("lparcfg", proc_ppc64_lparcfg->parent); } } module_init(lparcfg_init); module_exit(lparcfg_cleanup); MODULE_DESCRIPTION("Interface for LPAR configuration data"); MODULE_AUTHOR("Dave Engebretsen"); MODULE_LICENSE("GPL");