These changes are the raw update to qemu-2.6.
[kvmfornfv.git] / qemu / target-openrisc / mmu.c
1 /*
2  * OpenRISC MMU.
3  *
4  * Copyright (c) 2011-2012 Jia Liu <proljc@gmail.com>
5  *                         Zhizhou Zhang <etouzh@gmail.com>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include "qemu/osdep.h"
22 #include "cpu.h"
23 #include "qemu-common.h"
24 #include "exec/gdbstub.h"
25 #include "qemu/host-utils.h"
26 #ifndef CONFIG_USER_ONLY
27 #include "hw/loader.h"
28 #endif
29
30 #ifndef CONFIG_USER_ONLY
31 int cpu_openrisc_get_phys_nommu(OpenRISCCPU *cpu,
32                                 hwaddr *physical,
33                                 int *prot, target_ulong address, int rw)
34 {
35     *physical = address;
36     *prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC;
37     return TLBRET_MATCH;
38 }
39
40 int cpu_openrisc_get_phys_code(OpenRISCCPU *cpu,
41                                hwaddr *physical,
42                                int *prot, target_ulong address, int rw)
43 {
44     int vpn = address >> TARGET_PAGE_BITS;
45     int idx = vpn & ITLB_MASK;
46     int right = 0;
47
48     if ((cpu->env.tlb->itlb[0][idx].mr >> TARGET_PAGE_BITS) != vpn) {
49         return TLBRET_NOMATCH;
50     }
51     if (!(cpu->env.tlb->itlb[0][idx].mr & 1)) {
52         return TLBRET_INVALID;
53     }
54
55     if (cpu->env.sr & SR_SM) { /* supervisor mode */
56         if (cpu->env.tlb->itlb[0][idx].tr & SXE) {
57             right |= PAGE_EXEC;
58         }
59     } else {
60         if (cpu->env.tlb->itlb[0][idx].tr & UXE) {
61             right |= PAGE_EXEC;
62         }
63     }
64
65     if ((rw & 2) && ((right & PAGE_EXEC) == 0)) {
66         return TLBRET_BADADDR;
67     }
68
69     *physical = (cpu->env.tlb->itlb[0][idx].tr & TARGET_PAGE_MASK) |
70                 (address & (TARGET_PAGE_SIZE-1));
71     *prot = right;
72     return TLBRET_MATCH;
73 }
74
75 int cpu_openrisc_get_phys_data(OpenRISCCPU *cpu,
76                                hwaddr *physical,
77                                int *prot, target_ulong address, int rw)
78 {
79     int vpn = address >> TARGET_PAGE_BITS;
80     int idx = vpn & DTLB_MASK;
81     int right = 0;
82
83     if ((cpu->env.tlb->dtlb[0][idx].mr >> TARGET_PAGE_BITS) != vpn) {
84         return TLBRET_NOMATCH;
85     }
86     if (!(cpu->env.tlb->dtlb[0][idx].mr & 1)) {
87         return TLBRET_INVALID;
88     }
89
90     if (cpu->env.sr & SR_SM) { /* supervisor mode */
91         if (cpu->env.tlb->dtlb[0][idx].tr & SRE) {
92             right |= PAGE_READ;
93         }
94         if (cpu->env.tlb->dtlb[0][idx].tr & SWE) {
95             right |= PAGE_WRITE;
96         }
97     } else {
98         if (cpu->env.tlb->dtlb[0][idx].tr & URE) {
99             right |= PAGE_READ;
100         }
101         if (cpu->env.tlb->dtlb[0][idx].tr & UWE) {
102             right |= PAGE_WRITE;
103         }
104     }
105
106     if (!(rw & 1) && ((right & PAGE_READ) == 0)) {
107         return TLBRET_BADADDR;
108     }
109     if ((rw & 1) && ((right & PAGE_WRITE) == 0)) {
110         return TLBRET_BADADDR;
111     }
112
113     *physical = (cpu->env.tlb->dtlb[0][idx].tr & TARGET_PAGE_MASK) |
114                 (address & (TARGET_PAGE_SIZE-1));
115     *prot = right;
116     return TLBRET_MATCH;
117 }
118
119 static int cpu_openrisc_get_phys_addr(OpenRISCCPU *cpu,
120                                       hwaddr *physical,
121                                       int *prot, target_ulong address,
122                                       int rw)
123 {
124     int ret = TLBRET_MATCH;
125
126     if (rw == 2) {    /* ITLB */
127        *physical = 0;
128         ret = cpu->env.tlb->cpu_openrisc_map_address_code(cpu, physical,
129                                                           prot, address, rw);
130     } else {          /* DTLB */
131         ret = cpu->env.tlb->cpu_openrisc_map_address_data(cpu, physical,
132                                                           prot, address, rw);
133     }
134
135     return ret;
136 }
137 #endif
138
139 static void cpu_openrisc_raise_mmu_exception(OpenRISCCPU *cpu,
140                                              target_ulong address,
141                                              int rw, int tlb_error)
142 {
143     CPUState *cs = CPU(cpu);
144     int exception = 0;
145
146     switch (tlb_error) {
147     default:
148         if (rw == 2) {
149             exception = EXCP_IPF;
150         } else {
151             exception = EXCP_DPF;
152         }
153         break;
154 #ifndef CONFIG_USER_ONLY
155     case TLBRET_BADADDR:
156         if (rw == 2) {
157             exception = EXCP_IPF;
158         } else {
159             exception = EXCP_DPF;
160         }
161         break;
162     case TLBRET_INVALID:
163     case TLBRET_NOMATCH:
164         /* No TLB match for a mapped address */
165         if (rw == 2) {
166             exception = EXCP_ITLBMISS;
167         } else {
168             exception = EXCP_DTLBMISS;
169         }
170         break;
171 #endif
172     }
173
174     cs->exception_index = exception;
175     cpu->env.eear = address;
176 }
177
178 #ifndef CONFIG_USER_ONLY
179 int openrisc_cpu_handle_mmu_fault(CPUState *cs,
180                                   vaddr address, int rw, int mmu_idx)
181 {
182     OpenRISCCPU *cpu = OPENRISC_CPU(cs);
183     int ret = 0;
184     hwaddr physical = 0;
185     int prot = 0;
186
187     ret = cpu_openrisc_get_phys_addr(cpu, &physical, &prot,
188                                      address, rw);
189
190     if (ret == TLBRET_MATCH) {
191         tlb_set_page(cs, address & TARGET_PAGE_MASK,
192                      physical & TARGET_PAGE_MASK, prot,
193                      mmu_idx, TARGET_PAGE_SIZE);
194         ret = 0;
195     } else if (ret < 0) {
196         cpu_openrisc_raise_mmu_exception(cpu, address, rw, ret);
197         ret = 1;
198     }
199
200     return ret;
201 }
202 #else
203 int openrisc_cpu_handle_mmu_fault(CPUState *cs,
204                                   vaddr address, int rw, int mmu_idx)
205 {
206     OpenRISCCPU *cpu = OPENRISC_CPU(cs);
207     int ret = 0;
208
209     cpu_openrisc_raise_mmu_exception(cpu, address, rw, ret);
210     ret = 1;
211
212     return ret;
213 }
214 #endif
215
216 #ifndef CONFIG_USER_ONLY
217 hwaddr openrisc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr)
218 {
219     OpenRISCCPU *cpu = OPENRISC_CPU(cs);
220     hwaddr phys_addr;
221     int prot;
222
223     if (cpu_openrisc_get_phys_addr(cpu, &phys_addr, &prot, addr, 0)) {
224         return -1;
225     }
226
227     return phys_addr;
228 }
229
230 void cpu_openrisc_mmu_init(OpenRISCCPU *cpu)
231 {
232     cpu->env.tlb = g_malloc0(sizeof(CPUOpenRISCTLBContext));
233
234     cpu->env.tlb->cpu_openrisc_map_address_code = &cpu_openrisc_get_phys_nommu;
235     cpu->env.tlb->cpu_openrisc_map_address_data = &cpu_openrisc_get_phys_nommu;
236 }
237 #endif