xref: /openbmc/qemu/target/xtensa/op_helper.c (revision 42fe7499)
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 "cpu.h"
30 #include "exec/helper-proto.h"
31 #include "qemu/host-utils.h"
32 #include "exec/exec-all.h"
33 #include "qemu/atomic.h"
34 #include "qemu/timer.h"
35 
36 #ifndef CONFIG_USER_ONLY
37 
38 void HELPER(update_ccount)(CPUXtensaState *env)
39 {
40     XtensaCPU *cpu = XTENSA_CPU(env_cpu(env));
41     uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
42 
43     env->ccount_time = now;
44     env->sregs[CCOUNT] = env->ccount_base +
45         (uint32_t)clock_ns_to_ticks(cpu->clock, now - env->time_base);
46 }
47 
48 void HELPER(wsr_ccount)(CPUXtensaState *env, uint32_t v)
49 {
50     int i;
51 
52     HELPER(update_ccount)(env);
53     env->ccount_base += v - env->sregs[CCOUNT];
54     for (i = 0; i < env->config->nccompare; ++i) {
55         HELPER(update_ccompare)(env, i);
56     }
57 }
58 
59 void HELPER(update_ccompare)(CPUXtensaState *env, uint32_t i)
60 {
61     XtensaCPU *cpu = XTENSA_CPU(env_cpu(env));
62     uint64_t dcc;
63 
64     qatomic_and(&env->sregs[INTSET],
65                ~(1u << env->config->timerint[i]));
66     HELPER(update_ccount)(env);
67     dcc = (uint64_t)(env->sregs[CCOMPARE + i] - env->sregs[CCOUNT] - 1) + 1;
68     timer_mod(env->ccompare[i].timer,
69               env->ccount_time + clock_ticks_to_ns(cpu->clock, dcc));
70     env->yield_needed = 1;
71 }
72 
73 /*!
74  * Check vaddr accessibility/cache attributes and raise an exception if
75  * specified by the ATOMCTL SR.
76  *
77  * Note: local memory exclusion is not implemented
78  */
79 void HELPER(check_atomctl)(CPUXtensaState *env, uint32_t pc, uint32_t vaddr)
80 {
81     uint32_t paddr, page_size, access;
82     uint32_t atomctl = env->sregs[ATOMCTL];
83     int rc = xtensa_get_physical_addr(env, true, vaddr, 1,
84             xtensa_get_cring(env), &paddr, &page_size, &access);
85 
86     /*
87      * s32c1i never causes LOAD_PROHIBITED_CAUSE exceptions,
88      * see opcode description in the ISA
89      */
90     if (rc == 0 &&
91             (access & (PAGE_READ | PAGE_WRITE)) != (PAGE_READ | PAGE_WRITE)) {
92         rc = STORE_PROHIBITED_CAUSE;
93     }
94 
95     if (rc) {
96         HELPER(exception_cause_vaddr)(env, pc, rc, vaddr);
97     }
98 
99     /*
100      * When data cache is not configured use ATOMCTL bypass field.
101      * See ISA, 4.3.12.4 The Atomic Operation Control Register (ATOMCTL)
102      * under the Conditional Store Option.
103      */
104     if (!xtensa_option_enabled(env->config, XTENSA_OPTION_DCACHE)) {
105         access = PAGE_CACHE_BYPASS;
106     }
107 
108     switch (access & PAGE_CACHE_MASK) {
109     case PAGE_CACHE_WB:
110         atomctl >>= 2;
111         /* fall through */
112     case PAGE_CACHE_WT:
113         atomctl >>= 2;
114         /* fall through */
115     case PAGE_CACHE_BYPASS:
116         if ((atomctl & 0x3) == 0) {
117             HELPER(exception_cause_vaddr)(env, pc,
118                     LOAD_STORE_ERROR_CAUSE, vaddr);
119         }
120         break;
121 
122     case PAGE_CACHE_ISOLATE:
123         HELPER(exception_cause_vaddr)(env, pc,
124                 LOAD_STORE_ERROR_CAUSE, vaddr);
125         break;
126 
127     default:
128         break;
129     }
130 }
131 
132 void HELPER(check_exclusive)(CPUXtensaState *env, uint32_t pc, uint32_t vaddr,
133                              uint32_t is_write)
134 {
135     uint32_t paddr, page_size, access;
136     uint32_t atomctl = env->sregs[ATOMCTL];
137     int rc = xtensa_get_physical_addr(env, true, vaddr, is_write,
138                                       xtensa_get_cring(env), &paddr,
139                                       &page_size, &access);
140 
141     if (rc) {
142         HELPER(exception_cause_vaddr)(env, pc, rc, vaddr);
143     }
144 
145     /* When data cache is not configured use ATOMCTL bypass field. */
146     if (!xtensa_option_enabled(env->config, XTENSA_OPTION_DCACHE)) {
147         access = PAGE_CACHE_BYPASS;
148     }
149 
150     switch (access & PAGE_CACHE_MASK) {
151     case PAGE_CACHE_WB:
152         atomctl >>= 2;
153         /* fall through */
154     case PAGE_CACHE_WT:
155         atomctl >>= 2;
156         /* fall through */
157     case PAGE_CACHE_BYPASS:
158         if ((atomctl & 0x3) == 0) {
159             HELPER(exception_cause_vaddr)(env, pc,
160                                           EXCLUSIVE_ERROR_CAUSE, vaddr);
161         }
162         break;
163 
164     case PAGE_CACHE_ISOLATE:
165         HELPER(exception_cause_vaddr)(env, pc,
166                 LOAD_STORE_ERROR_CAUSE, vaddr);
167         break;
168 
169     default:
170         break;
171     }
172 }
173 
174 void HELPER(wsr_memctl)(CPUXtensaState *env, uint32_t v)
175 {
176     if (xtensa_option_enabled(env->config, XTENSA_OPTION_ICACHE)) {
177         if (extract32(v, MEMCTL_IUSEWAYS_SHIFT, MEMCTL_IUSEWAYS_LEN) >
178             env->config->icache_ways) {
179             deposit32(v, MEMCTL_IUSEWAYS_SHIFT, MEMCTL_IUSEWAYS_LEN,
180                       env->config->icache_ways);
181         }
182     }
183     if (xtensa_option_enabled(env->config, XTENSA_OPTION_DCACHE)) {
184         if (extract32(v, MEMCTL_DUSEWAYS_SHIFT, MEMCTL_DUSEWAYS_LEN) >
185             env->config->dcache_ways) {
186             deposit32(v, MEMCTL_DUSEWAYS_SHIFT, MEMCTL_DUSEWAYS_LEN,
187                       env->config->dcache_ways);
188         }
189         if (extract32(v, MEMCTL_DALLOCWAYS_SHIFT, MEMCTL_DALLOCWAYS_LEN) >
190             env->config->dcache_ways) {
191             deposit32(v, MEMCTL_DALLOCWAYS_SHIFT, MEMCTL_DALLOCWAYS_LEN,
192                       env->config->dcache_ways);
193         }
194     }
195     env->sregs[MEMCTL] = v & env->config->memctl_mask;
196 }
197 
198 #endif
199 
200 uint32_t HELPER(rer)(CPUXtensaState *env, uint32_t addr)
201 {
202 #ifndef CONFIG_USER_ONLY
203     return address_space_ldl(env->address_space_er, addr,
204                              MEMTXATTRS_UNSPECIFIED, NULL);
205 #else
206     return 0;
207 #endif
208 }
209 
210 void HELPER(wer)(CPUXtensaState *env, uint32_t data, uint32_t addr)
211 {
212 #ifndef CONFIG_USER_ONLY
213     address_space_stl(env->address_space_er, addr, data,
214                       MEMTXATTRS_UNSPECIFIED, NULL);
215 #endif
216 }
217