bellard | 6af0bf9 | 2005-07-02 14:58:51 +0000 | [diff] [blame] | 1 | /* |
| 2 | * MIPS emulation helpers for qemu. |
ths | 5fafdf2 | 2007-09-16 21:08:06 +0000 | [diff] [blame] | 3 | * |
bellard | 6af0bf9 | 2005-07-02 14:58:51 +0000 | [diff] [blame] | 4 | * Copyright (c) 2004-2005 Jocelyn Mayer |
| 5 | * |
| 6 | * This library is free software; you can redistribute it and/or |
| 7 | * modify it under the terms of the GNU Lesser General Public |
| 8 | * License as published by the Free Software Foundation; either |
Chetan Pant | 8997521 | 2020-10-16 14:35:09 +0000 | [diff] [blame] | 9 | * version 2.1 of the License, or (at your option) any later version. |
bellard | 6af0bf9 | 2005-07-02 14:58:51 +0000 | [diff] [blame] | 10 | * |
| 11 | * This library is distributed in the hope that it will be useful, |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 14 | * Lesser General Public License for more details. |
| 15 | * |
| 16 | * You should have received a copy of the GNU Lesser General Public |
Blue Swirl | 8167ee8 | 2009-07-16 20:47:01 +0000 | [diff] [blame] | 17 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
Aleksandar Markovic | 256eb7e | 2020-02-03 16:57:22 +0100 | [diff] [blame] | 18 | * |
bellard | 6af0bf9 | 2005-07-02 14:58:51 +0000 | [diff] [blame] | 19 | */ |
Aleksandar Markovic | 256eb7e | 2020-02-03 16:57:22 +0100 | [diff] [blame] | 20 | |
Peter Maydell | c684822 | 2016-01-18 17:35:00 +0000 | [diff] [blame] | 21 | #include "qemu/osdep.h" |
Blue Swirl | 3e45717 | 2011-07-13 12:44:15 +0000 | [diff] [blame] | 22 | #include "cpu.h" |
Philippe Mathieu-Daudé | 26aa3d9 | 2017-09-20 16:49:30 -0300 | [diff] [blame] | 23 | #include "internal.h" |
Richard Henderson | 2ef6175 | 2014-04-07 22:31:41 -0700 | [diff] [blame] | 24 | #include "exec/helper-proto.h" |
Paolo Bonzini | 63c9155 | 2016-03-15 13:18:37 +0100 | [diff] [blame] | 25 | #include "exec/exec-all.h" |
Tony Nguyen | e501824 | 2019-08-24 04:36:41 +1000 | [diff] [blame] | 26 | #include "exec/memop.h" |
Philippe Mathieu-Daudé | 81ddae7 | 2020-11-14 19:03:11 +0100 | [diff] [blame] | 27 | #include "fpu_helper.h" |
Aleksandar Markovic | 256eb7e | 2020-02-03 16:57:22 +0100 | [diff] [blame] | 28 | |
Yongbok Kim | 15eacb9 | 2014-06-27 08:49:05 +0100 | [diff] [blame] | 29 | static inline target_ulong bitswap(target_ulong v) |
| 30 | { |
Leon Alrae | 74dda987 | 2014-10-22 14:00:29 +0100 | [diff] [blame] | 31 | v = ((v >> 1) & (target_ulong)0x5555555555555555ULL) | |
| 32 | ((v & (target_ulong)0x5555555555555555ULL) << 1); |
| 33 | v = ((v >> 2) & (target_ulong)0x3333333333333333ULL) | |
| 34 | ((v & (target_ulong)0x3333333333333333ULL) << 2); |
| 35 | v = ((v >> 4) & (target_ulong)0x0F0F0F0F0F0F0F0FULL) | |
| 36 | ((v & (target_ulong)0x0F0F0F0F0F0F0F0FULL) << 4); |
Yongbok Kim | 15eacb9 | 2014-06-27 08:49:05 +0100 | [diff] [blame] | 37 | return v; |
| 38 | } |
| 39 | |
| 40 | #ifdef TARGET_MIPS64 |
| 41 | target_ulong helper_dbitswap(target_ulong rt) |
| 42 | { |
| 43 | return bitswap(rt); |
| 44 | } |
| 45 | #endif |
| 46 | |
| 47 | target_ulong helper_bitswap(target_ulong rt) |
| 48 | { |
| 49 | return (int32_t)bitswap(rt); |
| 50 | } |
| 51 | |
Matthew Fortune | e222f50 | 2018-08-02 16:16:20 +0200 | [diff] [blame] | 52 | target_ulong helper_rotx(target_ulong rs, uint32_t shift, uint32_t shiftx, |
| 53 | uint32_t stripe) |
| 54 | { |
| 55 | int i; |
| 56 | uint64_t tmp0 = ((uint64_t)rs) << 32 | ((uint64_t)rs & 0xffffffff); |
| 57 | uint64_t tmp1 = tmp0; |
| 58 | for (i = 0; i <= 46; i++) { |
| 59 | int s; |
| 60 | if (i & 0x8) { |
| 61 | s = shift; |
| 62 | } else { |
| 63 | s = shiftx; |
| 64 | } |
| 65 | |
| 66 | if (stripe != 0 && !(i & 0x4)) { |
| 67 | s = ~s; |
| 68 | } |
| 69 | if (s & 0x10) { |
| 70 | if (tmp0 & (1LL << (i + 16))) { |
| 71 | tmp1 |= 1LL << i; |
| 72 | } else { |
| 73 | tmp1 &= ~(1LL << i); |
| 74 | } |
| 75 | } |
| 76 | } |
| 77 | |
| 78 | uint64_t tmp2 = tmp1; |
| 79 | for (i = 0; i <= 38; i++) { |
| 80 | int s; |
| 81 | if (i & 0x4) { |
| 82 | s = shift; |
| 83 | } else { |
| 84 | s = shiftx; |
| 85 | } |
| 86 | |
| 87 | if (s & 0x8) { |
| 88 | if (tmp1 & (1LL << (i + 8))) { |
| 89 | tmp2 |= 1LL << i; |
| 90 | } else { |
| 91 | tmp2 &= ~(1LL << i); |
| 92 | } |
| 93 | } |
| 94 | } |
| 95 | |
| 96 | uint64_t tmp3 = tmp2; |
| 97 | for (i = 0; i <= 34; i++) { |
| 98 | int s; |
| 99 | if (i & 0x2) { |
| 100 | s = shift; |
| 101 | } else { |
| 102 | s = shiftx; |
| 103 | } |
| 104 | if (s & 0x4) { |
| 105 | if (tmp2 & (1LL << (i + 4))) { |
| 106 | tmp3 |= 1LL << i; |
| 107 | } else { |
| 108 | tmp3 &= ~(1LL << i); |
| 109 | } |
| 110 | } |
| 111 | } |
| 112 | |
| 113 | uint64_t tmp4 = tmp3; |
| 114 | for (i = 0; i <= 32; i++) { |
| 115 | int s; |
| 116 | if (i & 0x1) { |
| 117 | s = shift; |
| 118 | } else { |
| 119 | s = shiftx; |
| 120 | } |
| 121 | if (s & 0x2) { |
| 122 | if (tmp3 & (1LL << (i + 2))) { |
| 123 | tmp4 |= 1LL << i; |
| 124 | } else { |
| 125 | tmp4 &= ~(1LL << i); |
| 126 | } |
| 127 | } |
| 128 | } |
| 129 | |
| 130 | uint64_t tmp5 = tmp4; |
| 131 | for (i = 0; i <= 31; i++) { |
| 132 | int s; |
| 133 | s = shift; |
| 134 | if (s & 0x1) { |
| 135 | if (tmp4 & (1LL << (i + 1))) { |
| 136 | tmp5 |= 1LL << i; |
| 137 | } else { |
| 138 | tmp5 &= ~(1LL << i); |
| 139 | } |
| 140 | } |
| 141 | } |
| 142 | |
| 143 | return (int64_t)(int32_t)(uint32_t)tmp5; |
| 144 | } |
| 145 | |
aurel32 | d9bea11 | 2009-04-15 14:41:44 +0000 | [diff] [blame] | 146 | void helper_fork(target_ulong arg1, target_ulong arg2) |
ths | f1aa632 | 2008-06-09 07:13:38 +0000 | [diff] [blame] | 147 | { |
Aleksandar Markovic | 14521a2 | 2019-10-23 12:23:35 +0200 | [diff] [blame] | 148 | /* |
| 149 | * arg1 = rt, arg2 = rs |
| 150 | * TODO: store to TC register |
| 151 | */ |
ths | f1aa632 | 2008-06-09 07:13:38 +0000 | [diff] [blame] | 152 | } |
| 153 | |
Blue Swirl | 895c2d0 | 2012-09-02 14:52:59 +0000 | [diff] [blame] | 154 | target_ulong helper_yield(CPUMIPSState *env, target_ulong arg) |
ths | f1aa632 | 2008-06-09 07:13:38 +0000 | [diff] [blame] | 155 | { |
Blue Swirl | 1c7242d | 2010-09-18 05:53:15 +0000 | [diff] [blame] | 156 | target_long arg1 = arg; |
| 157 | |
aurel32 | d9bea11 | 2009-04-15 14:41:44 +0000 | [diff] [blame] | 158 | if (arg1 < 0) { |
ths | f1aa632 | 2008-06-09 07:13:38 +0000 | [diff] [blame] | 159 | /* No scheduling policy implemented. */ |
aurel32 | d9bea11 | 2009-04-15 14:41:44 +0000 | [diff] [blame] | 160 | if (arg1 != -2) { |
ths | f1aa632 | 2008-06-09 07:13:38 +0000 | [diff] [blame] | 161 | if (env->CP0_VPEControl & (1 << CP0VPECo_YSI) && |
ths | b5dc773 | 2008-06-27 10:02:35 +0000 | [diff] [blame] | 162 | env->active_tc.CP0_TCStatus & (1 << CP0TCSt_DT)) { |
ths | f1aa632 | 2008-06-09 07:13:38 +0000 | [diff] [blame] | 163 | env->CP0_VPEControl &= ~(0x7 << CP0VPECo_EXCPT); |
| 164 | env->CP0_VPEControl |= 4 << CP0VPECo_EXCPT; |
Pavel Dovgaluk | 9c708c7 | 2015-07-10 12:57:08 +0300 | [diff] [blame] | 165 | do_raise_exception(env, EXCP_THREAD, GETPC()); |
ths | f1aa632 | 2008-06-09 07:13:38 +0000 | [diff] [blame] | 166 | } |
| 167 | } |
aurel32 | d9bea11 | 2009-04-15 14:41:44 +0000 | [diff] [blame] | 168 | } else if (arg1 == 0) { |
Aleksandar Markovic | 14521a2 | 2019-10-23 12:23:35 +0200 | [diff] [blame] | 169 | if (0) { |
| 170 | /* TODO: TC underflow */ |
ths | f1aa632 | 2008-06-09 07:13:38 +0000 | [diff] [blame] | 171 | env->CP0_VPEControl &= ~(0x7 << CP0VPECo_EXCPT); |
Pavel Dovgaluk | 9c708c7 | 2015-07-10 12:57:08 +0300 | [diff] [blame] | 172 | do_raise_exception(env, EXCP_THREAD, GETPC()); |
ths | f1aa632 | 2008-06-09 07:13:38 +0000 | [diff] [blame] | 173 | } else { |
Aleksandar Markovic | 14521a2 | 2019-10-23 12:23:35 +0200 | [diff] [blame] | 174 | /* TODO: Deallocate TC */ |
ths | f1aa632 | 2008-06-09 07:13:38 +0000 | [diff] [blame] | 175 | } |
aurel32 | d9bea11 | 2009-04-15 14:41:44 +0000 | [diff] [blame] | 176 | } else if (arg1 > 0) { |
ths | f1aa632 | 2008-06-09 07:13:38 +0000 | [diff] [blame] | 177 | /* Yield qualifier inputs not implemented. */ |
| 178 | env->CP0_VPEControl &= ~(0x7 << CP0VPECo_EXCPT); |
| 179 | env->CP0_VPEControl |= 2 << CP0VPECo_EXCPT; |
Pavel Dovgaluk | 9c708c7 | 2015-07-10 12:57:08 +0300 | [diff] [blame] | 180 | do_raise_exception(env, EXCP_THREAD, GETPC()); |
ths | f1aa632 | 2008-06-09 07:13:38 +0000 | [diff] [blame] | 181 | } |
ths | be24bb4 | 2008-06-23 12:57:09 +0000 | [diff] [blame] | 182 | return env->CP0_YQMask; |
ths | f1aa632 | 2008-06-09 07:13:38 +0000 | [diff] [blame] | 183 | } |
| 184 | |
James Hogan | d96391c | 2016-04-27 23:21:06 +0100 | [diff] [blame] | 185 | static inline void check_hwrena(CPUMIPSState *env, int reg, uintptr_t pc) |
Yongbok Kim | b00c721 | 2015-10-29 15:18:39 +0000 | [diff] [blame] | 186 | { |
| 187 | if ((env->hflags & MIPS_HFLAG_CP0) || (env->CP0_HWREna & (1 << reg))) { |
| 188 | return; |
| 189 | } |
James Hogan | d96391c | 2016-04-27 23:21:06 +0100 | [diff] [blame] | 190 | do_raise_exception(env, EXCP_RI, pc); |
Yongbok Kim | b00c721 | 2015-10-29 15:18:39 +0000 | [diff] [blame] | 191 | } |
| 192 | |
Blue Swirl | 895c2d0 | 2012-09-02 14:52:59 +0000 | [diff] [blame] | 193 | target_ulong helper_rdhwr_cpunum(CPUMIPSState *env) |
ths | 2b0233a | 2008-06-12 12:42:35 +0000 | [diff] [blame] | 194 | { |
James Hogan | d96391c | 2016-04-27 23:21:06 +0100 | [diff] [blame] | 195 | check_hwrena(env, 0, GETPC()); |
Yongbok Kim | b00c721 | 2015-10-29 15:18:39 +0000 | [diff] [blame] | 196 | return env->CP0_EBase & 0x3ff; |
ths | 2b0233a | 2008-06-12 12:42:35 +0000 | [diff] [blame] | 197 | } |
| 198 | |
Blue Swirl | 895c2d0 | 2012-09-02 14:52:59 +0000 | [diff] [blame] | 199 | target_ulong helper_rdhwr_synci_step(CPUMIPSState *env) |
ths | 2b0233a | 2008-06-12 12:42:35 +0000 | [diff] [blame] | 200 | { |
James Hogan | d96391c | 2016-04-27 23:21:06 +0100 | [diff] [blame] | 201 | check_hwrena(env, 1, GETPC()); |
Yongbok Kim | b00c721 | 2015-10-29 15:18:39 +0000 | [diff] [blame] | 202 | return env->SYNCI_Step; |
ths | 2b0233a | 2008-06-12 12:42:35 +0000 | [diff] [blame] | 203 | } |
| 204 | |
Blue Swirl | 895c2d0 | 2012-09-02 14:52:59 +0000 | [diff] [blame] | 205 | target_ulong helper_rdhwr_cc(CPUMIPSState *env) |
ths | 2b0233a | 2008-06-12 12:42:35 +0000 | [diff] [blame] | 206 | { |
James Hogan | d96391c | 2016-04-27 23:21:06 +0100 | [diff] [blame] | 207 | check_hwrena(env, 2, GETPC()); |
Alex Smith | cdfcad7 | 2015-09-08 11:34:11 +0100 | [diff] [blame] | 208 | #ifdef CONFIG_USER_ONLY |
Aleksandar Markovic | 215581b | 2019-02-11 16:28:16 +0100 | [diff] [blame] | 209 | return env->CP0_Count; |
Alex Smith | cdfcad7 | 2015-09-08 11:34:11 +0100 | [diff] [blame] | 210 | #else |
Aleksandar Markovic | 215581b | 2019-02-11 16:28:16 +0100 | [diff] [blame] | 211 | return (int32_t)cpu_mips_get_count(env); |
Alex Smith | cdfcad7 | 2015-09-08 11:34:11 +0100 | [diff] [blame] | 212 | #endif |
ths | 2b0233a | 2008-06-12 12:42:35 +0000 | [diff] [blame] | 213 | } |
| 214 | |
Blue Swirl | 895c2d0 | 2012-09-02 14:52:59 +0000 | [diff] [blame] | 215 | target_ulong helper_rdhwr_ccres(CPUMIPSState *env) |
ths | 2b0233a | 2008-06-12 12:42:35 +0000 | [diff] [blame] | 216 | { |
James Hogan | d96391c | 2016-04-27 23:21:06 +0100 | [diff] [blame] | 217 | check_hwrena(env, 3, GETPC()); |
Yongbok Kim | b00c721 | 2015-10-29 15:18:39 +0000 | [diff] [blame] | 218 | return env->CCRes; |
| 219 | } |
ths | be24bb4 | 2008-06-23 12:57:09 +0000 | [diff] [blame] | 220 | |
Yongbok Kim | b00c721 | 2015-10-29 15:18:39 +0000 | [diff] [blame] | 221 | target_ulong helper_rdhwr_performance(CPUMIPSState *env) |
| 222 | { |
James Hogan | d96391c | 2016-04-27 23:21:06 +0100 | [diff] [blame] | 223 | check_hwrena(env, 4, GETPC()); |
Yongbok Kim | b00c721 | 2015-10-29 15:18:39 +0000 | [diff] [blame] | 224 | return env->CP0_Performance0; |
| 225 | } |
| 226 | |
| 227 | target_ulong helper_rdhwr_xnp(CPUMIPSState *env) |
| 228 | { |
James Hogan | d96391c | 2016-04-27 23:21:06 +0100 | [diff] [blame] | 229 | check_hwrena(env, 5, GETPC()); |
Yongbok Kim | b00c721 | 2015-10-29 15:18:39 +0000 | [diff] [blame] | 230 | return (env->CP0_Config5 >> CP0C5_XNP) & 1; |
ths | 2b0233a | 2008-06-12 12:42:35 +0000 | [diff] [blame] | 231 | } |
| 232 | |
Blue Swirl | 895c2d0 | 2012-09-02 14:52:59 +0000 | [diff] [blame] | 233 | void helper_pmon(CPUMIPSState *env, int function) |
bellard | 6af0bf9 | 2005-07-02 14:58:51 +0000 | [diff] [blame] | 234 | { |
| 235 | function /= 2; |
| 236 | switch (function) { |
| 237 | case 2: /* TODO: char inbyte(int waitflag); */ |
Aleksandar Markovic | 14521a2 | 2019-10-23 12:23:35 +0200 | [diff] [blame] | 238 | if (env->active_tc.gpr[4] == 0) { |
ths | b5dc773 | 2008-06-27 10:02:35 +0000 | [diff] [blame] | 239 | env->active_tc.gpr[2] = -1; |
Aleksandar Markovic | 14521a2 | 2019-10-23 12:23:35 +0200 | [diff] [blame] | 240 | } |
bellard | 6af0bf9 | 2005-07-02 14:58:51 +0000 | [diff] [blame] | 241 | /* Fall through */ |
| 242 | case 11: /* TODO: char inbyte (void); */ |
ths | b5dc773 | 2008-06-27 10:02:35 +0000 | [diff] [blame] | 243 | env->active_tc.gpr[2] = -1; |
bellard | 6af0bf9 | 2005-07-02 14:58:51 +0000 | [diff] [blame] | 244 | break; |
| 245 | case 3: |
| 246 | case 12: |
ths | b5dc773 | 2008-06-27 10:02:35 +0000 | [diff] [blame] | 247 | printf("%c", (char)(env->active_tc.gpr[4] & 0xFF)); |
bellard | 6af0bf9 | 2005-07-02 14:58:51 +0000 | [diff] [blame] | 248 | break; |
| 249 | case 17: |
| 250 | break; |
| 251 | case 158: |
| 252 | { |
Stefan Weil | b69e48a | 2012-04-12 15:43:09 +0200 | [diff] [blame] | 253 | unsigned char *fmt = (void *)(uintptr_t)env->active_tc.gpr[4]; |
bellard | 6af0bf9 | 2005-07-02 14:58:51 +0000 | [diff] [blame] | 254 | printf("%s", fmt); |
| 255 | } |
| 256 | break; |
| 257 | } |
| 258 | } |
bellard | e37e863 | 2005-07-04 22:17:33 +0000 | [diff] [blame] | 259 | |
ths | 5fafdf2 | 2007-09-16 21:08:06 +0000 | [diff] [blame] | 260 | #if !defined(CONFIG_USER_ONLY) |
bellard | e37e863 | 2005-07-04 22:17:33 +0000 | [diff] [blame] | 261 | |
Paolo Bonzini | 93e2232 | 2014-03-28 18:14:58 +0100 | [diff] [blame] | 262 | void mips_cpu_do_unaligned_access(CPUState *cs, vaddr addr, |
Sergey Sorokin | b35399b | 2016-06-14 15:26:17 +0300 | [diff] [blame] | 263 | MMUAccessType access_type, |
| 264 | int mmu_idx, uintptr_t retaddr) |
bellard | 4ad40f3 | 2005-12-05 19:59:36 +0000 | [diff] [blame] | 265 | { |
Paolo Bonzini | 93e2232 | 2014-03-28 18:14:58 +0100 | [diff] [blame] | 266 | MIPSCPU *cpu = MIPS_CPU(cs); |
| 267 | CPUMIPSState *env = &cpu->env; |
Leon Alrae | aea1409 | 2014-07-07 11:24:01 +0100 | [diff] [blame] | 268 | int error_code = 0; |
| 269 | int excp; |
Paolo Bonzini | 93e2232 | 2014-03-28 18:14:58 +0100 | [diff] [blame] | 270 | |
Yongbok Kim | e807bcc | 2018-08-02 16:15:55 +0200 | [diff] [blame] | 271 | if (!(env->hflags & MIPS_HFLAG_DM)) { |
| 272 | env->CP0_BadVAddr = addr; |
| 273 | } |
Leon Alrae | aea1409 | 2014-07-07 11:24:01 +0100 | [diff] [blame] | 274 | |
| 275 | if (access_type == MMU_DATA_STORE) { |
| 276 | excp = EXCP_AdES; |
| 277 | } else { |
| 278 | excp = EXCP_AdEL; |
| 279 | if (access_type == MMU_INST_FETCH) { |
| 280 | error_code |= EXCP_INST_NOTAVAIL; |
| 281 | } |
| 282 | } |
| 283 | |
| 284 | do_raise_exception_err(env, excp, error_code, retaddr); |
bellard | 4ad40f3 | 2005-12-05 19:59:36 +0000 | [diff] [blame] | 285 | } |
| 286 | |
Peter Maydell | 4f02a06 | 2019-08-02 17:04:57 +0100 | [diff] [blame] | 287 | void mips_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, |
| 288 | vaddr addr, unsigned size, |
| 289 | MMUAccessType access_type, |
| 290 | int mmu_idx, MemTxAttrs attrs, |
| 291 | MemTxResult response, uintptr_t retaddr) |
ths | 647de6c | 2007-10-20 19:45:44 +0000 | [diff] [blame] | 292 | { |
Andreas Färber | c658b94 | 2013-05-27 06:49:53 +0200 | [diff] [blame] | 293 | MIPSCPU *cpu = MIPS_CPU(cs); |
Richard Henderson | 3803b6b | 2021-02-27 12:44:00 -0800 | [diff] [blame] | 294 | MIPSCPUClass *mcc = MIPS_CPU_GET_CLASS(cpu); |
Andreas Färber | c658b94 | 2013-05-27 06:49:53 +0200 | [diff] [blame] | 295 | CPUMIPSState *env = &cpu->env; |
| 296 | |
Peter Maydell | 4f02a06 | 2019-08-02 17:04:57 +0100 | [diff] [blame] | 297 | if (access_type == MMU_INST_FETCH) { |
| 298 | do_raise_exception(env, EXCP_IBE, retaddr); |
Richard Henderson | 3803b6b | 2021-02-27 12:44:00 -0800 | [diff] [blame] | 299 | } else if (!mcc->no_data_aborts) { |
Peter Maydell | 4f02a06 | 2019-08-02 17:04:57 +0100 | [diff] [blame] | 300 | do_raise_exception(env, EXCP_DBE, retaddr); |
Andreas Färber | c658b94 | 2013-05-27 06:49:53 +0200 | [diff] [blame] | 301 | } |
ths | 647de6c | 2007-10-20 19:45:44 +0000 | [diff] [blame] | 302 | } |
ths | f1aa632 | 2008-06-09 07:13:38 +0000 | [diff] [blame] | 303 | #endif /* !CONFIG_USER_ONLY */ |