1 /* Get system parameters, e.g. cache information.  S390/S390x version.
2    Copyright (C) 2015-2022 Free Software Foundation, Inc.
3    This file is part of the GNU C Library.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9 
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14 
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library.  If not, see
17    <https://www.gnu.org/licenses/>.  */
18 
19 #include <unistd.h>
20 #include <dl-procinfo.h>
21 
22 static long int linux_sysconf (int name);
23 
24 /* Possible arguments for get_cache_info.
25    The values are reflecting the level/attribute/type indications
26    of ecag-instruction (extract cpu attribue).  */
27 #define CACHE_LEVEL_MAX        8
28 #define CACHE_ATTR_LINESIZE    1
29 #define CACHE_ATTR_SIZE        2
30 #define CACHE_ATTR_ASSOC       3
31 #define CACHE_TYPE_DATA        0
32 #define CACHE_TYPE_INSTRUCTION 1
33 
34 static long
get_cache_info(int level,int attr,int type)35 get_cache_info (int level, int attr, int type)
36 {
37   unsigned long int val;
38   unsigned int cmd;
39   unsigned long int arg;
40 
41   /* Check arguments.  */
42   if (level < 1 || level > CACHE_LEVEL_MAX
43       || attr < CACHE_ATTR_LINESIZE || attr > CACHE_ATTR_ASSOC
44       || type < CACHE_TYPE_DATA || type > CACHE_TYPE_INSTRUCTION)
45     return 0L;
46 
47   /* Check if ecag-instruction is available.
48      ecag - extract CPU attribute (only in zarch; arch >= z10; in as 2.24)  */
49   if (!(GLRO (dl_hwcap) & HWCAP_S390_STFLE)
50 #if !defined __s390x__
51       || !(GLRO (dl_hwcap) & HWCAP_S390_ZARCH)
52       || !(GLRO (dl_hwcap) & HWCAP_S390_HIGH_GPRS)
53 #endif /* !__s390x__ */
54       )
55     {
56       /* stfle (or zarch, high-gprs on s390-32) is not available.
57 	 We are on an old machine. Return 256byte for LINESIZE for L1 d/i-cache,
58 	 otherwise 0.  */
59       if (level == 1 && attr == CACHE_ATTR_LINESIZE)
60 	return 256L;
61       else
62 	return 0L;
63     }
64 
65   /* Store facility list and check for z10.
66      (see ifunc-resolver for details)  */
67   register unsigned long reg0 __asm__("0") = 0;
68 #ifdef __s390x__
69   unsigned long stfle_bits;
70 # define STFLE_Z10_MASK (1UL << (63 - 34))
71 #else
72   unsigned long long stfle_bits;
73 # define STFLE_Z10_MASK (1ULL << (63 - 34))
74 #endif /* !__s390x__ */
75   __asm__ __volatile__(".machine push"        "\n\t"
76 		       ".machinemode \"zarch_nohighgprs\"\n\t"
77 		       ".machine \"z9-109\""  "\n\t"
78 		       "stfle %0"             "\n\t"
79 		       ".machine pop"         "\n"
80 		       : "=QS" (stfle_bits), "+d" (reg0)
81 		       : : "cc");
82 
83   if (!(stfle_bits & STFLE_Z10_MASK))
84     {
85       /* We are at least on a z9 machine.
86 	 Return 256byte for LINESIZE for L1 d/i-cache,
87 	 otherwise 0.  */
88       if (level == 1 && attr == CACHE_ATTR_LINESIZE)
89 	return 256L;
90       else
91 	return 0L;
92     }
93 
94   /* Check cache topology, if cache is available at this level.  */
95   arg = (CACHE_LEVEL_MAX - level) * 8;
96   __asm__ __volatile__ (".machine push\n\t"
97 			".machine \"z10\"\n\t"
98 			".machinemode \"zarch_nohighgprs\"\n\t"
99 			"ecag %0,%%r0,0\n\t"   /* returns 64bit unsigned integer.  */
100 			"srlg %0,%0,0(%1)\n\t" /* right align 8bit cache info field.  */
101 			".machine pop"
102 			: "=&d" (val)
103 			: "a" (arg)
104 			);
105   val &= 0xCUL; /* Extract cache scope information from cache topology summary.
106 		   (bits 4-5 of 8bit-field; 00 means cache does not exist).  */
107   if (val == 0)
108     return 0L;
109 
110   /* Get cache information for level, attribute and type.  */
111   cmd = (attr << 4) | ((level - 1) << 1) | type;
112   __asm__ __volatile__ (".machine push\n\t"
113 			".machine \"z10\"\n\t"
114 			".machinemode \"zarch_nohighgprs\"\n\t"
115 			"ecag %0,%%r0,0(%1)\n\t"
116 			".machine pop"
117 			: "=d" (val)
118 			: "a" (cmd)
119 			);
120   return val;
121 }
122 
123 long int
__sysconf(int name)124 __sysconf (int name)
125 {
126   if (name >= _SC_LEVEL1_ICACHE_SIZE && name <= _SC_LEVEL4_CACHE_LINESIZE)
127     {
128       int level;
129       int attr;
130       int type;
131 
132       switch (name)
133 	{
134 	case _SC_LEVEL1_ICACHE_SIZE:
135 	  level = 1;
136 	  attr = CACHE_ATTR_SIZE;
137 	  type = CACHE_TYPE_INSTRUCTION;
138 	  break;
139 	case _SC_LEVEL1_ICACHE_ASSOC:
140 	  level = 1;
141 	  attr = CACHE_ATTR_ASSOC;
142 	  type = CACHE_TYPE_INSTRUCTION;
143 	  break;
144 	case _SC_LEVEL1_ICACHE_LINESIZE:
145 	  level = 1;
146 	  attr = CACHE_ATTR_LINESIZE;
147 	  type = CACHE_TYPE_INSTRUCTION;
148 	  break;
149 
150 	case _SC_LEVEL1_DCACHE_SIZE:
151 	  level = 1;
152 	  attr = CACHE_ATTR_SIZE;
153 	  type = CACHE_TYPE_DATA;
154 	  break;
155 	case _SC_LEVEL1_DCACHE_ASSOC:
156 	  level = 1;
157 	  attr = CACHE_ATTR_ASSOC;
158 	  type = CACHE_TYPE_DATA;
159 	  break;
160 	case _SC_LEVEL1_DCACHE_LINESIZE:
161 	  level = 1;
162 	  attr = CACHE_ATTR_LINESIZE;
163 	  type = CACHE_TYPE_DATA;
164 	  break;
165 
166 	case _SC_LEVEL2_CACHE_SIZE:
167 	  level = 2;
168 	  attr = CACHE_ATTR_SIZE;
169 	  type = CACHE_TYPE_DATA;
170 	  break;
171 	case _SC_LEVEL2_CACHE_ASSOC:
172 	  level = 2;
173 	  attr = CACHE_ATTR_ASSOC;
174 	  type = CACHE_TYPE_DATA;
175 	  break;
176 	case _SC_LEVEL2_CACHE_LINESIZE:
177 	  level = 2;
178 	  attr = CACHE_ATTR_LINESIZE;
179 	  type = CACHE_TYPE_DATA;
180 	  break;
181 
182 	case _SC_LEVEL3_CACHE_SIZE:
183 	  level = 3;
184 	  attr = CACHE_ATTR_SIZE;
185 	  type = CACHE_TYPE_DATA;
186 	  break;
187 	case _SC_LEVEL3_CACHE_ASSOC:
188 	  level = 3;
189 	  attr = CACHE_ATTR_ASSOC;
190 	  type = CACHE_TYPE_DATA;
191 	  break;
192 	case _SC_LEVEL3_CACHE_LINESIZE:
193 	  level = 3;
194 	  attr = CACHE_ATTR_LINESIZE;
195 	  type = CACHE_TYPE_DATA;
196 	  break;
197 
198 	case _SC_LEVEL4_CACHE_SIZE:
199 	  level = 4;
200 	  attr = CACHE_ATTR_SIZE;
201 	  type = CACHE_TYPE_DATA;
202 	  break;
203 	case _SC_LEVEL4_CACHE_ASSOC:
204 	  level = 4;
205 	  attr = CACHE_ATTR_ASSOC;
206 	  type = CACHE_TYPE_DATA;
207 	  break;
208 	case _SC_LEVEL4_CACHE_LINESIZE:
209 	  level = 4;
210 	  attr = CACHE_ATTR_LINESIZE;
211 	  type = CACHE_TYPE_DATA;
212 	  break;
213 
214 	default:
215 	  level = 0;
216 	  attr = 0;
217 	  type = 0;
218 	  break;
219 	}
220 
221       return get_cache_info (level, attr, type);
222     }
223 
224   return linux_sysconf (name);
225 }
226 
227 /* Now the generic Linux version.  */
228 #undef __sysconf
229 #define __sysconf static linux_sysconf
230 #include <sysdeps/unix/sysv/linux/sysconf.c>
231