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