memory_sh.cc Source File

Back to the index.

memory_sh.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2006-2009 Anders Gavare. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * 1. Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * 2. 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  * 3. The name of the author may not be used to endorse or promote products
13  * derived from this software without specific prior written permission.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 
32 #include "cpu.h"
33 #include "machine.h"
34 #include "memory.h"
35 #include "misc.h"
36 
38 #include "thirdparty/sh4_mmu.h"
39 
40 
41 /*
42  * translate_via_mmu():
43  *
44  * Scan the UTLB for a matching virtual address. If a match was found, then
45  * check permission bits etc. If everything was ok, then return the physical
46  * page address, otherwise cause an exception.
47  *
48  * The implementation should (hopefully) be quite complete, except for lack
49  * of "Multiple matching entries" detection. (On a real CPU, these would
50  * cause exceptions.)
51  *
52  * Same return values as sh_translate_v2p().
53  */
54 static int translate_via_mmu(struct cpu *cpu, uint32_t vaddr,
55  uint64_t *return_paddr, int flags)
56 {
57  int wf = flags & FLAG_WRITEFLAG;
58  int i, urb, urc, require_asid_match, cur_asid, expevt = 0;
59  uint32_t hi, lo = 0, mask = 0;
60  int sh; /* Shared */
61  int d; /* Dirty bit */
62  int v; /* Valid bit */
63  int pr; /* Protection */
64  int i_start;
65 
66  cur_asid = cpu->cd.sh.pteh & SH4_PTEH_ASID_MASK;
67  require_asid_match = !(cpu->cd.sh.mmucr & SH4_MMUCR_SV)
68  || !(cpu->cd.sh.sr & SH_SR_MD);
69 
70  if (!(flags & FLAG_NOEXCEPTIONS)) {
71  /*
72  * Increase URC every time the UTLB is accessed. (Note:
73  * According to the SH4 manual, the URC should not be
74  * increased when running the ldtlb instruction. Perhaps this
75  * is a good place? Perhaps it is better to just set it to a
76  * random value? TODO: Find out.
77  */
78  urb = (cpu->cd.sh.mmucr & SH4_MMUCR_URB_MASK) >>
80  urc = (cpu->cd.sh.mmucr & SH4_MMUCR_URC_MASK) >>
82 
83  /* fatal("urc = %i ==> ", urc); */
84  urc ++;
85  if (urc >= SH_N_UTLB_ENTRIES || (urb > 0 && urc == urb))
86  urc = 0;
87  /* fatal("%i\n", urc); */
88 
89  cpu->cd.sh.mmucr &= ~SH4_MMUCR_URC_MASK;
90  cpu->cd.sh.mmucr |= (urc << SH4_MMUCR_URC_SHIFT);
91  }
92 
93  /*
94  * When doing Instruction lookups, the ITLB should be scanned first.
95  * This is done by using negative i. (Ugly hack, but works.)
96  */
97  if (flags & FLAG_INSTR)
98  i_start = -SH_N_ITLB_ENTRIES;
99  else
100  i_start = 0;
101 
102  for (i=i_start; i<SH_N_UTLB_ENTRIES; i++) {
103  if (i<0) {
104  hi = cpu->cd.sh.itlb_hi[i + SH_N_ITLB_ENTRIES];
105  lo = cpu->cd.sh.itlb_lo[i + SH_N_ITLB_ENTRIES];
106  } else {
107  hi = cpu->cd.sh.utlb_hi[i];
108  lo = cpu->cd.sh.utlb_lo[i];
109  }
110  mask = 0xfff00000;
111 
112  v = lo & SH4_PTEL_V;
113  if (!v)
114  continue;
115 
116  switch (lo & SH4_PTEL_SZ_MASK) {
117  case SH4_PTEL_SZ_1K: mask = 0xfffffc00; break;
118  case SH4_PTEL_SZ_4K: mask = 0xfffff000; break;
119  case SH4_PTEL_SZ_64K: mask = 0xffff0000; break;
120  /* case SH4_PTEL_SZ_1M: mask = 0xfff00000; break; */
121  }
122 
123  if ((hi & mask) != (vaddr & mask))
124  continue;
125 
126  sh = lo & SH4_PTEL_SH;
127 
128  if (!sh && require_asid_match) {
129  int asid = hi & SH4_PTEH_ASID_MASK;
130  if (asid != cur_asid)
131  continue;
132  }
133 
134  /* Note/TODO: Check for multiple matches is not implemented. */
135 
136  break;
137  }
138 
139  /* Virtual address not found? Then it's a TLB miss. */
140  if (i == SH_N_UTLB_ENTRIES)
141  goto tlb_miss;
142 
143  /* Matching address found! Let's see whether it is
144  readable/writable, etc.: */
145  d = lo & SH4_PTEL_D? 1 : 0;
146  pr = (lo & SH4_PTEL_PR_MASK) >> SH4_PTEL_PR_SHIFT;
147 
148  *return_paddr = (vaddr & ~mask) | (lo & mask & 0x1fffffff);
149 
150  if (flags & FLAG_INSTR) {
151  /*
152  * Instruction access:
153  */
154 #if 0
155  /* NOTE: Emulating the ITLB as exact as this is not
156  necessary... so I'm disabling it for now. */
157  /*
158  * If a matching entry wasn't found in the ITLB, but in the
159  * UTLB, then copy it to a random place in the ITLB.
160  */
161  if (i >= 0 && !(flags & FLAG_NOEXCEPTIONS)) {
162  int r = random() % SH_N_ITLB_ENTRIES;
163 
164  /* NOTE: Make sure that the old mapping for
165  that itlb entry is invalidated: */
167  cpu->cd.sh.itlb_hi[r] & ~0xfff, INVALIDATE_VADDR);
168 
170  cpu->cd.sh.utlb_lo[i] & ~0xfff, INVALIDATE_PADDR);
171 
172  cpu->cd.sh.itlb_hi[r] = cpu->cd.sh.utlb_hi[i];
173  cpu->cd.sh.itlb_lo[r] = cpu->cd.sh.utlb_lo[i];
174  }
175 #endif
176 
177  /* Permission checks: */
178  if (cpu->cd.sh.sr & SH_SR_MD)
179  return 1;
180  if (!(pr & 2))
181  goto protection_violation;
182 
183  return 1;
184  }
185 
186  /* Data access: */
187  if (cpu->cd.sh.sr & SH_SR_MD) {
188  /* Kernel access: */
189  switch (pr) {
190  case 0:
191  case 2: if (wf)
192  goto protection_violation;
193  return 1;
194  case 1:
195  case 3: if (wf && !d)
196  goto initial_write_exception;
197  return 1 + d;
198  }
199  }
200 
201  /* User access */
202  switch (pr) {
203  case 0:
204  case 1: goto protection_violation;
205  case 2: if (wf)
206  goto protection_violation;
207  return 1;
208  case 3: if (wf && !d)
209  goto initial_write_exception;
210  return 1 + d;
211  }
212 
213 
214 tlb_miss:
215  expevt = wf? EXPEVT_TLB_MISS_ST : EXPEVT_TLB_MISS_LD;
216  goto exception;
217 
218 protection_violation:
219  expevt = wf? EXPEVT_TLB_PROT_ST : EXPEVT_TLB_PROT_LD;
220  goto exception;
221 
222 initial_write_exception:
223  expevt = EXPEVT_TLB_MOD;
224 
225 
226 exception:
227  if (flags & FLAG_NOEXCEPTIONS) {
228  *return_paddr = 0;
229  return 2;
230  }
231 
232  sh_exception(cpu, expevt, 0, vaddr);
233 
234  return 0;
235 }
236 
237 
238 /*
239  * sh_translate_v2p():
240  *
241  * Return values:
242  *
243  * 0 No access to the virtual address.
244  * 1 return_paddr contains the physical address, the page is
245  * available as read-only.
246  * 2 Same as 1, but the page is available as read/write.
247  */
248 int sh_translate_v2p(struct cpu *cpu, uint64_t vaddr64, uint64_t *return_paddr,
249  int flags)
250 {
251  int user = cpu->cd.sh.sr & SH_SR_MD? 0 : 1;
252  uint32_t vaddr = vaddr64;
253 
254  /* U0/P0: Userspace addresses, or P3: Kernel virtual memory. */
255  if (!(vaddr & 0x80000000) ||
256  (vaddr >= 0xc0000000 && vaddr < 0xe0000000)) {
257  /* Address translation turned off? */
258  if (!(cpu->cd.sh.mmucr & SH4_MMUCR_AT)) {
259  /* Then return raw physical address: */
260  *return_paddr = vaddr & 0x1fffffff;
261  return 2;
262  }
263 
264  /* Perform translation via the MMU: */
265  return translate_via_mmu(cpu, vaddr, return_paddr, flags);
266  }
267 
268  /* Store queue region: */
269  if (vaddr >= 0xe0000000 && vaddr < 0xe4000000) {
270  /* Note/TODO: Take SH4_MMUCR_SQMD into account. */
271  *return_paddr = vaddr;
272  return 2;
273  }
274 
275  if (user) {
276  if (flags & FLAG_NOEXCEPTIONS) {
277  *return_paddr = 0;
278  return 2;
279  }
280 
281  // Hopefully correct behavior; nbjoerg on #GXemul (FreeNode)
282  // noticed that this case was easily triggered inside a
283  // guest OS, by: int main(void){ *(int *)-4 = 0; }
284  sh_exception(cpu, (flags & FLAG_WRITEFLAG)?
286  return 0;
287  }
288 
289  /* P1,P2: Direct-mapped physical memory. */
290  if (vaddr >= 0x80000000 && vaddr < 0xc0000000) {
291  *return_paddr = vaddr & 0x1fffffff;
292  return 2;
293  }
294 
295  if (flags & FLAG_INSTR) {
296  fatal("TODO: instr at 0x%08" PRIx32"\n", (uint32_t)vaddr);
297  exit(1);
298  }
299 
300  /* P4: Special registers mapped at 0xf0000000 .. 0xffffffff: */
301  if ((vaddr & 0xf0000000) == 0xf0000000) {
302  *return_paddr = vaddr;
303  return 2;
304  }
305 
306  if (flags & FLAG_NOEXCEPTIONS) {
307  *return_paddr = 0;
308  return 2;
309  }
310 
311  /* TODO */
312 
313  /* The ugly 'if' is just here to fool Compaq CC. */
314  if (!(flags & FLAG_NOEXCEPTIONS)) {
315  fatal("Unimplemented SH vaddr 0x%08" PRIx32"\n", vaddr);
316  exit(1);
317  }
318 
319  return 0;
320 }
321 
#define SH4_MMUCR_URB_MASK
Definition: sh4_mmu.h:76
#define EXPEVT_TLB_MISS_LD
Definition: sh4_exception.h:62
void fatal(const char *fmt,...)
Definition: main.cc:152
#define SH4_PTEL_D
Definition: sh4_mmu.h:51
#define INVALIDATE_VADDR
Definition: cpu.h:480
#define SH_N_UTLB_ENTRIES
Definition: cpu_sh.h:89
#define FLAG_NOEXCEPTIONS
Definition: memory.h:137
uint32_t sr
Definition: cpu_sh.h:112
#define SH4_MMUCR_URC_MASK
Definition: sh4_mmu.h:74
#define SH_SR_MD
Definition: cpu_sh.h:194
union cpu::@1 cd
#define SH4_PTEL_SZ_MASK
Definition: sh4_mmu.h:55
#define EXPEVT_TLB_MOD
Definition: sh4_exception.h:64
void sh_exception(struct cpu *cpu, int expevt, int intevt, uint32_t vaddr)
Definition: cpu_sh.cc:632
#define SH4_MMUCR_AT
Definition: sh4_mmu.h:69
#define SH4_MMUCR_URB_SHIFT
Definition: sh4_mmu.h:75
uint32_t mmucr
Definition: cpu_sh.h:131
#define SH4_PTEH_ASID_MASK
Definition: sh4_mmu.h:47
#define SH4_PTEL_V
Definition: sh4_mmu.h:60
#define SH4_PTEL_SZ_4K
Definition: sh4_mmu.h:57
#define SH_N_ITLB_ENTRIES
Definition: cpu_sh.h:88
#define FLAG_INSTR
Definition: memory.h:138
uint32_t itlb_lo[SH_N_ITLB_ENTRIES]
Definition: cpu_sh.h:133
uint32_t utlb_hi[SH_N_UTLB_ENTRIES]
Definition: cpu_sh.h:134
#define SH4_PTEL_PR_SHIFT
Definition: sh4_mmu.h:53
uint32_t pteh
Definition: cpu_sh.h:126
#define SH4_MMUCR_URC_SHIFT
Definition: sh4_mmu.h:73
#define SH4_PTEL_SH
Definition: sh4_mmu.h:50
#define EXPEVT_TLB_MISS_ST
Definition: sh4_exception.h:63
int sh_translate_v2p(struct cpu *cpu, uint64_t vaddr64, uint64_t *return_paddr, int flags)
Definition: memory_sh.cc:248
Definition: cpu.h:326
#define SH4_PTEL_PR_MASK
Definition: sh4_mmu.h:54
#define INVALIDATE_PADDR
Definition: cpu.h:479
#define EXPEVT_TLB_PROT_LD
Definition: sh4_exception.h:65
#define FLAG_WRITEFLAG
Definition: memory.h:136
#define SH4_MMUCR_SV
Definition: sh4_mmu.h:71
uint32_t itlb_hi[SH_N_ITLB_ENTRIES]
Definition: cpu_sh.h:132
#define SH4_PTEL_SZ_64K
Definition: sh4_mmu.h:58
void(* invalidate_code_translation)(struct cpu *, uint64_t paddr, int flags)
Definition: cpu.h:376
uint32_t utlb_lo[SH_N_UTLB_ENTRIES]
Definition: cpu_sh.h:135
struct sh_cpu sh
Definition: cpu.h:445
void(* invalidate_translation_caches)(struct cpu *, uint64_t paddr, int flags)
Definition: cpu.h:374
#define SH4_PTEL_SZ_1K
Definition: sh4_mmu.h:56
#define EXPEVT_TLB_PROT_ST
Definition: sh4_exception.h:66

Generated on Sun Sep 30 2018 16:05:18 for GXemul by doxygen 1.8.13