18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Hypervisor filesystem for Linux on s390. Diag 204 and 224 48c2ecf20Sopenharmony_ci * implementation. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Copyright IBM Corp. 2006, 2008 78c2ecf20Sopenharmony_ci * Author(s): Michael Holzheu <holzheu@de.ibm.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#define KMSG_COMPONENT "hypfs" 118c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci#include <linux/types.h> 148c2ecf20Sopenharmony_ci#include <linux/errno.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include <linux/string.h> 178c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 188c2ecf20Sopenharmony_ci#include <linux/mm.h> 198c2ecf20Sopenharmony_ci#include <asm/diag.h> 208c2ecf20Sopenharmony_ci#include <asm/ebcdic.h> 218c2ecf20Sopenharmony_ci#include "hypfs.h" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci#define TMP_SIZE 64 /* size of temporary buffers */ 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#define DBFS_D204_HDR_VERSION 0 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic char *diag224_cpu_names; /* diag 224 name table */ 288c2ecf20Sopenharmony_cistatic enum diag204_sc diag204_store_sc; /* used subcode for store */ 298c2ecf20Sopenharmony_cistatic enum diag204_format diag204_info_type; /* used diag 204 data format */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic void *diag204_buf; /* 4K aligned buffer for diag204 data */ 328c2ecf20Sopenharmony_cistatic void *diag204_buf_vmalloc; /* vmalloc pointer for diag204 data */ 338c2ecf20Sopenharmony_cistatic int diag204_buf_pages; /* number of pages for diag204 data */ 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic struct dentry *dbfs_d204_file; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* 388c2ecf20Sopenharmony_ci * DIAG 204 member access functions. 398c2ecf20Sopenharmony_ci * 408c2ecf20Sopenharmony_ci * Since we have two different diag 204 data formats for old and new s390 418c2ecf20Sopenharmony_ci * machines, we do not access the structs directly, but use getter functions for 428c2ecf20Sopenharmony_ci * each struct member instead. This should make the code more readable. 438c2ecf20Sopenharmony_ci */ 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci/* Time information block */ 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_cistatic inline int info_blk_hdr__size(enum diag204_format type) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci if (type == DIAG204_INFO_SIMPLE) 508c2ecf20Sopenharmony_ci return sizeof(struct diag204_info_blk_hdr); 518c2ecf20Sopenharmony_ci else /* DIAG204_INFO_EXT */ 528c2ecf20Sopenharmony_ci return sizeof(struct diag204_x_info_blk_hdr); 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistatic inline __u8 info_blk_hdr__npar(enum diag204_format type, void *hdr) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci if (type == DIAG204_INFO_SIMPLE) 588c2ecf20Sopenharmony_ci return ((struct diag204_info_blk_hdr *)hdr)->npar; 598c2ecf20Sopenharmony_ci else /* DIAG204_INFO_EXT */ 608c2ecf20Sopenharmony_ci return ((struct diag204_x_info_blk_hdr *)hdr)->npar; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_cistatic inline __u8 info_blk_hdr__flags(enum diag204_format type, void *hdr) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci if (type == DIAG204_INFO_SIMPLE) 668c2ecf20Sopenharmony_ci return ((struct diag204_info_blk_hdr *)hdr)->flags; 678c2ecf20Sopenharmony_ci else /* DIAG204_INFO_EXT */ 688c2ecf20Sopenharmony_ci return ((struct diag204_x_info_blk_hdr *)hdr)->flags; 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic inline __u16 info_blk_hdr__pcpus(enum diag204_format type, void *hdr) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci if (type == DIAG204_INFO_SIMPLE) 748c2ecf20Sopenharmony_ci return ((struct diag204_info_blk_hdr *)hdr)->phys_cpus; 758c2ecf20Sopenharmony_ci else /* DIAG204_INFO_EXT */ 768c2ecf20Sopenharmony_ci return ((struct diag204_x_info_blk_hdr *)hdr)->phys_cpus; 778c2ecf20Sopenharmony_ci} 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci/* Partition header */ 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic inline int part_hdr__size(enum diag204_format type) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci if (type == DIAG204_INFO_SIMPLE) 848c2ecf20Sopenharmony_ci return sizeof(struct diag204_part_hdr); 858c2ecf20Sopenharmony_ci else /* DIAG204_INFO_EXT */ 868c2ecf20Sopenharmony_ci return sizeof(struct diag204_x_part_hdr); 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic inline __u8 part_hdr__rcpus(enum diag204_format type, void *hdr) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci if (type == DIAG204_INFO_SIMPLE) 928c2ecf20Sopenharmony_ci return ((struct diag204_part_hdr *)hdr)->cpus; 938c2ecf20Sopenharmony_ci else /* DIAG204_INFO_EXT */ 948c2ecf20Sopenharmony_ci return ((struct diag204_x_part_hdr *)hdr)->rcpus; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic inline void part_hdr__part_name(enum diag204_format type, void *hdr, 988c2ecf20Sopenharmony_ci char *name) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci if (type == DIAG204_INFO_SIMPLE) 1018c2ecf20Sopenharmony_ci memcpy(name, ((struct diag204_part_hdr *)hdr)->part_name, 1028c2ecf20Sopenharmony_ci DIAG204_LPAR_NAME_LEN); 1038c2ecf20Sopenharmony_ci else /* DIAG204_INFO_EXT */ 1048c2ecf20Sopenharmony_ci memcpy(name, ((struct diag204_x_part_hdr *)hdr)->part_name, 1058c2ecf20Sopenharmony_ci DIAG204_LPAR_NAME_LEN); 1068c2ecf20Sopenharmony_ci EBCASC(name, DIAG204_LPAR_NAME_LEN); 1078c2ecf20Sopenharmony_ci name[DIAG204_LPAR_NAME_LEN] = 0; 1088c2ecf20Sopenharmony_ci strim(name); 1098c2ecf20Sopenharmony_ci} 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci/* CPU info block */ 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_cistatic inline int cpu_info__size(enum diag204_format type) 1148c2ecf20Sopenharmony_ci{ 1158c2ecf20Sopenharmony_ci if (type == DIAG204_INFO_SIMPLE) 1168c2ecf20Sopenharmony_ci return sizeof(struct diag204_cpu_info); 1178c2ecf20Sopenharmony_ci else /* DIAG204_INFO_EXT */ 1188c2ecf20Sopenharmony_ci return sizeof(struct diag204_x_cpu_info); 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic inline __u8 cpu_info__ctidx(enum diag204_format type, void *hdr) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci if (type == DIAG204_INFO_SIMPLE) 1248c2ecf20Sopenharmony_ci return ((struct diag204_cpu_info *)hdr)->ctidx; 1258c2ecf20Sopenharmony_ci else /* DIAG204_INFO_EXT */ 1268c2ecf20Sopenharmony_ci return ((struct diag204_x_cpu_info *)hdr)->ctidx; 1278c2ecf20Sopenharmony_ci} 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic inline __u16 cpu_info__cpu_addr(enum diag204_format type, void *hdr) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci if (type == DIAG204_INFO_SIMPLE) 1328c2ecf20Sopenharmony_ci return ((struct diag204_cpu_info *)hdr)->cpu_addr; 1338c2ecf20Sopenharmony_ci else /* DIAG204_INFO_EXT */ 1348c2ecf20Sopenharmony_ci return ((struct diag204_x_cpu_info *)hdr)->cpu_addr; 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic inline __u64 cpu_info__acc_time(enum diag204_format type, void *hdr) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci if (type == DIAG204_INFO_SIMPLE) 1408c2ecf20Sopenharmony_ci return ((struct diag204_cpu_info *)hdr)->acc_time; 1418c2ecf20Sopenharmony_ci else /* DIAG204_INFO_EXT */ 1428c2ecf20Sopenharmony_ci return ((struct diag204_x_cpu_info *)hdr)->acc_time; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic inline __u64 cpu_info__lp_time(enum diag204_format type, void *hdr) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci if (type == DIAG204_INFO_SIMPLE) 1488c2ecf20Sopenharmony_ci return ((struct diag204_cpu_info *)hdr)->lp_time; 1498c2ecf20Sopenharmony_ci else /* DIAG204_INFO_EXT */ 1508c2ecf20Sopenharmony_ci return ((struct diag204_x_cpu_info *)hdr)->lp_time; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic inline __u64 cpu_info__online_time(enum diag204_format type, void *hdr) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci if (type == DIAG204_INFO_SIMPLE) 1568c2ecf20Sopenharmony_ci return 0; /* online_time not available in simple info */ 1578c2ecf20Sopenharmony_ci else /* DIAG204_INFO_EXT */ 1588c2ecf20Sopenharmony_ci return ((struct diag204_x_cpu_info *)hdr)->online_time; 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci/* Physical header */ 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_cistatic inline int phys_hdr__size(enum diag204_format type) 1648c2ecf20Sopenharmony_ci{ 1658c2ecf20Sopenharmony_ci if (type == DIAG204_INFO_SIMPLE) 1668c2ecf20Sopenharmony_ci return sizeof(struct diag204_phys_hdr); 1678c2ecf20Sopenharmony_ci else /* DIAG204_INFO_EXT */ 1688c2ecf20Sopenharmony_ci return sizeof(struct diag204_x_phys_hdr); 1698c2ecf20Sopenharmony_ci} 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_cistatic inline __u8 phys_hdr__cpus(enum diag204_format type, void *hdr) 1728c2ecf20Sopenharmony_ci{ 1738c2ecf20Sopenharmony_ci if (type == DIAG204_INFO_SIMPLE) 1748c2ecf20Sopenharmony_ci return ((struct diag204_phys_hdr *)hdr)->cpus; 1758c2ecf20Sopenharmony_ci else /* DIAG204_INFO_EXT */ 1768c2ecf20Sopenharmony_ci return ((struct diag204_x_phys_hdr *)hdr)->cpus; 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci/* Physical CPU info block */ 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic inline int phys_cpu__size(enum diag204_format type) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci if (type == DIAG204_INFO_SIMPLE) 1848c2ecf20Sopenharmony_ci return sizeof(struct diag204_phys_cpu); 1858c2ecf20Sopenharmony_ci else /* DIAG204_INFO_EXT */ 1868c2ecf20Sopenharmony_ci return sizeof(struct diag204_x_phys_cpu); 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic inline __u16 phys_cpu__cpu_addr(enum diag204_format type, void *hdr) 1908c2ecf20Sopenharmony_ci{ 1918c2ecf20Sopenharmony_ci if (type == DIAG204_INFO_SIMPLE) 1928c2ecf20Sopenharmony_ci return ((struct diag204_phys_cpu *)hdr)->cpu_addr; 1938c2ecf20Sopenharmony_ci else /* DIAG204_INFO_EXT */ 1948c2ecf20Sopenharmony_ci return ((struct diag204_x_phys_cpu *)hdr)->cpu_addr; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic inline __u64 phys_cpu__mgm_time(enum diag204_format type, void *hdr) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci if (type == DIAG204_INFO_SIMPLE) 2008c2ecf20Sopenharmony_ci return ((struct diag204_phys_cpu *)hdr)->mgm_time; 2018c2ecf20Sopenharmony_ci else /* DIAG204_INFO_EXT */ 2028c2ecf20Sopenharmony_ci return ((struct diag204_x_phys_cpu *)hdr)->mgm_time; 2038c2ecf20Sopenharmony_ci} 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_cistatic inline __u64 phys_cpu__ctidx(enum diag204_format type, void *hdr) 2068c2ecf20Sopenharmony_ci{ 2078c2ecf20Sopenharmony_ci if (type == DIAG204_INFO_SIMPLE) 2088c2ecf20Sopenharmony_ci return ((struct diag204_phys_cpu *)hdr)->ctidx; 2098c2ecf20Sopenharmony_ci else /* DIAG204_INFO_EXT */ 2108c2ecf20Sopenharmony_ci return ((struct diag204_x_phys_cpu *)hdr)->ctidx; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci/* Diagnose 204 functions */ 2148c2ecf20Sopenharmony_ci/* 2158c2ecf20Sopenharmony_ci * For the old diag subcode 4 with simple data format we have to use real 2168c2ecf20Sopenharmony_ci * memory. If we use subcode 6 or 7 with extended data format, we can (and 2178c2ecf20Sopenharmony_ci * should) use vmalloc, since we need a lot of memory in that case. Currently 2188c2ecf20Sopenharmony_ci * up to 93 pages! 2198c2ecf20Sopenharmony_ci */ 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic void diag204_free_buffer(void) 2228c2ecf20Sopenharmony_ci{ 2238c2ecf20Sopenharmony_ci if (!diag204_buf) 2248c2ecf20Sopenharmony_ci return; 2258c2ecf20Sopenharmony_ci if (diag204_buf_vmalloc) { 2268c2ecf20Sopenharmony_ci vfree(diag204_buf_vmalloc); 2278c2ecf20Sopenharmony_ci diag204_buf_vmalloc = NULL; 2288c2ecf20Sopenharmony_ci } else { 2298c2ecf20Sopenharmony_ci free_pages((unsigned long) diag204_buf, 0); 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci diag204_buf = NULL; 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic void *page_align_ptr(void *ptr) 2358c2ecf20Sopenharmony_ci{ 2368c2ecf20Sopenharmony_ci return (void *) PAGE_ALIGN((unsigned long) ptr); 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_cistatic void *diag204_alloc_vbuf(int pages) 2408c2ecf20Sopenharmony_ci{ 2418c2ecf20Sopenharmony_ci /* The buffer has to be page aligned! */ 2428c2ecf20Sopenharmony_ci diag204_buf_vmalloc = vmalloc(array_size(PAGE_SIZE, (pages + 1))); 2438c2ecf20Sopenharmony_ci if (!diag204_buf_vmalloc) 2448c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2458c2ecf20Sopenharmony_ci diag204_buf = page_align_ptr(diag204_buf_vmalloc); 2468c2ecf20Sopenharmony_ci diag204_buf_pages = pages; 2478c2ecf20Sopenharmony_ci return diag204_buf; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic void *diag204_alloc_rbuf(void) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci diag204_buf = (void*)__get_free_pages(GFP_KERNEL,0); 2538c2ecf20Sopenharmony_ci if (!diag204_buf) 2548c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 2558c2ecf20Sopenharmony_ci diag204_buf_pages = 1; 2568c2ecf20Sopenharmony_ci return diag204_buf; 2578c2ecf20Sopenharmony_ci} 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_cistatic void *diag204_get_buffer(enum diag204_format fmt, int *pages) 2608c2ecf20Sopenharmony_ci{ 2618c2ecf20Sopenharmony_ci if (diag204_buf) { 2628c2ecf20Sopenharmony_ci *pages = diag204_buf_pages; 2638c2ecf20Sopenharmony_ci return diag204_buf; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci if (fmt == DIAG204_INFO_SIMPLE) { 2668c2ecf20Sopenharmony_ci *pages = 1; 2678c2ecf20Sopenharmony_ci return diag204_alloc_rbuf(); 2688c2ecf20Sopenharmony_ci } else {/* DIAG204_INFO_EXT */ 2698c2ecf20Sopenharmony_ci *pages = diag204((unsigned long)DIAG204_SUBC_RSI | 2708c2ecf20Sopenharmony_ci (unsigned long)DIAG204_INFO_EXT, 0, NULL); 2718c2ecf20Sopenharmony_ci if (*pages <= 0) 2728c2ecf20Sopenharmony_ci return ERR_PTR(-ENOSYS); 2738c2ecf20Sopenharmony_ci else 2748c2ecf20Sopenharmony_ci return diag204_alloc_vbuf(*pages); 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci} 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci/* 2798c2ecf20Sopenharmony_ci * diag204_probe() has to find out, which type of diagnose 204 implementation 2808c2ecf20Sopenharmony_ci * we have on our machine. Currently there are three possible scanarios: 2818c2ecf20Sopenharmony_ci * - subcode 4 + simple data format (only one page) 2828c2ecf20Sopenharmony_ci * - subcode 4-6 + extended data format 2838c2ecf20Sopenharmony_ci * - subcode 4-7 + extended data format 2848c2ecf20Sopenharmony_ci * 2858c2ecf20Sopenharmony_ci * Subcode 5 is used to retrieve the size of the data, provided by subcodes 2868c2ecf20Sopenharmony_ci * 6 and 7. Subcode 7 basically has the same function as subcode 6. In addition 2878c2ecf20Sopenharmony_ci * to subcode 6 it provides also information about secondary cpus. 2888c2ecf20Sopenharmony_ci * In order to get as much information as possible, we first try 2898c2ecf20Sopenharmony_ci * subcode 7, then 6 and if both fail, we use subcode 4. 2908c2ecf20Sopenharmony_ci */ 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic int diag204_probe(void) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci void *buf; 2958c2ecf20Sopenharmony_ci int pages, rc; 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci buf = diag204_get_buffer(DIAG204_INFO_EXT, &pages); 2988c2ecf20Sopenharmony_ci if (!IS_ERR(buf)) { 2998c2ecf20Sopenharmony_ci if (diag204((unsigned long)DIAG204_SUBC_STIB7 | 3008c2ecf20Sopenharmony_ci (unsigned long)DIAG204_INFO_EXT, pages, buf) >= 0) { 3018c2ecf20Sopenharmony_ci diag204_store_sc = DIAG204_SUBC_STIB7; 3028c2ecf20Sopenharmony_ci diag204_info_type = DIAG204_INFO_EXT; 3038c2ecf20Sopenharmony_ci goto out; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci if (diag204((unsigned long)DIAG204_SUBC_STIB6 | 3068c2ecf20Sopenharmony_ci (unsigned long)DIAG204_INFO_EXT, pages, buf) >= 0) { 3078c2ecf20Sopenharmony_ci diag204_store_sc = DIAG204_SUBC_STIB6; 3088c2ecf20Sopenharmony_ci diag204_info_type = DIAG204_INFO_EXT; 3098c2ecf20Sopenharmony_ci goto out; 3108c2ecf20Sopenharmony_ci } 3118c2ecf20Sopenharmony_ci diag204_free_buffer(); 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci /* subcodes 6 and 7 failed, now try subcode 4 */ 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci buf = diag204_get_buffer(DIAG204_INFO_SIMPLE, &pages); 3178c2ecf20Sopenharmony_ci if (IS_ERR(buf)) { 3188c2ecf20Sopenharmony_ci rc = PTR_ERR(buf); 3198c2ecf20Sopenharmony_ci goto fail_alloc; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci if (diag204((unsigned long)DIAG204_SUBC_STIB4 | 3228c2ecf20Sopenharmony_ci (unsigned long)DIAG204_INFO_SIMPLE, pages, buf) >= 0) { 3238c2ecf20Sopenharmony_ci diag204_store_sc = DIAG204_SUBC_STIB4; 3248c2ecf20Sopenharmony_ci diag204_info_type = DIAG204_INFO_SIMPLE; 3258c2ecf20Sopenharmony_ci goto out; 3268c2ecf20Sopenharmony_ci } else { 3278c2ecf20Sopenharmony_ci rc = -ENOSYS; 3288c2ecf20Sopenharmony_ci goto fail_store; 3298c2ecf20Sopenharmony_ci } 3308c2ecf20Sopenharmony_ciout: 3318c2ecf20Sopenharmony_ci rc = 0; 3328c2ecf20Sopenharmony_cifail_store: 3338c2ecf20Sopenharmony_ci diag204_free_buffer(); 3348c2ecf20Sopenharmony_cifail_alloc: 3358c2ecf20Sopenharmony_ci return rc; 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic int diag204_do_store(void *buf, int pages) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci int rc; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci rc = diag204((unsigned long) diag204_store_sc | 3438c2ecf20Sopenharmony_ci (unsigned long) diag204_info_type, pages, buf); 3448c2ecf20Sopenharmony_ci return rc < 0 ? -ENOSYS : 0; 3458c2ecf20Sopenharmony_ci} 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_cistatic void *diag204_store(void) 3488c2ecf20Sopenharmony_ci{ 3498c2ecf20Sopenharmony_ci void *buf; 3508c2ecf20Sopenharmony_ci int pages, rc; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci buf = diag204_get_buffer(diag204_info_type, &pages); 3538c2ecf20Sopenharmony_ci if (IS_ERR(buf)) 3548c2ecf20Sopenharmony_ci goto out; 3558c2ecf20Sopenharmony_ci rc = diag204_do_store(buf, pages); 3568c2ecf20Sopenharmony_ci if (rc) 3578c2ecf20Sopenharmony_ci return ERR_PTR(rc); 3588c2ecf20Sopenharmony_ciout: 3598c2ecf20Sopenharmony_ci return buf; 3608c2ecf20Sopenharmony_ci} 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci/* Diagnose 224 functions */ 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_cistatic int diag224_get_name_table(void) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci /* memory must be below 2GB */ 3678c2ecf20Sopenharmony_ci diag224_cpu_names = (char *) __get_free_page(GFP_KERNEL | GFP_DMA); 3688c2ecf20Sopenharmony_ci if (!diag224_cpu_names) 3698c2ecf20Sopenharmony_ci return -ENOMEM; 3708c2ecf20Sopenharmony_ci if (diag224(diag224_cpu_names)) { 3718c2ecf20Sopenharmony_ci free_page((unsigned long) diag224_cpu_names); 3728c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci EBCASC(diag224_cpu_names + 16, (*diag224_cpu_names + 1) * 16); 3758c2ecf20Sopenharmony_ci return 0; 3768c2ecf20Sopenharmony_ci} 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_cistatic void diag224_delete_name_table(void) 3798c2ecf20Sopenharmony_ci{ 3808c2ecf20Sopenharmony_ci free_page((unsigned long) diag224_cpu_names); 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic int diag224_idx2name(int index, char *name) 3848c2ecf20Sopenharmony_ci{ 3858c2ecf20Sopenharmony_ci memcpy(name, diag224_cpu_names + ((index + 1) * DIAG204_CPU_NAME_LEN), 3868c2ecf20Sopenharmony_ci DIAG204_CPU_NAME_LEN); 3878c2ecf20Sopenharmony_ci name[DIAG204_CPU_NAME_LEN] = 0; 3888c2ecf20Sopenharmony_ci strim(name); 3898c2ecf20Sopenharmony_ci return 0; 3908c2ecf20Sopenharmony_ci} 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_cistruct dbfs_d204_hdr { 3938c2ecf20Sopenharmony_ci u64 len; /* Length of d204 buffer without header */ 3948c2ecf20Sopenharmony_ci u16 version; /* Version of header */ 3958c2ecf20Sopenharmony_ci u8 sc; /* Used subcode */ 3968c2ecf20Sopenharmony_ci char reserved[53]; 3978c2ecf20Sopenharmony_ci} __attribute__ ((packed)); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistruct dbfs_d204 { 4008c2ecf20Sopenharmony_ci struct dbfs_d204_hdr hdr; /* 64 byte header */ 4018c2ecf20Sopenharmony_ci char buf[]; /* d204 buffer */ 4028c2ecf20Sopenharmony_ci} __attribute__ ((packed)); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_cistatic int dbfs_d204_create(void **data, void **data_free_ptr, size_t *size) 4058c2ecf20Sopenharmony_ci{ 4068c2ecf20Sopenharmony_ci struct dbfs_d204 *d204; 4078c2ecf20Sopenharmony_ci int rc, buf_size; 4088c2ecf20Sopenharmony_ci void *base; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci buf_size = PAGE_SIZE * (diag204_buf_pages + 1) + sizeof(d204->hdr); 4118c2ecf20Sopenharmony_ci base = vzalloc(buf_size); 4128c2ecf20Sopenharmony_ci if (!base) 4138c2ecf20Sopenharmony_ci return -ENOMEM; 4148c2ecf20Sopenharmony_ci d204 = page_align_ptr(base + sizeof(d204->hdr)) - sizeof(d204->hdr); 4158c2ecf20Sopenharmony_ci rc = diag204_do_store(d204->buf, diag204_buf_pages); 4168c2ecf20Sopenharmony_ci if (rc) { 4178c2ecf20Sopenharmony_ci vfree(base); 4188c2ecf20Sopenharmony_ci return rc; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci d204->hdr.version = DBFS_D204_HDR_VERSION; 4218c2ecf20Sopenharmony_ci d204->hdr.len = PAGE_SIZE * diag204_buf_pages; 4228c2ecf20Sopenharmony_ci d204->hdr.sc = diag204_store_sc; 4238c2ecf20Sopenharmony_ci *data = d204; 4248c2ecf20Sopenharmony_ci *data_free_ptr = base; 4258c2ecf20Sopenharmony_ci *size = d204->hdr.len + sizeof(struct dbfs_d204_hdr); 4268c2ecf20Sopenharmony_ci return 0; 4278c2ecf20Sopenharmony_ci} 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_cistatic struct hypfs_dbfs_file dbfs_file_d204 = { 4308c2ecf20Sopenharmony_ci .name = "diag_204", 4318c2ecf20Sopenharmony_ci .data_create = dbfs_d204_create, 4328c2ecf20Sopenharmony_ci .data_free = vfree, 4338c2ecf20Sopenharmony_ci}; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci__init int hypfs_diag_init(void) 4368c2ecf20Sopenharmony_ci{ 4378c2ecf20Sopenharmony_ci int rc; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci if (diag204_probe()) { 4408c2ecf20Sopenharmony_ci pr_info("The hardware system does not support hypfs\n"); 4418c2ecf20Sopenharmony_ci return -ENODATA; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (diag204_info_type == DIAG204_INFO_EXT) 4458c2ecf20Sopenharmony_ci hypfs_dbfs_create_file(&dbfs_file_d204); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if (MACHINE_IS_LPAR) { 4488c2ecf20Sopenharmony_ci rc = diag224_get_name_table(); 4498c2ecf20Sopenharmony_ci if (rc) { 4508c2ecf20Sopenharmony_ci pr_err("The hardware system does not provide all " 4518c2ecf20Sopenharmony_ci "functions required by hypfs\n"); 4528c2ecf20Sopenharmony_ci debugfs_remove(dbfs_d204_file); 4538c2ecf20Sopenharmony_ci return rc; 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci return 0; 4578c2ecf20Sopenharmony_ci} 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_civoid hypfs_diag_exit(void) 4608c2ecf20Sopenharmony_ci{ 4618c2ecf20Sopenharmony_ci debugfs_remove(dbfs_d204_file); 4628c2ecf20Sopenharmony_ci diag224_delete_name_table(); 4638c2ecf20Sopenharmony_ci diag204_free_buffer(); 4648c2ecf20Sopenharmony_ci hypfs_dbfs_remove_file(&dbfs_file_d204); 4658c2ecf20Sopenharmony_ci} 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci/* 4688c2ecf20Sopenharmony_ci * Functions to create the directory structure 4698c2ecf20Sopenharmony_ci * ******************************************* 4708c2ecf20Sopenharmony_ci */ 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic int hypfs_create_cpu_files(struct dentry *cpus_dir, void *cpu_info) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci struct dentry *cpu_dir; 4758c2ecf20Sopenharmony_ci char buffer[TMP_SIZE]; 4768c2ecf20Sopenharmony_ci void *rc; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci snprintf(buffer, TMP_SIZE, "%d", cpu_info__cpu_addr(diag204_info_type, 4798c2ecf20Sopenharmony_ci cpu_info)); 4808c2ecf20Sopenharmony_ci cpu_dir = hypfs_mkdir(cpus_dir, buffer); 4818c2ecf20Sopenharmony_ci rc = hypfs_create_u64(cpu_dir, "mgmtime", 4828c2ecf20Sopenharmony_ci cpu_info__acc_time(diag204_info_type, cpu_info) - 4838c2ecf20Sopenharmony_ci cpu_info__lp_time(diag204_info_type, cpu_info)); 4848c2ecf20Sopenharmony_ci if (IS_ERR(rc)) 4858c2ecf20Sopenharmony_ci return PTR_ERR(rc); 4868c2ecf20Sopenharmony_ci rc = hypfs_create_u64(cpu_dir, "cputime", 4878c2ecf20Sopenharmony_ci cpu_info__lp_time(diag204_info_type, cpu_info)); 4888c2ecf20Sopenharmony_ci if (IS_ERR(rc)) 4898c2ecf20Sopenharmony_ci return PTR_ERR(rc); 4908c2ecf20Sopenharmony_ci if (diag204_info_type == DIAG204_INFO_EXT) { 4918c2ecf20Sopenharmony_ci rc = hypfs_create_u64(cpu_dir, "onlinetime", 4928c2ecf20Sopenharmony_ci cpu_info__online_time(diag204_info_type, 4938c2ecf20Sopenharmony_ci cpu_info)); 4948c2ecf20Sopenharmony_ci if (IS_ERR(rc)) 4958c2ecf20Sopenharmony_ci return PTR_ERR(rc); 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci diag224_idx2name(cpu_info__ctidx(diag204_info_type, cpu_info), buffer); 4988c2ecf20Sopenharmony_ci rc = hypfs_create_str(cpu_dir, "type", buffer); 4998c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(rc); 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_cistatic void *hypfs_create_lpar_files(struct dentry *systems_dir, void *part_hdr) 5038c2ecf20Sopenharmony_ci{ 5048c2ecf20Sopenharmony_ci struct dentry *cpus_dir; 5058c2ecf20Sopenharmony_ci struct dentry *lpar_dir; 5068c2ecf20Sopenharmony_ci char lpar_name[DIAG204_LPAR_NAME_LEN + 1]; 5078c2ecf20Sopenharmony_ci void *cpu_info; 5088c2ecf20Sopenharmony_ci int i; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci part_hdr__part_name(diag204_info_type, part_hdr, lpar_name); 5118c2ecf20Sopenharmony_ci lpar_name[DIAG204_LPAR_NAME_LEN] = 0; 5128c2ecf20Sopenharmony_ci lpar_dir = hypfs_mkdir(systems_dir, lpar_name); 5138c2ecf20Sopenharmony_ci if (IS_ERR(lpar_dir)) 5148c2ecf20Sopenharmony_ci return lpar_dir; 5158c2ecf20Sopenharmony_ci cpus_dir = hypfs_mkdir(lpar_dir, "cpus"); 5168c2ecf20Sopenharmony_ci if (IS_ERR(cpus_dir)) 5178c2ecf20Sopenharmony_ci return cpus_dir; 5188c2ecf20Sopenharmony_ci cpu_info = part_hdr + part_hdr__size(diag204_info_type); 5198c2ecf20Sopenharmony_ci for (i = 0; i < part_hdr__rcpus(diag204_info_type, part_hdr); i++) { 5208c2ecf20Sopenharmony_ci int rc; 5218c2ecf20Sopenharmony_ci rc = hypfs_create_cpu_files(cpus_dir, cpu_info); 5228c2ecf20Sopenharmony_ci if (rc) 5238c2ecf20Sopenharmony_ci return ERR_PTR(rc); 5248c2ecf20Sopenharmony_ci cpu_info += cpu_info__size(diag204_info_type); 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci return cpu_info; 5278c2ecf20Sopenharmony_ci} 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_cistatic int hypfs_create_phys_cpu_files(struct dentry *cpus_dir, void *cpu_info) 5308c2ecf20Sopenharmony_ci{ 5318c2ecf20Sopenharmony_ci struct dentry *cpu_dir; 5328c2ecf20Sopenharmony_ci char buffer[TMP_SIZE]; 5338c2ecf20Sopenharmony_ci void *rc; 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci snprintf(buffer, TMP_SIZE, "%i", phys_cpu__cpu_addr(diag204_info_type, 5368c2ecf20Sopenharmony_ci cpu_info)); 5378c2ecf20Sopenharmony_ci cpu_dir = hypfs_mkdir(cpus_dir, buffer); 5388c2ecf20Sopenharmony_ci if (IS_ERR(cpu_dir)) 5398c2ecf20Sopenharmony_ci return PTR_ERR(cpu_dir); 5408c2ecf20Sopenharmony_ci rc = hypfs_create_u64(cpu_dir, "mgmtime", 5418c2ecf20Sopenharmony_ci phys_cpu__mgm_time(diag204_info_type, cpu_info)); 5428c2ecf20Sopenharmony_ci if (IS_ERR(rc)) 5438c2ecf20Sopenharmony_ci return PTR_ERR(rc); 5448c2ecf20Sopenharmony_ci diag224_idx2name(phys_cpu__ctidx(diag204_info_type, cpu_info), buffer); 5458c2ecf20Sopenharmony_ci rc = hypfs_create_str(cpu_dir, "type", buffer); 5468c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(rc); 5478c2ecf20Sopenharmony_ci} 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_cistatic void *hypfs_create_phys_files(struct dentry *parent_dir, void *phys_hdr) 5508c2ecf20Sopenharmony_ci{ 5518c2ecf20Sopenharmony_ci int i; 5528c2ecf20Sopenharmony_ci void *cpu_info; 5538c2ecf20Sopenharmony_ci struct dentry *cpus_dir; 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci cpus_dir = hypfs_mkdir(parent_dir, "cpus"); 5568c2ecf20Sopenharmony_ci if (IS_ERR(cpus_dir)) 5578c2ecf20Sopenharmony_ci return cpus_dir; 5588c2ecf20Sopenharmony_ci cpu_info = phys_hdr + phys_hdr__size(diag204_info_type); 5598c2ecf20Sopenharmony_ci for (i = 0; i < phys_hdr__cpus(diag204_info_type, phys_hdr); i++) { 5608c2ecf20Sopenharmony_ci int rc; 5618c2ecf20Sopenharmony_ci rc = hypfs_create_phys_cpu_files(cpus_dir, cpu_info); 5628c2ecf20Sopenharmony_ci if (rc) 5638c2ecf20Sopenharmony_ci return ERR_PTR(rc); 5648c2ecf20Sopenharmony_ci cpu_info += phys_cpu__size(diag204_info_type); 5658c2ecf20Sopenharmony_ci } 5668c2ecf20Sopenharmony_ci return cpu_info; 5678c2ecf20Sopenharmony_ci} 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ciint hypfs_diag_create_files(struct dentry *root) 5708c2ecf20Sopenharmony_ci{ 5718c2ecf20Sopenharmony_ci struct dentry *systems_dir, *hyp_dir; 5728c2ecf20Sopenharmony_ci void *time_hdr, *part_hdr; 5738c2ecf20Sopenharmony_ci int i, rc; 5748c2ecf20Sopenharmony_ci void *buffer, *ptr; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci buffer = diag204_store(); 5778c2ecf20Sopenharmony_ci if (IS_ERR(buffer)) 5788c2ecf20Sopenharmony_ci return PTR_ERR(buffer); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci systems_dir = hypfs_mkdir(root, "systems"); 5818c2ecf20Sopenharmony_ci if (IS_ERR(systems_dir)) { 5828c2ecf20Sopenharmony_ci rc = PTR_ERR(systems_dir); 5838c2ecf20Sopenharmony_ci goto err_out; 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci time_hdr = (struct x_info_blk_hdr *)buffer; 5868c2ecf20Sopenharmony_ci part_hdr = time_hdr + info_blk_hdr__size(diag204_info_type); 5878c2ecf20Sopenharmony_ci for (i = 0; i < info_blk_hdr__npar(diag204_info_type, time_hdr); i++) { 5888c2ecf20Sopenharmony_ci part_hdr = hypfs_create_lpar_files(systems_dir, part_hdr); 5898c2ecf20Sopenharmony_ci if (IS_ERR(part_hdr)) { 5908c2ecf20Sopenharmony_ci rc = PTR_ERR(part_hdr); 5918c2ecf20Sopenharmony_ci goto err_out; 5928c2ecf20Sopenharmony_ci } 5938c2ecf20Sopenharmony_ci } 5948c2ecf20Sopenharmony_ci if (info_blk_hdr__flags(diag204_info_type, time_hdr) & 5958c2ecf20Sopenharmony_ci DIAG204_LPAR_PHYS_FLG) { 5968c2ecf20Sopenharmony_ci ptr = hypfs_create_phys_files(root, part_hdr); 5978c2ecf20Sopenharmony_ci if (IS_ERR(ptr)) { 5988c2ecf20Sopenharmony_ci rc = PTR_ERR(ptr); 5998c2ecf20Sopenharmony_ci goto err_out; 6008c2ecf20Sopenharmony_ci } 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci hyp_dir = hypfs_mkdir(root, "hyp"); 6038c2ecf20Sopenharmony_ci if (IS_ERR(hyp_dir)) { 6048c2ecf20Sopenharmony_ci rc = PTR_ERR(hyp_dir); 6058c2ecf20Sopenharmony_ci goto err_out; 6068c2ecf20Sopenharmony_ci } 6078c2ecf20Sopenharmony_ci ptr = hypfs_create_str(hyp_dir, "type", "LPAR Hypervisor"); 6088c2ecf20Sopenharmony_ci if (IS_ERR(ptr)) { 6098c2ecf20Sopenharmony_ci rc = PTR_ERR(ptr); 6108c2ecf20Sopenharmony_ci goto err_out; 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci rc = 0; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_cierr_out: 6158c2ecf20Sopenharmony_ci return rc; 6168c2ecf20Sopenharmony_ci} 617