1 | /* |
2 | * Copyright (c) 2011, Max Filippov, Open Source and Linux Lab. |
3 | * All rights reserved. |
4 | * |
5 | * Redistribution and use in source and binary forms, with or without |
6 | * modification, are permitted provided that the following conditions are met: |
7 | * * Redistributions of source code must retain the above copyright |
8 | * notice, this list of conditions and the following disclaimer. |
9 | * * Redistributions in binary form must reproduce the above copyright |
10 | * notice, this list of conditions and the following disclaimer in the |
11 | * documentation and/or other materials provided with the distribution. |
12 | * * Neither the name of the Open Source and Linux Lab nor the |
13 | * names of its contributors may be used to endorse or promote products |
14 | * derived from this software without specific prior written permission. |
15 | * |
16 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
17 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
18 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
19 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
26 | */ |
27 | |
28 | #include "qemu/osdep.h" |
29 | #include "qemu/main-loop.h" |
30 | #include "cpu.h" |
31 | #include "exec/helper-proto.h" |
32 | #include "qemu/host-utils.h" |
33 | #include "exec/exec-all.h" |
34 | #include "exec/cpu_ldst.h" |
35 | #include "exec/address-spaces.h" |
36 | #include "qemu/timer.h" |
37 | |
38 | #ifndef CONFIG_USER_ONLY |
39 | |
40 | void HELPER(update_ccount)(CPUXtensaState *env) |
41 | { |
42 | uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); |
43 | |
44 | env->ccount_time = now; |
45 | env->sregs[CCOUNT] = env->ccount_base + |
46 | (uint32_t)((now - env->time_base) * |
47 | env->config->clock_freq_khz / 1000000); |
48 | } |
49 | |
50 | void HELPER(wsr_ccount)(CPUXtensaState *env, uint32_t v) |
51 | { |
52 | int i; |
53 | |
54 | HELPER(update_ccount)(env); |
55 | env->ccount_base += v - env->sregs[CCOUNT]; |
56 | for (i = 0; i < env->config->nccompare; ++i) { |
57 | HELPER(update_ccompare)(env, i); |
58 | } |
59 | } |
60 | |
61 | void HELPER(update_ccompare)(CPUXtensaState *env, uint32_t i) |
62 | { |
63 | uint64_t dcc; |
64 | |
65 | atomic_and(&env->sregs[INTSET], |
66 | ~(1u << env->config->timerint[i])); |
67 | HELPER(update_ccount)(env); |
68 | dcc = (uint64_t)(env->sregs[CCOMPARE + i] - env->sregs[CCOUNT] - 1) + 1; |
69 | timer_mod(env->ccompare[i].timer, |
70 | env->ccount_time + (dcc * 1000000) / env->config->clock_freq_khz); |
71 | env->yield_needed = 1; |
72 | } |
73 | |
74 | /*! |
75 | * Check vaddr accessibility/cache attributes and raise an exception if |
76 | * specified by the ATOMCTL SR. |
77 | * |
78 | * Note: local memory exclusion is not implemented |
79 | */ |
80 | void HELPER(check_atomctl)(CPUXtensaState *env, uint32_t pc, uint32_t vaddr) |
81 | { |
82 | uint32_t paddr, page_size, access; |
83 | uint32_t atomctl = env->sregs[ATOMCTL]; |
84 | int rc = xtensa_get_physical_addr(env, true, vaddr, 1, |
85 | xtensa_get_cring(env), &paddr, &page_size, &access); |
86 | |
87 | /* |
88 | * s32c1i never causes LOAD_PROHIBITED_CAUSE exceptions, |
89 | * see opcode description in the ISA |
90 | */ |
91 | if (rc == 0 && |
92 | (access & (PAGE_READ | PAGE_WRITE)) != (PAGE_READ | PAGE_WRITE)) { |
93 | rc = STORE_PROHIBITED_CAUSE; |
94 | } |
95 | |
96 | if (rc) { |
97 | HELPER(exception_cause_vaddr)(env, pc, rc, vaddr); |
98 | } |
99 | |
100 | /* |
101 | * When data cache is not configured use ATOMCTL bypass field. |
102 | * See ISA, 4.3.12.4 The Atomic Operation Control Register (ATOMCTL) |
103 | * under the Conditional Store Option. |
104 | */ |
105 | if (!xtensa_option_enabled(env->config, XTENSA_OPTION_DCACHE)) { |
106 | access = PAGE_CACHE_BYPASS; |
107 | } |
108 | |
109 | switch (access & PAGE_CACHE_MASK) { |
110 | case PAGE_CACHE_WB: |
111 | atomctl >>= 2; |
112 | /* fall through */ |
113 | case PAGE_CACHE_WT: |
114 | atomctl >>= 2; |
115 | /* fall through */ |
116 | case PAGE_CACHE_BYPASS: |
117 | if ((atomctl & 0x3) == 0) { |
118 | HELPER(exception_cause_vaddr)(env, pc, |
119 | LOAD_STORE_ERROR_CAUSE, vaddr); |
120 | } |
121 | break; |
122 | |
123 | case PAGE_CACHE_ISOLATE: |
124 | HELPER(exception_cause_vaddr)(env, pc, |
125 | LOAD_STORE_ERROR_CAUSE, vaddr); |
126 | break; |
127 | |
128 | default: |
129 | break; |
130 | } |
131 | } |
132 | |
133 | void HELPER(check_exclusive)(CPUXtensaState *env, uint32_t pc, uint32_t vaddr, |
134 | uint32_t is_write) |
135 | { |
136 | uint32_t paddr, page_size, access; |
137 | uint32_t atomctl = env->sregs[ATOMCTL]; |
138 | int rc = xtensa_get_physical_addr(env, true, vaddr, is_write, |
139 | xtensa_get_cring(env), &paddr, |
140 | &page_size, &access); |
141 | |
142 | if (rc) { |
143 | HELPER(exception_cause_vaddr)(env, pc, rc, vaddr); |
144 | } |
145 | |
146 | /* When data cache is not configured use ATOMCTL bypass field. */ |
147 | if (!xtensa_option_enabled(env->config, XTENSA_OPTION_DCACHE)) { |
148 | access = PAGE_CACHE_BYPASS; |
149 | } |
150 | |
151 | switch (access & PAGE_CACHE_MASK) { |
152 | case PAGE_CACHE_WB: |
153 | atomctl >>= 2; |
154 | /* fall through */ |
155 | case PAGE_CACHE_WT: |
156 | atomctl >>= 2; |
157 | /* fall through */ |
158 | case PAGE_CACHE_BYPASS: |
159 | if ((atomctl & 0x3) == 0) { |
160 | HELPER(exception_cause_vaddr)(env, pc, |
161 | EXCLUSIVE_ERROR_CAUSE, vaddr); |
162 | } |
163 | break; |
164 | |
165 | case PAGE_CACHE_ISOLATE: |
166 | HELPER(exception_cause_vaddr)(env, pc, |
167 | LOAD_STORE_ERROR_CAUSE, vaddr); |
168 | break; |
169 | |
170 | default: |
171 | break; |
172 | } |
173 | } |
174 | |
175 | void HELPER(wsr_memctl)(CPUXtensaState *env, uint32_t v) |
176 | { |
177 | if (xtensa_option_enabled(env->config, XTENSA_OPTION_ICACHE)) { |
178 | if (extract32(v, MEMCTL_IUSEWAYS_SHIFT, MEMCTL_IUSEWAYS_LEN) > |
179 | env->config->icache_ways) { |
180 | deposit32(v, MEMCTL_IUSEWAYS_SHIFT, MEMCTL_IUSEWAYS_LEN, |
181 | env->config->icache_ways); |
182 | } |
183 | } |
184 | if (xtensa_option_enabled(env->config, XTENSA_OPTION_DCACHE)) { |
185 | if (extract32(v, MEMCTL_DUSEWAYS_SHIFT, MEMCTL_DUSEWAYS_LEN) > |
186 | env->config->dcache_ways) { |
187 | deposit32(v, MEMCTL_DUSEWAYS_SHIFT, MEMCTL_DUSEWAYS_LEN, |
188 | env->config->dcache_ways); |
189 | } |
190 | if (extract32(v, MEMCTL_DALLOCWAYS_SHIFT, MEMCTL_DALLOCWAYS_LEN) > |
191 | env->config->dcache_ways) { |
192 | deposit32(v, MEMCTL_DALLOCWAYS_SHIFT, MEMCTL_DALLOCWAYS_LEN, |
193 | env->config->dcache_ways); |
194 | } |
195 | } |
196 | env->sregs[MEMCTL] = v & env->config->memctl_mask; |
197 | } |
198 | |
199 | #endif |
200 | |
201 | uint32_t HELPER(rer)(CPUXtensaState *env, uint32_t addr) |
202 | { |
203 | #ifndef CONFIG_USER_ONLY |
204 | return address_space_ldl(env->address_space_er, addr, |
205 | MEMTXATTRS_UNSPECIFIED, NULL); |
206 | #else |
207 | return 0; |
208 | #endif |
209 | } |
210 | |
211 | void HELPER(wer)(CPUXtensaState *env, uint32_t data, uint32_t addr) |
212 | { |
213 | #ifndef CONFIG_USER_ONLY |
214 | address_space_stl(env->address_space_er, addr, data, |
215 | MEMTXATTRS_UNSPECIFIED, NULL); |
216 | #endif |
217 | } |
218 | |