1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * Hypervisor filesystem for Linux on s390. z/VM implementation. |
4 | * |
5 | * Copyright IBM Corp. 2006 |
6 | * Author(s): Michael Holzheu <holzheu@de.ibm.com> |
7 | */ |
8 | |
9 | #include <linux/types.h> |
10 | #include <linux/errno.h> |
11 | #include <linux/string.h> |
12 | #include <linux/vmalloc.h> |
13 | #include <asm/extable.h> |
14 | #include <asm/diag.h> |
15 | #include <asm/ebcdic.h> |
16 | #include <asm/timex.h> |
17 | #include "hypfs_vm.h" |
18 | #include "hypfs.h" |
19 | |
20 | #define DBFS_D2FC_HDR_VERSION 0 |
21 | |
22 | static char local_guest[] = " " ; |
23 | static char all_guests[] = "* " ; |
24 | static char *all_groups = all_guests; |
25 | char *diag2fc_guest_query; |
26 | |
27 | static int diag2fc(int size, char* query, void *addr) |
28 | { |
29 | unsigned long residual_cnt; |
30 | unsigned long rc; |
31 | struct diag2fc_parm_list parm_list; |
32 | |
33 | memcpy(parm_list.userid, query, DIAG2FC_NAME_LEN); |
34 | ASCEBC(parm_list.userid, DIAG2FC_NAME_LEN); |
35 | memcpy(parm_list.aci_grp, all_groups, DIAG2FC_NAME_LEN); |
36 | ASCEBC(parm_list.aci_grp, DIAG2FC_NAME_LEN); |
37 | parm_list.addr = (unsigned long)addr; |
38 | parm_list.size = size; |
39 | parm_list.fmt = 0x02; |
40 | rc = -1; |
41 | |
42 | diag_stat_inc(DIAG_STAT_X2FC); |
43 | asm volatile( |
44 | " diag %0,%1,0x2fc\n" |
45 | "0: nopr %%r7\n" |
46 | EX_TABLE(0b,0b) |
47 | : "=d" (residual_cnt), "+d" (rc) : "0" (&parm_list) : "memory" ); |
48 | |
49 | if ((rc != 0 ) && (rc != -2)) |
50 | return rc; |
51 | else |
52 | return -residual_cnt; |
53 | } |
54 | |
55 | /* |
56 | * Allocate buffer for "query" and store diag 2fc at "offset" |
57 | */ |
58 | void *diag2fc_store(char *query, unsigned int *count, int offset) |
59 | { |
60 | void *data; |
61 | int size; |
62 | |
63 | do { |
64 | size = diag2fc(size: 0, query, NULL); |
65 | if (size < 0) |
66 | return ERR_PTR(error: -EACCES); |
67 | data = vmalloc(size: size + offset); |
68 | if (!data) |
69 | return ERR_PTR(error: -ENOMEM); |
70 | if (diag2fc(size, query, addr: data + offset) == 0) |
71 | break; |
72 | vfree(addr: data); |
73 | } while (1); |
74 | *count = (size / sizeof(struct diag2fc_data)); |
75 | |
76 | return data; |
77 | } |
78 | |
79 | void diag2fc_free(const void *data) |
80 | { |
81 | vfree(addr: data); |
82 | } |
83 | |
84 | struct dbfs_d2fc_hdr { |
85 | u64 len; /* Length of d2fc buffer without header */ |
86 | u16 version; /* Version of header */ |
87 | union tod_clock tod_ext; /* TOD clock for d2fc */ |
88 | u64 count; /* Number of VM guests in d2fc buffer */ |
89 | char reserved[30]; |
90 | } __attribute__ ((packed)); |
91 | |
92 | struct dbfs_d2fc { |
93 | struct dbfs_d2fc_hdr hdr; /* 64 byte header */ |
94 | char buf[]; /* d2fc buffer */ |
95 | } __attribute__ ((packed)); |
96 | |
97 | static int dbfs_diag2fc_create(void **data, void **data_free_ptr, size_t *size) |
98 | { |
99 | struct dbfs_d2fc *d2fc; |
100 | unsigned int count; |
101 | |
102 | d2fc = diag2fc_store(query: diag2fc_guest_query, count: &count, offset: sizeof(d2fc->hdr)); |
103 | if (IS_ERR(ptr: d2fc)) |
104 | return PTR_ERR(ptr: d2fc); |
105 | store_tod_clock_ext(&d2fc->hdr.tod_ext); |
106 | d2fc->hdr.len = count * sizeof(struct diag2fc_data); |
107 | d2fc->hdr.version = DBFS_D2FC_HDR_VERSION; |
108 | d2fc->hdr.count = count; |
109 | memset(&d2fc->hdr.reserved, 0, sizeof(d2fc->hdr.reserved)); |
110 | *data = d2fc; |
111 | *data_free_ptr = d2fc; |
112 | *size = d2fc->hdr.len + sizeof(struct dbfs_d2fc_hdr); |
113 | return 0; |
114 | } |
115 | |
116 | static struct hypfs_dbfs_file dbfs_file_2fc = { |
117 | .name = "diag_2fc" , |
118 | .data_create = dbfs_diag2fc_create, |
119 | .data_free = diag2fc_free, |
120 | }; |
121 | |
122 | int hypfs_vm_init(void) |
123 | { |
124 | if (!MACHINE_IS_VM) |
125 | return 0; |
126 | if (diag2fc(size: 0, query: all_guests, NULL) > 0) |
127 | diag2fc_guest_query = all_guests; |
128 | else if (diag2fc(size: 0, query: local_guest, NULL) > 0) |
129 | diag2fc_guest_query = local_guest; |
130 | else |
131 | return -EACCES; |
132 | hypfs_dbfs_create_file(df: &dbfs_file_2fc); |
133 | return 0; |
134 | } |
135 | |
136 | void hypfs_vm_exit(void) |
137 | { |
138 | if (!MACHINE_IS_VM) |
139 | return; |
140 | hypfs_dbfs_remove_file(df: &dbfs_file_2fc); |
141 | } |
142 | |