1 |
dpavlin |
7 |
/* |
2 |
|
|
* Cisco router simulation platform. |
3 |
|
|
* Copyright (c) 2006 Christophe Fillot (cf@utc.fr) |
4 |
|
|
* |
5 |
|
|
* PowerPC MMU. |
6 |
|
|
*/ |
7 |
|
|
|
8 |
|
|
#define _GNU_SOURCE |
9 |
|
|
#include <stdio.h> |
10 |
|
|
#include <stdlib.h> |
11 |
|
|
#include <unistd.h> |
12 |
|
|
#include <string.h> |
13 |
|
|
#include <sys/types.h> |
14 |
|
|
#include <sys/stat.h> |
15 |
|
|
#include <sys/mman.h> |
16 |
|
|
#include <fcntl.h> |
17 |
|
|
#include <assert.h> |
18 |
|
|
|
19 |
|
|
#include "cpu.h" |
20 |
|
|
#include "vm.h" |
21 |
|
|
#include "dynamips.h" |
22 |
|
|
#include "memory.h" |
23 |
|
|
#include "device.h" |
24 |
|
|
#include "ppc32_jit.h" |
25 |
|
|
|
26 |
|
|
#define DEBUG_ICBI 0 |
27 |
|
|
|
28 |
|
|
/* Memory access with special access mask */ |
29 |
|
|
void ppc32_access_special(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int cid, |
30 |
|
|
m_uint32_t mask,u_int op_code,u_int op_type, |
31 |
dpavlin |
11 |
u_int op_size,m_uint64_t *data) |
32 |
dpavlin |
7 |
{ |
33 |
|
|
switch(mask) { |
34 |
|
|
case MTS_ACC_T: |
35 |
|
|
if (op_code != PPC_MEMOP_LOOKUP) { |
36 |
|
|
#if DEBUG_MTS_ACC_T |
37 |
|
|
cpu_log(cpu->gen, |
38 |
|
|
"MTS","MMU exception for address 0x%8.8x at ia=0x%8.8x " |
39 |
|
|
"(%s access, size=%u)\n", |
40 |
|
|
vaddr,cpu->ia,(op_type == MTS_READ) ? |
41 |
|
|
"read":"write",op_size); |
42 |
|
|
//ppc32_dump_regs(cpu->gen); |
43 |
|
|
#if MEMLOG_ENABLE |
44 |
|
|
memlog_dump(cpu->gen); |
45 |
|
|
#endif |
46 |
|
|
#endif |
47 |
|
|
if (cid == PPC32_MTS_DCACHE) { |
48 |
|
|
cpu->dsisr = PPC32_DSISR_NOTRANS; |
49 |
|
|
|
50 |
|
|
if (op_type == MTS_WRITE) |
51 |
|
|
cpu->dsisr |= PPC32_DSISR_STORE; |
52 |
|
|
|
53 |
|
|
cpu->dar = vaddr; |
54 |
|
|
ppc32_trigger_exception(cpu,PPC32_EXC_DSI); |
55 |
dpavlin |
11 |
cpu_exec_loop_enter(cpu->gen); |
56 |
dpavlin |
7 |
} |
57 |
dpavlin |
11 |
} |
58 |
dpavlin |
7 |
break; |
59 |
|
|
|
60 |
|
|
case MTS_ACC_U: |
61 |
dpavlin |
11 |
if (op_type == MTS_READ) |
62 |
|
|
*data = 0; |
63 |
|
|
|
64 |
|
|
if (cpu->gen->undef_mem_handler != NULL) { |
65 |
|
|
if (cpu->gen->undef_mem_handler(cpu->gen,(m_uint64_t)vaddr, |
66 |
|
|
op_size,op_type,data)) |
67 |
|
|
return; |
68 |
|
|
} |
69 |
|
|
|
70 |
dpavlin |
7 |
#if DEBUG_MTS_ACC_U |
71 |
|
|
if (op_type == MTS_READ) |
72 |
|
|
cpu_log(cpu->gen, |
73 |
|
|
"MTS","read access to undefined address 0x%8.8x at " |
74 |
|
|
"ia=0x%8.8x (size=%u)\n",vaddr,cpu->ia,op_size); |
75 |
|
|
else |
76 |
|
|
cpu_log(cpu->gen, |
77 |
|
|
"MTS","write access to undefined address 0x%8.8x at " |
78 |
|
|
"ia=0x%8.8x, value=0x%8.8llx (size=%u)\n", |
79 |
|
|
vaddr,cpu->ia,*data,op_size); |
80 |
|
|
#endif |
81 |
|
|
break; |
82 |
|
|
} |
83 |
|
|
} |
84 |
|
|
|
85 |
|
|
/* Initialize the MTS subsystem for the specified CPU */ |
86 |
|
|
int ppc32_mem_init(cpu_ppc_t *cpu) |
87 |
|
|
{ |
88 |
|
|
size_t len; |
89 |
|
|
|
90 |
|
|
/* Initialize the cache entries to 0 (empty) */ |
91 |
|
|
len = MTS32_HASH_SIZE * sizeof(mts32_entry_t); |
92 |
|
|
|
93 |
|
|
if (!(cpu->mts_cache[PPC32_MTS_ICACHE] = malloc(len))) |
94 |
|
|
return(-1); |
95 |
|
|
|
96 |
|
|
if (!(cpu->mts_cache[PPC32_MTS_DCACHE] = malloc(len))) |
97 |
|
|
return(-1); |
98 |
|
|
|
99 |
|
|
memset(cpu->mts_cache[PPC32_MTS_ICACHE],0xFF,len); |
100 |
|
|
memset(cpu->mts_cache[PPC32_MTS_DCACHE],0xFF,len); |
101 |
|
|
|
102 |
|
|
cpu->mts_lookups = 0; |
103 |
|
|
cpu->mts_misses = 0; |
104 |
|
|
return(0); |
105 |
|
|
} |
106 |
|
|
|
107 |
|
|
/* Free memory used by MTS */ |
108 |
|
|
void ppc32_mem_shutdown(cpu_ppc_t *cpu) |
109 |
|
|
{ |
110 |
|
|
if (cpu != NULL) { |
111 |
|
|
/* Free the caches themselves */ |
112 |
|
|
free(cpu->mts_cache[PPC32_MTS_ICACHE]); |
113 |
|
|
free(cpu->mts_cache[PPC32_MTS_DCACHE]); |
114 |
|
|
cpu->mts_cache[PPC32_MTS_ICACHE] = NULL; |
115 |
|
|
cpu->mts_cache[PPC32_MTS_DCACHE] = NULL; |
116 |
|
|
} |
117 |
|
|
} |
118 |
|
|
|
119 |
|
|
/* Show MTS detailed information (debugging only!) */ |
120 |
|
|
void ppc32_mem_show_stats(cpu_gen_t *gen_cpu) |
121 |
|
|
{ |
122 |
|
|
cpu_ppc_t *cpu = CPU_PPC32(gen_cpu); |
123 |
|
|
#if DEBUG_MTS_MAP_VIRT |
124 |
|
|
mts32_entry_t *entry; |
125 |
|
|
u_int i,count; |
126 |
|
|
#endif |
127 |
|
|
|
128 |
|
|
printf("\nCPU%u: MTS statistics:\n",cpu->gen->id); |
129 |
|
|
|
130 |
|
|
#if DEBUG_MTS_MAP_VIRT |
131 |
|
|
printf("Instruction cache:\n"); |
132 |
|
|
|
133 |
|
|
/* Valid hash entries for Instruction Cache */ |
134 |
|
|
for(count=0,i=0;i<MTS32_HASH_SIZE;i++) { |
135 |
|
|
entry = &cpu->mts_cache[PPC32_MTS_ICACHE][i]; |
136 |
|
|
|
137 |
|
|
if (!(entry->gvpa & MTS_INV_ENTRY_MASK)) { |
138 |
|
|
printf(" %4u: vaddr=0x%8.8x, paddr=0x%8.8x, hpa=%p\n", |
139 |
|
|
i,entry->gvpa,entry->gppa,(void *)entry->hpa); |
140 |
|
|
count++; |
141 |
|
|
} |
142 |
|
|
} |
143 |
|
|
|
144 |
|
|
printf(" %u/%u valid hash entries for icache.\n",count,MTS32_HASH_SIZE); |
145 |
|
|
|
146 |
|
|
|
147 |
|
|
printf("Data cache:\n"); |
148 |
|
|
|
149 |
|
|
/* Valid hash entries for Instruction Cache */ |
150 |
|
|
for(count=0,i=0;i<MTS32_HASH_SIZE;i++) { |
151 |
|
|
entry = &cpu->mts_cache[PPC32_MTS_DCACHE][i]; |
152 |
|
|
|
153 |
|
|
if (!(entry->gvpa & MTS_INV_ENTRY_MASK)) { |
154 |
|
|
printf(" %4u: vaddr=0x%8.8x, paddr=0x%8.8x, hpa=%p\n", |
155 |
|
|
i,entry->gvpa,entry->gppa,(void *)entry->hpa); |
156 |
|
|
count++; |
157 |
|
|
} |
158 |
|
|
} |
159 |
|
|
|
160 |
|
|
printf(" %u/%u valid hash entries for dcache.\n",count,MTS32_HASH_SIZE); |
161 |
|
|
#endif |
162 |
|
|
|
163 |
|
|
printf("\n Total lookups: %llu, misses: %llu, efficiency: %g%%\n", |
164 |
|
|
cpu->mts_lookups, cpu->mts_misses, |
165 |
|
|
100 - ((double)(cpu->mts_misses*100)/ |
166 |
|
|
(double)cpu->mts_lookups)); |
167 |
|
|
} |
168 |
|
|
|
169 |
|
|
/* Invalidate the MTS caches (instruction and data) */ |
170 |
|
|
void ppc32_mem_invalidate_cache(cpu_ppc_t *cpu) |
171 |
|
|
{ |
172 |
|
|
size_t len; |
173 |
|
|
|
174 |
|
|
len = MTS32_HASH_SIZE * sizeof(mts32_entry_t); |
175 |
|
|
memset(cpu->mts_cache[PPC32_MTS_ICACHE],0xFF,len); |
176 |
|
|
memset(cpu->mts_cache[PPC32_MTS_DCACHE],0xFF,len); |
177 |
|
|
} |
178 |
|
|
|
179 |
|
|
/* |
180 |
|
|
* MTS mapping. |
181 |
|
|
* |
182 |
|
|
* It is NOT inlined since it triggers a GCC bug on my config (x86, GCC 3.3.5) |
183 |
|
|
*/ |
184 |
|
|
static no_inline struct mts32_entry * |
185 |
|
|
ppc32_mem_map(cpu_ppc_t *cpu,u_int op_type,mts_map_t *map, |
186 |
|
|
mts32_entry_t *entry,mts32_entry_t *alt_entry) |
187 |
|
|
{ |
188 |
dpavlin |
8 |
ppc32_jit_tcb_t *block; |
189 |
dpavlin |
7 |
struct vdevice *dev; |
190 |
|
|
m_uint32_t offset; |
191 |
|
|
m_iptr_t host_ptr; |
192 |
dpavlin |
8 |
m_uint32_t exec_flag = 0; |
193 |
dpavlin |
7 |
int cow; |
194 |
|
|
|
195 |
dpavlin |
11 |
if (!(dev = dev_lookup(cpu->vm,map->paddr+map->offset,map->cached))) |
196 |
dpavlin |
7 |
return NULL; |
197 |
|
|
|
198 |
dpavlin |
8 |
if (cpu->exec_phys_map) { |
199 |
|
|
block = ppc32_jit_find_by_phys_page(cpu,map->paddr >> VM_PAGE_SHIFT); |
200 |
|
|
|
201 |
|
|
if (block) |
202 |
|
|
exec_flag = MTS_FLAG_EXEC; |
203 |
|
|
} |
204 |
|
|
|
205 |
dpavlin |
7 |
if (dev->flags & VDEVICE_FLAG_SPARSE) { |
206 |
|
|
host_ptr = dev_sparse_get_host_addr(cpu->vm,dev,map->paddr,op_type,&cow); |
207 |
|
|
|
208 |
|
|
entry->gvpa = map->vaddr; |
209 |
|
|
entry->gppa = map->paddr; |
210 |
|
|
entry->hpa = host_ptr; |
211 |
|
|
entry->flags = (cow) ? MTS_FLAG_COW : 0; |
212 |
dpavlin |
8 |
entry->flags |= exec_flag; |
213 |
dpavlin |
7 |
return entry; |
214 |
|
|
} |
215 |
|
|
|
216 |
|
|
if (!dev->host_addr || (dev->flags & VDEVICE_FLAG_NO_MTS_MMAP)) { |
217 |
dpavlin |
11 |
offset = (map->paddr + map->offset) - dev->phys_addr; |
218 |
dpavlin |
7 |
|
219 |
dpavlin |
11 |
/* device entries are never stored in virtual TLB */ |
220 |
dpavlin |
7 |
alt_entry->hpa = (dev->id << MTS_DEVID_SHIFT) + offset; |
221 |
|
|
alt_entry->flags = MTS_FLAG_DEV; |
222 |
|
|
return alt_entry; |
223 |
|
|
} |
224 |
|
|
|
225 |
|
|
entry->gvpa = map->vaddr; |
226 |
|
|
entry->gppa = map->paddr; |
227 |
|
|
entry->hpa = dev->host_addr + (map->paddr - dev->phys_addr); |
228 |
dpavlin |
8 |
entry->flags = exec_flag; |
229 |
dpavlin |
7 |
return entry; |
230 |
|
|
} |
231 |
|
|
|
232 |
|
|
/* BAT lookup */ |
233 |
|
|
static forced_inline int ppc32_bat_lookup(cpu_ppc_t *cpu,m_uint32_t vaddr, |
234 |
|
|
u_int cid,mts_map_t *map) |
235 |
|
|
{ |
236 |
|
|
m_uint32_t bepi,mask,bl,pr,ubat; |
237 |
|
|
int i; |
238 |
|
|
|
239 |
|
|
pr = (cpu->msr & PPC32_MSR_PR) >> PPC32_MSR_PR_SHIFT; |
240 |
|
|
pr = ((~pr << 1) | pr) & 0x03; |
241 |
|
|
|
242 |
|
|
for(i=0;i<PPC32_BAT_NR;i++) { |
243 |
|
|
ubat = cpu->bat[cid][i].reg[0]; |
244 |
|
|
|
245 |
|
|
if (!(ubat & pr)) |
246 |
|
|
continue; |
247 |
|
|
|
248 |
|
|
//bl = (ubat & PPC32_UBAT_BL_MASK) >> PPC32_UBAT_BL_SHIFT; |
249 |
|
|
bl = (ubat & PPC32_UBAT_XBL_MASK) >> PPC32_UBAT_XBL_SHIFT; |
250 |
|
|
|
251 |
|
|
mask = ~bl << PPC32_BAT_ADDR_SHIFT; |
252 |
|
|
bepi = ubat & PPC32_UBAT_BEPI_MASK; |
253 |
|
|
|
254 |
|
|
if (bepi == (vaddr & mask)) { |
255 |
|
|
map->vaddr = vaddr & PPC32_MIN_PAGE_MASK; |
256 |
|
|
map->paddr = cpu->bat[cid][i].reg[1] & PPC32_LBAT_BRPN_MASK; |
257 |
|
|
map->paddr += map->vaddr - bepi; |
258 |
dpavlin |
11 |
map->offset = vaddr & PPC32_MIN_PAGE_IMASK; |
259 |
dpavlin |
7 |
map->cached = FALSE; |
260 |
|
|
return(TRUE); |
261 |
|
|
} |
262 |
|
|
} |
263 |
|
|
|
264 |
|
|
return(FALSE); |
265 |
|
|
} |
266 |
|
|
|
267 |
|
|
/* Memory slow lookup */ |
268 |
|
|
static mts32_entry_t *ppc32_slow_lookup(cpu_ppc_t *cpu,m_uint32_t vaddr, |
269 |
|
|
u_int cid,u_int op_code,u_int op_size, |
270 |
|
|
u_int op_type,m_uint64_t *data, |
271 |
dpavlin |
11 |
mts32_entry_t *alt_entry) |
272 |
dpavlin |
7 |
{ |
273 |
|
|
m_uint32_t hash_bucket,segment,vsid; |
274 |
|
|
m_uint32_t hash,tmp,pteg_offset,pte_key,key,pte2; |
275 |
|
|
mts32_entry_t *entry; |
276 |
|
|
m_uint8_t *pte_haddr; |
277 |
|
|
m_uint64_t paddr; |
278 |
|
|
mts_map_t map; |
279 |
|
|
int i; |
280 |
|
|
|
281 |
|
|
#if DEBUG_MTS_STATS |
282 |
|
|
cpu->mts_misses++; |
283 |
|
|
#endif |
284 |
|
|
|
285 |
|
|
hash_bucket = MTS32_HASH(vaddr); |
286 |
|
|
entry = &cpu->mts_cache[cid][hash_bucket]; |
287 |
|
|
|
288 |
|
|
/* No translation - cover the 4GB space */ |
289 |
|
|
if (((cid == PPC32_MTS_ICACHE) && !(cpu->msr & PPC32_MSR_IR)) || |
290 |
|
|
((cid == PPC32_MTS_DCACHE) && !(cpu->msr & PPC32_MSR_DR))) |
291 |
|
|
{ |
292 |
|
|
map.vaddr = vaddr & PPC32_MIN_PAGE_MASK; |
293 |
|
|
map.paddr = vaddr & PPC32_MIN_PAGE_MASK; |
294 |
dpavlin |
11 |
map.offset = vaddr & PPC32_MIN_PAGE_IMASK; |
295 |
dpavlin |
7 |
map.cached = FALSE; |
296 |
|
|
|
297 |
|
|
if (!(entry = ppc32_mem_map(cpu,op_type,&map,entry,alt_entry))) |
298 |
|
|
goto err_undef; |
299 |
|
|
|
300 |
|
|
return entry; |
301 |
|
|
} |
302 |
|
|
|
303 |
|
|
/* Walk through the BAT registers */ |
304 |
|
|
if (ppc32_bat_lookup(cpu,vaddr,cid,&map)) { |
305 |
|
|
if (!(entry = ppc32_mem_map(cpu,op_type,&map,entry,alt_entry))) |
306 |
|
|
goto err_undef; |
307 |
|
|
|
308 |
|
|
return entry; |
309 |
|
|
} |
310 |
|
|
|
311 |
|
|
if (unlikely(!cpu->sdr1)) |
312 |
|
|
goto no_pte; |
313 |
|
|
|
314 |
|
|
/* Get the virtual segment identifier */ |
315 |
|
|
segment = vaddr >> 28; |
316 |
|
|
vsid = cpu->sr[segment] & PPC32_SD_VSID_MASK; |
317 |
|
|
|
318 |
|
|
/* Compute the first hash value */ |
319 |
|
|
hash = (vaddr >> PPC32_MIN_PAGE_SHIFT) & 0xFFFF; |
320 |
|
|
hash ^= vsid; |
321 |
|
|
hash &= 0x7FFFFF; |
322 |
|
|
|
323 |
|
|
tmp = (hash >> 10) & (cpu->sdr1 & PPC32_SDR1_HTMEXT_MASK); |
324 |
|
|
pteg_offset = (hash & 0x3FF) << 6; |
325 |
|
|
pteg_offset |= tmp << 16; |
326 |
|
|
pte_haddr = cpu->sdr1_hptr + pteg_offset; |
327 |
|
|
|
328 |
|
|
pte_key = 0x80000000 | (vsid << 7); |
329 |
|
|
pte_key |= (vaddr >> 22) & 0x3F; |
330 |
|
|
|
331 |
|
|
for(i=0;i<8;i++,pte_haddr+=PPC32_PTE_SIZE) { |
332 |
|
|
key = vmtoh32(*(m_uint32_t *)pte_haddr); |
333 |
|
|
|
334 |
|
|
if (key == pte_key) |
335 |
|
|
goto pte_lookup_done; |
336 |
|
|
} |
337 |
|
|
|
338 |
|
|
/* Secondary hash value */ |
339 |
|
|
hash = (~hash) & 0x7FFFFF; |
340 |
|
|
|
341 |
|
|
tmp = (hash >> 10) & (cpu->sdr1 & PPC32_SDR1_HTMEXT_MASK); |
342 |
|
|
pteg_offset = (hash & 0x3FF) << 6; |
343 |
|
|
pteg_offset |= tmp << 16; |
344 |
|
|
pte_haddr = cpu->sdr1_hptr + pteg_offset; |
345 |
|
|
|
346 |
|
|
pte_key = 0x80000040 | (vsid << 7); |
347 |
|
|
pte_key |= (vaddr >> 22) & 0x3F; |
348 |
|
|
|
349 |
|
|
for(i=0;i<8;i++,pte_haddr+=PPC32_PTE_SIZE) { |
350 |
|
|
key = vmtoh32(*(m_uint32_t *)pte_haddr); |
351 |
|
|
|
352 |
|
|
if (key == pte_key) |
353 |
|
|
goto pte_lookup_done; |
354 |
|
|
} |
355 |
|
|
|
356 |
|
|
no_pte: |
357 |
|
|
/* No matching PTE for this virtual address */ |
358 |
dpavlin |
11 |
ppc32_access_special(cpu,vaddr,cid,MTS_ACC_T,op_code,op_type,op_size,data); |
359 |
dpavlin |
7 |
return NULL; |
360 |
|
|
|
361 |
|
|
pte_lookup_done: |
362 |
|
|
pte2 = vmtoh32(*(m_uint32_t *)(pte_haddr + sizeof(m_uint32_t))); |
363 |
|
|
paddr = pte2 & PPC32_PTEL_RPN_MASK; |
364 |
|
|
paddr |= (pte2 & PPC32_PTEL_XPN_MASK) << (33 - PPC32_PTEL_XPN_SHIFT); |
365 |
|
|
paddr |= (pte2 & PPC32_PTEL_X_MASK) << (32 - PPC32_PTEL_X_SHIFT); |
366 |
|
|
|
367 |
|
|
map.vaddr = vaddr & ~PPC32_MIN_PAGE_IMASK; |
368 |
|
|
map.paddr = paddr; |
369 |
dpavlin |
11 |
map.offset = vaddr & PPC32_MIN_PAGE_IMASK; |
370 |
dpavlin |
7 |
map.cached = FALSE; |
371 |
|
|
|
372 |
|
|
if ((entry = ppc32_mem_map(cpu,op_type,&map,entry,alt_entry))) |
373 |
|
|
return entry; |
374 |
|
|
|
375 |
|
|
err_undef: |
376 |
dpavlin |
11 |
ppc32_access_special(cpu,vaddr,cid,MTS_ACC_U,op_code,op_type,op_size,data); |
377 |
dpavlin |
7 |
return NULL; |
378 |
|
|
} |
379 |
|
|
|
380 |
|
|
/* Memory access */ |
381 |
|
|
static inline void *ppc32_mem_access(cpu_ppc_t *cpu,m_uint32_t vaddr, |
382 |
|
|
u_int cid,u_int op_code,u_int op_size, |
383 |
dpavlin |
11 |
u_int op_type,m_uint64_t *data) |
384 |
dpavlin |
7 |
{ |
385 |
|
|
mts32_entry_t *entry,alt_entry; |
386 |
dpavlin |
8 |
ppc32_jit_tcb_t *block; |
387 |
dpavlin |
7 |
m_uint32_t hash_bucket; |
388 |
dpavlin |
8 |
m_uint32_t phys_page; |
389 |
|
|
m_uint32_t ia_hash; |
390 |
dpavlin |
7 |
m_iptr_t haddr; |
391 |
|
|
u_int dev_id; |
392 |
|
|
int cow; |
393 |
|
|
|
394 |
|
|
#if MEMLOG_ENABLE |
395 |
|
|
/* Record the memory access */ |
396 |
|
|
memlog_rec_access(cpu->gen,vaddr,*data,op_size,op_type); |
397 |
|
|
#endif |
398 |
|
|
|
399 |
|
|
hash_bucket = MTS32_HASH(vaddr); |
400 |
|
|
entry = &cpu->mts_cache[cid][hash_bucket]; |
401 |
|
|
|
402 |
|
|
#if DEBUG_MTS_STATS |
403 |
|
|
cpu->mts_lookups++; |
404 |
|
|
#endif |
405 |
|
|
|
406 |
|
|
/* Copy-On-Write for sparse device ? */ |
407 |
|
|
cow = (op_type == MTS_WRITE) && (entry->flags & MTS_FLAG_COW); |
408 |
|
|
|
409 |
|
|
/* Slow lookup if nothing found in cache */ |
410 |
|
|
if (unlikely(((vaddr & PPC32_MIN_PAGE_MASK) != entry->gvpa) || cow)) { |
411 |
|
|
entry = cpu->mts_slow_lookup(cpu,vaddr,cid,op_code,op_size,op_type, |
412 |
dpavlin |
11 |
data,&alt_entry); |
413 |
dpavlin |
7 |
if (!entry) |
414 |
|
|
return NULL; |
415 |
|
|
|
416 |
|
|
if (entry->flags & MTS_FLAG_DEV) { |
417 |
|
|
dev_id = (entry->hpa & MTS_DEVID_MASK) >> MTS_DEVID_SHIFT; |
418 |
|
|
haddr = entry->hpa & MTS_DEVOFF_MASK; |
419 |
|
|
return(dev_access_fast(cpu->gen,dev_id,haddr,op_size,op_type,data)); |
420 |
|
|
} |
421 |
|
|
} |
422 |
|
|
|
423 |
dpavlin |
8 |
/* Invalidate JIT code for written pages */ |
424 |
|
|
if ((op_type == MTS_WRITE) && (entry->flags & MTS_FLAG_EXEC)) { |
425 |
|
|
if (cpu->exec_phys_map) { |
426 |
|
|
phys_page = entry->gppa >> VM_PAGE_SHIFT; |
427 |
|
|
|
428 |
|
|
if (vaddr >= PPC32_EXC_SYS_RST) { |
429 |
|
|
block = ppc32_jit_find_by_phys_page(cpu,phys_page); |
430 |
|
|
|
431 |
|
|
if (block != NULL) { |
432 |
|
|
//printf("Invalidation of block 0x%8.8x\n",block->start_ia); |
433 |
|
|
ia_hash = ppc32_jit_get_ia_hash(block->start_ia); |
434 |
|
|
ppc32_jit_tcb_free(cpu,block,TRUE); |
435 |
|
|
|
436 |
|
|
if (cpu->exec_blk_map[ia_hash] == block) |
437 |
|
|
cpu->exec_blk_map[ia_hash] = NULL; |
438 |
|
|
|
439 |
|
|
entry->flags &= ~MTS_FLAG_EXEC; |
440 |
|
|
} |
441 |
|
|
} |
442 |
|
|
} |
443 |
|
|
} |
444 |
|
|
|
445 |
dpavlin |
7 |
/* Raw memory access */ |
446 |
|
|
haddr = entry->hpa + (vaddr & PPC32_MIN_PAGE_IMASK); |
447 |
|
|
#if MEMLOG_ENABLE |
448 |
|
|
memlog_update_read(cpu->gen,haddr); |
449 |
|
|
#endif |
450 |
|
|
return((void *)haddr); |
451 |
|
|
} |
452 |
|
|
|
453 |
|
|
/* Memory data access */ |
454 |
dpavlin |
11 |
#define PPC32_MEM_DACCESS(cpu,vaddr,op_code,op_size,op_type,data) \ |
455 |
dpavlin |
7 |
ppc32_mem_access((cpu),(vaddr),PPC32_MTS_DCACHE,(op_code),(op_size),\ |
456 |
dpavlin |
11 |
(op_type),(data)) |
457 |
dpavlin |
7 |
|
458 |
|
|
/* Virtual address to physical page translation */ |
459 |
|
|
static fastcall int ppc32_translate(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int cid, |
460 |
|
|
m_uint32_t *phys_page) |
461 |
|
|
{ |
462 |
|
|
mts32_entry_t *entry,alt_entry; |
463 |
|
|
m_uint32_t hash_bucket; |
464 |
|
|
m_uint64_t data = 0; |
465 |
|
|
|
466 |
|
|
hash_bucket = MTS32_HASH(vaddr); |
467 |
|
|
entry = &cpu->mts_cache[cid][hash_bucket]; |
468 |
|
|
|
469 |
|
|
/* Slow lookup if nothing found in cache */ |
470 |
|
|
if (unlikely(((m_uint32_t)vaddr & PPC32_MIN_PAGE_MASK) != entry->gvpa)) { |
471 |
|
|
entry = cpu->mts_slow_lookup(cpu,vaddr,cid,PPC_MEMOP_LOOKUP,4,MTS_READ, |
472 |
dpavlin |
11 |
&data,&alt_entry); |
473 |
dpavlin |
7 |
if (!entry) |
474 |
|
|
return(-1); |
475 |
|
|
} |
476 |
|
|
|
477 |
|
|
*phys_page = entry->gppa >> PPC32_MIN_PAGE_SHIFT; |
478 |
|
|
return(0); |
479 |
|
|
} |
480 |
|
|
|
481 |
|
|
/* Virtual address lookup */ |
482 |
|
|
static void *ppc32_mem_lookup(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int cid) |
483 |
|
|
{ |
484 |
|
|
m_uint64_t data; |
485 |
dpavlin |
11 |
return(ppc32_mem_access(cpu,vaddr,cid,PPC_MEMOP_LOOKUP,4,MTS_READ,&data)); |
486 |
dpavlin |
7 |
} |
487 |
|
|
|
488 |
|
|
/* Set a BAT register */ |
489 |
|
|
int ppc32_set_bat(cpu_ppc_t *cpu,struct ppc32_bat_prog *bp) |
490 |
|
|
{ |
491 |
|
|
struct ppc32_bat_reg *bat; |
492 |
|
|
|
493 |
|
|
if ((bp->type != PPC32_IBAT_IDX) && (bp->type != PPC32_DBAT_IDX)) |
494 |
|
|
return(-1); |
495 |
|
|
|
496 |
|
|
if (bp->index >= PPC32_BAT_NR) |
497 |
|
|
return(-1); |
498 |
|
|
|
499 |
|
|
bat = &cpu->bat[bp->type][bp->index]; |
500 |
|
|
bat->reg[0] = bp->hi; |
501 |
|
|
bat->reg[1] = bp->lo; |
502 |
|
|
return(0); |
503 |
|
|
} |
504 |
|
|
|
505 |
|
|
/* Load BAT registers from a BAT array */ |
506 |
|
|
void ppc32_load_bat_array(cpu_ppc_t *cpu,struct ppc32_bat_prog *bp) |
507 |
|
|
{ |
508 |
|
|
while(bp->index != -1) { |
509 |
|
|
ppc32_set_bat(cpu,bp); |
510 |
|
|
bp++; |
511 |
|
|
} |
512 |
|
|
} |
513 |
|
|
|
514 |
|
|
/* Get the host address for SDR1 */ |
515 |
|
|
int ppc32_set_sdr1(cpu_ppc_t *cpu,m_uint32_t sdr1) |
516 |
|
|
{ |
517 |
|
|
struct vdevice *dev; |
518 |
|
|
m_uint64_t pt_addr; |
519 |
|
|
|
520 |
|
|
cpu->sdr1 = sdr1; |
521 |
|
|
pt_addr = sdr1 & PPC32_SDR1_HTABORG_MASK; |
522 |
|
|
pt_addr |= ((m_uint64_t)(sdr1 & PPC32_SDR1_HTABEXT_MASK) << 20); |
523 |
|
|
|
524 |
|
|
if (!(dev = dev_lookup(cpu->vm,pt_addr,TRUE))) { |
525 |
|
|
fprintf(stderr,"ppc32_set_sdr1: unable to find haddr for SDR1=0x%8.8x\n", |
526 |
|
|
sdr1); |
527 |
|
|
return(-1); |
528 |
|
|
} |
529 |
|
|
|
530 |
|
|
cpu->sdr1_hptr = (char *)dev->host_addr + (pt_addr - dev->phys_addr); |
531 |
|
|
return(0); |
532 |
|
|
} |
533 |
|
|
|
534 |
|
|
/* Initialize the page table */ |
535 |
|
|
int ppc32_init_page_table(cpu_ppc_t *cpu) |
536 |
|
|
{ |
537 |
|
|
m_uint32_t pt_size; |
538 |
|
|
|
539 |
|
|
if (!cpu->sdr1_hptr) |
540 |
|
|
return(-1); |
541 |
|
|
|
542 |
|
|
pt_size = (1 + (cpu->sdr1 & PPC32_SDR1_HTMEXT_MASK)) << 16; |
543 |
|
|
memset(cpu->sdr1_hptr,0,pt_size); |
544 |
|
|
return(0); |
545 |
|
|
} |
546 |
|
|
|
547 |
|
|
/* Map a page */ |
548 |
|
|
int ppc32_map_page(cpu_ppc_t *cpu,u_int vsid,m_uint32_t vaddr,m_uint64_t paddr, |
549 |
|
|
u_int wimg,u_int pp) |
550 |
|
|
{ |
551 |
|
|
m_uint32_t hash,tmp,pteg_offset,key; |
552 |
|
|
m_uint8_t *pte_haddr; |
553 |
|
|
int i; |
554 |
|
|
|
555 |
|
|
/* Compute the first hash value */ |
556 |
|
|
hash = (vaddr >> PPC32_MIN_PAGE_SHIFT) & 0xFFFF; |
557 |
|
|
hash ^= vsid; |
558 |
|
|
hash &= 0x7FFFFF; |
559 |
|
|
|
560 |
|
|
tmp = (hash >> 10) & (cpu->sdr1 & PPC32_SDR1_HTMEXT_MASK); |
561 |
|
|
pteg_offset = (hash & 0x3FF) << 6; |
562 |
|
|
pteg_offset |= tmp << 16; |
563 |
|
|
pte_haddr = cpu->sdr1_hptr + pteg_offset; |
564 |
|
|
|
565 |
|
|
for(i=0;i<8;i++,pte_haddr+=PPC32_PTE_SIZE) { |
566 |
|
|
key = vmtoh32(*(m_uint32_t *)pte_haddr); |
567 |
|
|
|
568 |
|
|
if (!(key & PPC32_PTEU_V)) { |
569 |
|
|
hash = 0; |
570 |
|
|
goto free_pte_found; |
571 |
|
|
} |
572 |
|
|
} |
573 |
|
|
|
574 |
|
|
/* Secondary hash value */ |
575 |
|
|
hash = (~hash) & 0x7FFFFF; |
576 |
|
|
|
577 |
|
|
tmp = (hash >> 10) & (cpu->sdr1 & PPC32_SDR1_HTMEXT_MASK); |
578 |
|
|
pteg_offset = (hash & 0x3FF) << 6; |
579 |
|
|
pteg_offset |= tmp << 16; |
580 |
|
|
pte_haddr = cpu->sdr1_hptr + pteg_offset; |
581 |
|
|
|
582 |
|
|
for(i=0;i<8;i++,pte_haddr+=PPC32_PTE_SIZE) { |
583 |
|
|
key = vmtoh32(*(m_uint32_t *)pte_haddr); |
584 |
|
|
|
585 |
|
|
if (!(key & PPC32_PTEU_V)) { |
586 |
|
|
hash = PPC32_PTEU_H; |
587 |
|
|
goto free_pte_found; |
588 |
|
|
} |
589 |
|
|
} |
590 |
|
|
|
591 |
|
|
/* No free PTE found */ |
592 |
|
|
return(-1); |
593 |
|
|
|
594 |
|
|
free_pte_found: |
595 |
|
|
tmp = PPC32_PTEU_V | (vsid << PPC32_PTEU_VSID_SHIFT) | hash; |
596 |
|
|
tmp |= (vaddr >> 22) & 0x3F; |
597 |
|
|
*(m_uint32_t *)pte_haddr = htovm32(tmp); |
598 |
|
|
|
599 |
|
|
tmp = vaddr & PPC32_PTEL_RPN_MASK; |
600 |
|
|
tmp |= (vaddr >> (32 - PPC32_PTEL_X_SHIFT)) & PPC32_PTEL_X_MASK; |
601 |
|
|
tmp |= (vaddr >> (33 - PPC32_PTEL_XPN_SHIFT)) & PPC32_PTEL_XPN_MASK; |
602 |
|
|
|
603 |
|
|
tmp |= (wimg << PPC32_PTEL_WIMG_SHIFT) + pp; |
604 |
|
|
*(m_uint32_t *)(pte_haddr+sizeof(m_uint32_t)) = htovm32(tmp); |
605 |
|
|
return(0); |
606 |
|
|
} |
607 |
|
|
|
608 |
|
|
/* Map a memory zone */ |
609 |
|
|
int ppc32_map_zone(cpu_ppc_t *cpu,u_int vsid,m_uint32_t vaddr,m_uint64_t paddr, |
610 |
|
|
m_uint32_t size,u_int wimg,u_int pp) |
611 |
|
|
{ |
612 |
|
|
while(size > 0) { |
613 |
|
|
if (ppc32_map_page(cpu,vsid,vaddr,paddr,wimg,pp) == -1) |
614 |
|
|
return(-1); |
615 |
|
|
|
616 |
|
|
size -= PPC32_MIN_PAGE_SIZE; |
617 |
|
|
vaddr += PPC32_MIN_PAGE_SIZE; |
618 |
|
|
paddr += PPC32_MIN_PAGE_SIZE; |
619 |
|
|
} |
620 |
|
|
|
621 |
|
|
return(0); |
622 |
|
|
} |
623 |
|
|
|
624 |
|
|
/* PowerPC 405 TLB masks */ |
625 |
|
|
static m_uint32_t ppc405_tlb_masks[8] = { |
626 |
|
|
0xFFFFFC00, 0xFFFFF000, 0xFFFFC000, 0xFFFF0000, |
627 |
|
|
0xFFFC0000, 0xFFF00000, 0xFFC00000, 0xFF000000, |
628 |
|
|
}; |
629 |
|
|
|
630 |
|
|
/* PowerPC 405 slow lookup */ |
631 |
|
|
static mts32_entry_t *ppc405_slow_lookup(cpu_ppc_t *cpu,m_uint32_t vaddr, |
632 |
|
|
u_int cid,u_int op_code,u_int op_size, |
633 |
|
|
u_int op_type,m_uint64_t *data, |
634 |
dpavlin |
11 |
mts32_entry_t *alt_entry) |
635 |
dpavlin |
7 |
{ |
636 |
|
|
struct ppc405_tlb_entry *tlb_entry; |
637 |
|
|
m_uint32_t hash_bucket,mask; |
638 |
|
|
m_uint32_t page_size; |
639 |
|
|
mts32_entry_t *entry; |
640 |
|
|
mts_map_t map; |
641 |
|
|
int i; |
642 |
|
|
|
643 |
|
|
#if DEBUG_MTS_STATS |
644 |
|
|
cpu->mts_misses++; |
645 |
|
|
#endif |
646 |
|
|
|
647 |
|
|
hash_bucket = MTS32_HASH(vaddr); |
648 |
|
|
entry = &cpu->mts_cache[cid][hash_bucket]; |
649 |
|
|
|
650 |
|
|
/* No translation - cover the 4GB space */ |
651 |
|
|
if (((cid == PPC32_MTS_ICACHE) && !(cpu->msr & PPC32_MSR_IR)) || |
652 |
|
|
((cid == PPC32_MTS_DCACHE) && !(cpu->msr & PPC32_MSR_DR))) |
653 |
|
|
{ |
654 |
|
|
map.vaddr = vaddr & PPC32_MIN_PAGE_MASK; |
655 |
|
|
map.paddr = vaddr & PPC32_MIN_PAGE_MASK; |
656 |
|
|
map.cached = FALSE; |
657 |
|
|
|
658 |
|
|
if (!(entry = ppc32_mem_map(cpu,op_type,&map,entry,alt_entry))) |
659 |
|
|
goto err_undef; |
660 |
|
|
|
661 |
|
|
return entry; |
662 |
|
|
} |
663 |
|
|
|
664 |
|
|
/* Walk through the unified TLB */ |
665 |
|
|
for(i=0;i<PPC405_TLB_ENTRIES;i++) |
666 |
|
|
{ |
667 |
|
|
tlb_entry = &cpu->ppc405_tlb[i]; |
668 |
|
|
|
669 |
|
|
/* We want a valid entry with TID = PID */ |
670 |
|
|
if (!(tlb_entry->tlb_hi & PPC405_TLBHI_V) || |
671 |
|
|
(tlb_entry->tid != cpu->ppc405_pid)) |
672 |
|
|
continue; |
673 |
|
|
|
674 |
|
|
/* Get the address mask corresponding to this entry */ |
675 |
|
|
page_size = tlb_entry->tlb_hi & PPC405_TLBHI_SIZE_MASK; |
676 |
|
|
page_size >>= PPC405_TLBHI_SIZE_SHIFT; |
677 |
|
|
mask = ppc405_tlb_masks[page_size]; |
678 |
|
|
|
679 |
|
|
/* Matching entry ? */ |
680 |
|
|
if ((vaddr & mask) == (tlb_entry->tlb_hi & mask)) { |
681 |
|
|
map.vaddr = vaddr & mask; |
682 |
|
|
map.paddr = tlb_entry->tlb_lo & mask; |
683 |
|
|
map.cached = FALSE; |
684 |
|
|
|
685 |
|
|
if (!(entry = ppc32_mem_map(cpu,op_type,&map,entry,alt_entry))) |
686 |
|
|
goto err_undef; |
687 |
|
|
|
688 |
|
|
return entry; |
689 |
|
|
} |
690 |
|
|
} |
691 |
|
|
|
692 |
|
|
/* No matching TLB entry for this virtual address */ |
693 |
dpavlin |
11 |
ppc32_access_special(cpu,vaddr,cid,MTS_ACC_T,op_code,op_type,op_size,data); |
694 |
dpavlin |
7 |
return NULL; |
695 |
|
|
|
696 |
|
|
err_undef: |
697 |
dpavlin |
11 |
ppc32_access_special(cpu,vaddr,cid,MTS_ACC_U,op_code,op_type,op_size,data); |
698 |
dpavlin |
7 |
return NULL; |
699 |
|
|
} |
700 |
|
|
|
701 |
|
|
/* Dump a PowerPC 405 TLB entry */ |
702 |
|
|
static void ppc405_dump_tlb_entry(cpu_ppc_t *cpu,u_int index) |
703 |
|
|
{ |
704 |
|
|
struct ppc405_tlb_entry *entry; |
705 |
|
|
|
706 |
|
|
entry = &cpu->ppc405_tlb[index]; |
707 |
|
|
|
708 |
|
|
printf(" %2d: hi=0x%8.8x lo=0x%8.8x tid=0x%2.2x\n", |
709 |
|
|
index,entry->tlb_hi,entry->tlb_lo,entry->tid); |
710 |
|
|
} |
711 |
|
|
|
712 |
|
|
/* Dump the PowerPC 405 TLB */ |
713 |
|
|
static void ppc405_dump_tlb(cpu_gen_t *cpu) |
714 |
|
|
{ |
715 |
|
|
cpu_ppc_t *pcpu = CPU_PPC32(cpu); |
716 |
|
|
u_int i; |
717 |
|
|
|
718 |
|
|
for(i=0;i<PPC405_TLB_ENTRIES;i++) |
719 |
|
|
ppc405_dump_tlb_entry(pcpu,i); |
720 |
|
|
} |
721 |
|
|
|
722 |
|
|
/* === PPC Memory Operations ============================================= */ |
723 |
|
|
|
724 |
|
|
/* LBZ: Load Byte Zero */ |
725 |
dpavlin |
11 |
fastcall void ppc32_lbz(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) |
726 |
dpavlin |
7 |
{ |
727 |
|
|
m_uint64_t data; |
728 |
|
|
void *haddr; |
729 |
|
|
|
730 |
dpavlin |
11 |
haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_LBZ,1,MTS_READ,&data); |
731 |
dpavlin |
7 |
if (likely(haddr != NULL)) data = *(m_uint8_t *)haddr; |
732 |
dpavlin |
11 |
cpu->gpr[reg] = data & 0xFF; |
733 |
dpavlin |
7 |
} |
734 |
|
|
|
735 |
|
|
/* LHZ: Load Half-Word Zero */ |
736 |
dpavlin |
11 |
fastcall void ppc32_lhz(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) |
737 |
dpavlin |
7 |
{ |
738 |
|
|
m_uint64_t data; |
739 |
|
|
void *haddr; |
740 |
|
|
|
741 |
dpavlin |
11 |
haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_LHZ,2,MTS_READ,&data); |
742 |
dpavlin |
7 |
if (likely(haddr != NULL)) data = vmtoh16(*(m_uint16_t *)haddr); |
743 |
dpavlin |
11 |
cpu->gpr[reg] = data & 0xFFFF; |
744 |
dpavlin |
7 |
} |
745 |
|
|
|
746 |
|
|
/* LWZ: Load Word Zero */ |
747 |
dpavlin |
11 |
fastcall void ppc32_lwz(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) |
748 |
dpavlin |
7 |
{ |
749 |
|
|
m_uint64_t data; |
750 |
|
|
void *haddr; |
751 |
|
|
|
752 |
dpavlin |
11 |
haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_LWZ,4,MTS_READ,&data); |
753 |
dpavlin |
7 |
if (likely(haddr != NULL)) data = vmtoh32(*(m_uint32_t *)haddr); |
754 |
dpavlin |
11 |
cpu->gpr[reg] = data; |
755 |
dpavlin |
7 |
} |
756 |
|
|
|
757 |
|
|
/* LWBR: Load Word Byte Reverse */ |
758 |
dpavlin |
11 |
fastcall void ppc32_lwbr(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) |
759 |
dpavlin |
7 |
{ |
760 |
|
|
m_uint64_t data; |
761 |
|
|
void *haddr; |
762 |
|
|
|
763 |
dpavlin |
11 |
haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_LWBR,4,MTS_READ,&data); |
764 |
dpavlin |
7 |
if (likely(haddr != NULL)) data = vmtoh32(*(m_uint32_t *)haddr); |
765 |
dpavlin |
11 |
cpu->gpr[reg] = swap32(data); |
766 |
dpavlin |
7 |
} |
767 |
|
|
|
768 |
|
|
/* LHA: Load Half-Word Algebraic */ |
769 |
dpavlin |
11 |
fastcall void ppc32_lha(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) |
770 |
dpavlin |
7 |
{ |
771 |
|
|
m_uint64_t data; |
772 |
|
|
void *haddr; |
773 |
|
|
|
774 |
dpavlin |
11 |
haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_LHZ,2,MTS_READ,&data); |
775 |
dpavlin |
7 |
if (likely(haddr != NULL)) data = vmtoh16(*(m_uint16_t *)haddr); |
776 |
dpavlin |
11 |
cpu->gpr[reg] = sign_extend_32(data,16); |
777 |
dpavlin |
7 |
} |
778 |
|
|
|
779 |
|
|
/* STB: Store Byte */ |
780 |
dpavlin |
11 |
fastcall void ppc32_stb(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) |
781 |
dpavlin |
7 |
{ |
782 |
|
|
m_uint64_t data; |
783 |
|
|
void *haddr; |
784 |
|
|
|
785 |
|
|
data = cpu->gpr[reg] & 0xff; |
786 |
dpavlin |
11 |
haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_STB,1,MTS_WRITE,&data); |
787 |
dpavlin |
7 |
if (likely(haddr != NULL)) *(m_uint8_t *)haddr = data; |
788 |
|
|
} |
789 |
|
|
|
790 |
|
|
/* STH: Store Half-Word */ |
791 |
dpavlin |
11 |
fastcall void ppc32_sth(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) |
792 |
dpavlin |
7 |
{ |
793 |
|
|
m_uint64_t data; |
794 |
|
|
void *haddr; |
795 |
|
|
|
796 |
|
|
data = cpu->gpr[reg] & 0xffff; |
797 |
dpavlin |
11 |
haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_STH,2,MTS_WRITE,&data); |
798 |
dpavlin |
7 |
if (likely(haddr != NULL)) *(m_uint16_t *)haddr = htovm16(data); |
799 |
|
|
} |
800 |
|
|
|
801 |
|
|
/* STW: Store Word */ |
802 |
dpavlin |
11 |
fastcall void ppc32_stw(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) |
803 |
dpavlin |
7 |
{ |
804 |
|
|
m_uint64_t data; |
805 |
|
|
void *haddr; |
806 |
|
|
|
807 |
|
|
data = cpu->gpr[reg]; |
808 |
dpavlin |
11 |
haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_STW,4,MTS_WRITE,&data); |
809 |
dpavlin |
7 |
if (likely(haddr != NULL)) *(m_uint32_t *)haddr = htovm32(data); |
810 |
|
|
} |
811 |
|
|
|
812 |
|
|
/* STWBR: Store Word Byte Reversed */ |
813 |
dpavlin |
11 |
fastcall void ppc32_stwbr(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) |
814 |
dpavlin |
7 |
{ |
815 |
|
|
m_uint64_t data; |
816 |
|
|
void *haddr; |
817 |
|
|
|
818 |
|
|
data = swap32(cpu->gpr[reg]); |
819 |
dpavlin |
11 |
haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_STWBR,4,MTS_WRITE,&data); |
820 |
dpavlin |
7 |
if (likely(haddr != NULL)) *(m_uint32_t *)haddr = htovm32(data); |
821 |
|
|
} |
822 |
|
|
|
823 |
|
|
/* LSW: Load String Word */ |
824 |
dpavlin |
11 |
fastcall void ppc32_lsw(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) |
825 |
dpavlin |
7 |
{ |
826 |
|
|
m_uint64_t data; |
827 |
|
|
void *haddr; |
828 |
|
|
|
829 |
dpavlin |
11 |
haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_LSW,1,MTS_READ,&data); |
830 |
dpavlin |
7 |
if (likely(haddr != NULL)) data = *(m_uint8_t *)haddr; |
831 |
dpavlin |
11 |
cpu->gpr[reg] |= (data & 0xFF) << (24 - cpu->sw_pos); |
832 |
dpavlin |
7 |
} |
833 |
|
|
|
834 |
|
|
/* STW: Store String Word */ |
835 |
dpavlin |
11 |
fastcall void ppc32_stsw(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) |
836 |
dpavlin |
7 |
{ |
837 |
|
|
m_uint64_t data; |
838 |
|
|
void *haddr; |
839 |
|
|
|
840 |
|
|
data = (cpu->gpr[reg] >> (24 - cpu->sw_pos)) & 0xFF; |
841 |
dpavlin |
11 |
haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_STSW,1,MTS_WRITE,&data); |
842 |
dpavlin |
7 |
if (likely(haddr != NULL)) *(m_uint8_t *)haddr = data; |
843 |
|
|
} |
844 |
|
|
|
845 |
|
|
/* LFD: Load Floating-Point Double */ |
846 |
dpavlin |
11 |
fastcall void ppc32_lfd(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) |
847 |
dpavlin |
7 |
{ |
848 |
|
|
m_uint64_t data; |
849 |
|
|
void *haddr; |
850 |
|
|
|
851 |
dpavlin |
11 |
haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_LWZ,8,MTS_READ,&data); |
852 |
dpavlin |
7 |
if (likely(haddr != NULL)) data = vmtoh64(*(m_uint64_t *)haddr); |
853 |
dpavlin |
11 |
cpu->fpu.reg[reg] = data; |
854 |
dpavlin |
7 |
} |
855 |
|
|
|
856 |
|
|
/* STFD: Store Floating-Point Double */ |
857 |
dpavlin |
11 |
fastcall void ppc32_stfd(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int reg) |
858 |
dpavlin |
7 |
{ |
859 |
|
|
m_uint64_t data; |
860 |
|
|
void *haddr; |
861 |
|
|
|
862 |
|
|
data = cpu->fpu.reg[reg]; |
863 |
dpavlin |
11 |
haddr = PPC32_MEM_DACCESS(cpu,vaddr,PPC_MEMOP_STW,8,MTS_WRITE,&data); |
864 |
dpavlin |
7 |
if (likely(haddr != NULL)) *(m_uint64_t *)haddr = htovm64(data); |
865 |
|
|
} |
866 |
|
|
|
867 |
|
|
/* ICBI: Instruction Cache Block Invalidate */ |
868 |
dpavlin |
11 |
fastcall void ppc32_icbi(cpu_ppc_t *cpu,m_uint32_t vaddr,u_int op) |
869 |
dpavlin |
7 |
{ |
870 |
|
|
ppc32_jit_tcb_t *block; |
871 |
|
|
m_uint32_t phys_page; |
872 |
|
|
|
873 |
|
|
#if DEBUG_ICBI |
874 |
|
|
cpu_log(cpu->gen,"MTS","ICBI: ia=0x%8.8x, vaddr=0x%8.8x\n",cpu->ia,vaddr); |
875 |
|
|
#endif |
876 |
|
|
|
877 |
|
|
if (!cpu->translate(cpu,vaddr,PPC32_MTS_ICACHE,&phys_page)) { |
878 |
dpavlin |
8 |
if (cpu->exec_phys_map) { |
879 |
|
|
block = ppc32_jit_find_by_phys_page(cpu,phys_page); |
880 |
dpavlin |
7 |
|
881 |
dpavlin |
9 |
if (block && (block->start_ia == (vaddr & PPC32_MIN_PAGE_MASK))) { |
882 |
dpavlin |
7 |
#if DEBUG_ICBI |
883 |
dpavlin |
8 |
cpu_log(cpu->gen,"MTS", |
884 |
|
|
"ICBI: removing compiled page at 0x%8.8x, pc=0x%8.8x\n", |
885 |
|
|
block->start_ia,cpu->ia); |
886 |
dpavlin |
7 |
#endif |
887 |
dpavlin |
8 |
ppc32_jit_tcb_free(cpu,block,TRUE); |
888 |
|
|
cpu->exec_blk_map[ppc32_jit_get_ia_hash(vaddr)] = NULL; |
889 |
|
|
} |
890 |
|
|
else |
891 |
|
|
{ |
892 |
dpavlin |
7 |
#if DEBUG_ICBI |
893 |
dpavlin |
8 |
cpu_log(cpu->gen,"MTS", |
894 |
|
|
"ICBI: trying to remove page 0x%llx with pc=0x%llx\n", |
895 |
|
|
block->start_ia,cpu->ia); |
896 |
dpavlin |
7 |
#endif |
897 |
|
|
} |
898 |
|
|
} |
899 |
|
|
} |
900 |
|
|
} |
901 |
|
|
|
902 |
|
|
/* ======================================================================== */ |
903 |
|
|
|
904 |
|
|
/* Get a BAT register pointer given a SPR index */ |
905 |
|
|
static inline m_uint32_t *ppc32_get_bat_spr_ptr(cpu_ppc_t *cpu,u_int spr) |
906 |
|
|
{ |
907 |
|
|
m_uint32_t spr_cat,cid,index; |
908 |
|
|
|
909 |
|
|
spr_cat = spr >> 5; |
910 |
|
|
if ((spr_cat != 0x10) && (spr_cat != 0x11)) |
911 |
|
|
return NULL; |
912 |
|
|
|
913 |
|
|
cid = (spr >> 3) & 0x1; |
914 |
|
|
index = (spr >> 1) & 0x3; |
915 |
|
|
|
916 |
|
|
if (spr & 0x20) |
917 |
|
|
index += 4; |
918 |
|
|
|
919 |
|
|
//printf("GET_BAT_SPR: SPR=%u => cid=%u, index=%u\n",spr,cid,index); |
920 |
|
|
|
921 |
|
|
return(&cpu->bat[cid][index].reg[spr & 0x1]); |
922 |
|
|
} |
923 |
|
|
|
924 |
|
|
/* Get a BAT SPR */ |
925 |
|
|
m_uint32_t ppc32_get_bat_spr(cpu_ppc_t *cpu,u_int spr) |
926 |
|
|
{ |
927 |
|
|
m_uint32_t *p; |
928 |
|
|
|
929 |
|
|
if (!(p = ppc32_get_bat_spr_ptr(cpu,spr))) |
930 |
|
|
return(0); |
931 |
|
|
|
932 |
|
|
return(*p); |
933 |
|
|
} |
934 |
|
|
|
935 |
|
|
/* Set a BAT SPR */ |
936 |
|
|
void ppc32_set_bat_spr(cpu_ppc_t *cpu,u_int spr,m_uint32_t val) |
937 |
|
|
{ |
938 |
|
|
m_uint32_t *p; |
939 |
|
|
|
940 |
|
|
if ((p = ppc32_get_bat_spr_ptr(cpu,spr))) { |
941 |
|
|
*p = val; |
942 |
|
|
ppc32_mem_invalidate_cache(cpu); |
943 |
|
|
} |
944 |
|
|
} |
945 |
|
|
|
946 |
|
|
/* ======================================================================== */ |
947 |
|
|
|
948 |
|
|
/* Rebuild MTS data structures */ |
949 |
|
|
static void ppc32_mem_rebuild_mts(cpu_gen_t *gen_cpu) |
950 |
|
|
{ |
951 |
|
|
ppc32_mem_invalidate_cache(CPU_PPC32(gen_cpu)); |
952 |
|
|
} |
953 |
|
|
|
954 |
|
|
/* Initialize memory access vectors */ |
955 |
|
|
void ppc32_init_memop_vectors(cpu_ppc_t *cpu) |
956 |
|
|
{ |
957 |
|
|
/* MTS slow lookup */ |
958 |
|
|
cpu->mts_slow_lookup = ppc32_slow_lookup; |
959 |
|
|
|
960 |
|
|
/* MTS rebuild */ |
961 |
|
|
cpu->gen->mts_rebuild = ppc32_mem_rebuild_mts; |
962 |
|
|
|
963 |
|
|
/* MTS statistics */ |
964 |
|
|
cpu->gen->mts_show_stats = ppc32_mem_show_stats; |
965 |
|
|
|
966 |
|
|
/* Memory lookup operation */ |
967 |
|
|
cpu->mem_op_lookup = ppc32_mem_lookup; |
968 |
|
|
|
969 |
|
|
/* Translation operation */ |
970 |
|
|
cpu->translate = ppc32_translate; |
971 |
|
|
|
972 |
|
|
/* Load Operations */ |
973 |
|
|
cpu->mem_op_fn[PPC_MEMOP_LBZ] = ppc32_lbz; |
974 |
|
|
cpu->mem_op_fn[PPC_MEMOP_LHZ] = ppc32_lhz; |
975 |
|
|
cpu->mem_op_fn[PPC_MEMOP_LWZ] = ppc32_lwz; |
976 |
|
|
|
977 |
|
|
/* Load Operation with sign-extension */ |
978 |
|
|
cpu->mem_op_fn[PPC_MEMOP_LHA] = ppc32_lha; |
979 |
|
|
|
980 |
|
|
/* Store Operations */ |
981 |
|
|
cpu->mem_op_fn[PPC_MEMOP_STB] = ppc32_stb; |
982 |
|
|
cpu->mem_op_fn[PPC_MEMOP_STH] = ppc32_sth; |
983 |
|
|
cpu->mem_op_fn[PPC_MEMOP_STW] = ppc32_stw; |
984 |
|
|
|
985 |
|
|
/* Byte-Reversed operations */ |
986 |
|
|
cpu->mem_op_fn[PPC_MEMOP_LWBR] = ppc32_lwbr; |
987 |
|
|
cpu->mem_op_fn[PPC_MEMOP_STWBR] = ppc32_stwbr; |
988 |
|
|
|
989 |
|
|
/* String operations */ |
990 |
|
|
cpu->mem_op_fn[PPC_MEMOP_LSW] = ppc32_lsw; |
991 |
|
|
cpu->mem_op_fn[PPC_MEMOP_STSW] = ppc32_stsw; |
992 |
|
|
|
993 |
|
|
/* FPU operations */ |
994 |
|
|
cpu->mem_op_fn[PPC_MEMOP_LFD] = ppc32_lfd; |
995 |
|
|
cpu->mem_op_fn[PPC_MEMOP_STFD] = ppc32_stfd; |
996 |
|
|
|
997 |
|
|
/* ICBI - Instruction Cache Block Invalidate */ |
998 |
|
|
cpu->mem_op_fn[PPC_MEMOP_ICBI] = ppc32_icbi; |
999 |
|
|
} |
1000 |
|
|
|
1001 |
|
|
/* Restart the memory subsystem */ |
1002 |
|
|
int ppc32_mem_restart(cpu_ppc_t *cpu) |
1003 |
|
|
{ |
1004 |
|
|
m_uint32_t family; |
1005 |
|
|
|
1006 |
|
|
ppc32_mem_shutdown(cpu); |
1007 |
|
|
ppc32_mem_init(cpu); |
1008 |
|
|
ppc32_init_memop_vectors(cpu); |
1009 |
|
|
|
1010 |
|
|
/* Override the MTS lookup vector depending on the cpu type */ |
1011 |
|
|
family = cpu->pvr & 0xFFFF0000; |
1012 |
|
|
|
1013 |
|
|
if (family == PPC32_PVR_405) { |
1014 |
|
|
cpu->mts_slow_lookup = ppc405_slow_lookup; |
1015 |
|
|
cpu->gen->mmu_dump = ppc405_dump_tlb; |
1016 |
|
|
cpu->gen->mmu_raw_dump = ppc405_dump_tlb; |
1017 |
|
|
} |
1018 |
|
|
|
1019 |
|
|
return(0); |
1020 |
|
|
} |