[ppc64] add/forward port of lparcfg, from Will Schmidt --- arch/ppc64/Kconfig | 7 arch/ppc64/kernel/Makefile | 2 arch/ppc64/kernel/lparcfg.c | 478 +++++++++++++++++++++++++++++++++++++ arch/ppc64/kernel/pSeries_hvCall.S | 38 ++ include/asm-ppc64/hvcall.h | 17 + 5 files changed, 542 insertions(+) diff -puN arch/ppc64/Kconfig~ppc64-lparcfg arch/ppc64/Kconfig --- 25/arch/ppc64/Kconfig~ppc64-lparcfg 2004-01-13 23:23:00.000000000 -0800 +++ 25-akpm/arch/ppc64/Kconfig 2004-01-13 23:23:00.000000000 -0800 @@ -142,6 +142,13 @@ config RTAS_FLASH config SCANLOG tristate "Scanlog dump interface" depends on PPC_RTAS + +config LPARCFG + bool "LPAR Configuration Data" + help + Provide system capacity information via human readable + = pairs through a /proc/ppc64/lparcfg interface. + endmenu diff -puN arch/ppc64/kernel/Makefile~ppc64-lparcfg arch/ppc64/kernel/Makefile --- 25/arch/ppc64/kernel/Makefile~ppc64-lparcfg 2004-01-13 23:23:00.000000000 -0800 +++ 25-akpm/arch/ppc64/kernel/Makefile 2004-01-13 23:23:00.000000000 -0800 @@ -38,5 +38,7 @@ obj-$(CONFIG_MODULES) += module.o ppc_k obj-$(CONFIG_PPC_RTAS) += rtas-proc.o obj-$(CONFIG_SCANLOG) += scanlog.o obj-$(CONFIG_VIOPATH) += viopath.o +obj-$(CONFIG_LPARCFG) += lparcfg.o + CFLAGS_ioctl32.o += -Ifs/ diff -puN /dev/null arch/ppc64/kernel/lparcfg.c --- /dev/null 2002-08-30 16:31:37.000000000 -0700 +++ 25-akpm/arch/ppc64/kernel/lparcfg.c 2004-01-13 23:23:00.000000000 -0800 @@ -0,0 +1,478 @@ +/* + * 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(void) +{ + /* 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 will come out of get_splpar_potential_characteristics() when that function is complete */ + 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 = PDE(file->f_dentry->d_inode); + 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 += read; + return read; +} + +static int lparcfg_open(struct inode * inode, struct file * file) +{ + struct proc_dir_entry *dp = PDE(file->f_dentry->d_inode); + 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"); diff -puN arch/ppc64/kernel/pSeries_hvCall.S~ppc64-lparcfg arch/ppc64/kernel/pSeries_hvCall.S --- 25/arch/ppc64/kernel/pSeries_hvCall.S~ppc64-lparcfg 2004-01-13 23:23:00.000000000 -0800 +++ 25-akpm/arch/ppc64/kernel/pSeries_hvCall.S 2004-01-13 23:23:00.000000000 -0800 @@ -103,3 +103,41 @@ _GLOBAL(plpar_hcall_8arg_2ret) ld r0,-8(r1) mtcrf 0xff,r0 blr /* return r3 = status */ + + +/* long plpar_hcall_4out(unsigned long opcode, R3 + unsigned long arg1, R4 + unsigned long arg2, R5 + unsigned long arg3, R6 + unsigned long arg4, R7 + unsigned long *out1, (r4) R8 + unsigned long *out2, (r5) R9 + unsigned long *out3, (r6) R10 + unsigned long *out4); (r7) 112(R1). From Parameter save area. + */ +_GLOBAL(plpar_hcall_4out) + mfcr r0 + std r0,-8(r1) + ld r14,112(r1) + stdu r1,-48(r1) + + std r8,32(r1) /* Save out ptrs. */ + std r9,24(r1) + std r10,16(r1) + std r14,8(r1) + + HSC /* invoke the hypervisor */ + + ld r14,32(r1) /* Fetch r4-r7 ret args. */ + std r4,0(r14) + ld r14,24(r1) + std r5,0(r14) + ld r14,16(r1) + std r6,0(r14) + ld r14,8(r1) + std r7,0(r14) + + ld r1,0(r1) + ld r0,-8(r1) + mtcrf 0xff,r0 + blr /* return r3 = status */ diff -puN include/asm-ppc64/hvcall.h~ppc64-lparcfg include/asm-ppc64/hvcall.h --- 25/include/asm-ppc64/hvcall.h~ppc64-lparcfg 2004-01-13 23:23:00.000000000 -0800 +++ 25-akpm/include/asm-ppc64/hvcall.h 2004-01-13 23:23:00.000000000 -0800 @@ -121,3 +121,20 @@ long plpar_hcall_8arg_2ret(unsigned long unsigned long *out1); + + +/* plpar_hcall_4out() + * + * same as plpar_hcall except with 4 output arguments. + * + */ +long plpar_hcall_4out(unsigned long opcode, + unsigned long arg1, + unsigned long arg2, + unsigned long arg3, + unsigned long arg4, + unsigned long *out1, + unsigned long *out2, + unsigned long *out3, + unsigned long *out4); + _