diff options
author | Michal Simek <monstr@monstr.eu> | 2009-05-26 10:30:22 -0400 |
---|---|---|
committer | Michal Simek <monstr@monstr.eu> | 2009-05-26 10:45:20 -0400 |
commit | 7db29dde731db02143418cfa008b7b77ccb2fa57 (patch) | |
tree | 198d28ff688f28a129a806b2799015bc63f6cdd8 /arch/microblaze | |
parent | ca54502bd52a5d483f7ba076b613ad2ee43941da (diff) |
microblaze_mmu_v2: Update exception handling - MMU exception
Signed-off-by: Michal Simek <monstr@monstr.eu>
Diffstat (limited to 'arch/microblaze')
-rw-r--r-- | arch/microblaze/kernel/hw_exception_handler.S | 746 |
1 files changed, 739 insertions, 7 deletions
diff --git a/arch/microblaze/kernel/hw_exception_handler.S b/arch/microblaze/kernel/hw_exception_handler.S index cf9486d9983..9d591cd74fc 100644 --- a/arch/microblaze/kernel/hw_exception_handler.S +++ b/arch/microblaze/kernel/hw_exception_handler.S | |||
@@ -53,6 +53,12 @@ | |||
53 | * - Illegal instruction opcode | 53 | * - Illegal instruction opcode |
54 | * - Divide-by-zero | 54 | * - Divide-by-zero |
55 | * | 55 | * |
56 | * - Privileged instruction exception (MMU) | ||
57 | * - Data storage exception (MMU) | ||
58 | * - Instruction storage exception (MMU) | ||
59 | * - Data TLB miss exception (MMU) | ||
60 | * - Instruction TLB miss exception (MMU) | ||
61 | * | ||
56 | * Note we disable interrupts during exception handling, otherwise we will | 62 | * Note we disable interrupts during exception handling, otherwise we will |
57 | * possibly get multiple re-entrancy if interrupt handles themselves cause | 63 | * possibly get multiple re-entrancy if interrupt handles themselves cause |
58 | * exceptions. JW | 64 | * exceptions. JW |
@@ -71,9 +77,24 @@ | |||
71 | #include <asm/asm-offsets.h> | 77 | #include <asm/asm-offsets.h> |
72 | 78 | ||
73 | /* Helpful Macros */ | 79 | /* Helpful Macros */ |
80 | #ifndef CONFIG_MMU | ||
74 | #define EX_HANDLER_STACK_SIZ (4*19) | 81 | #define EX_HANDLER_STACK_SIZ (4*19) |
82 | #endif | ||
75 | #define NUM_TO_REG(num) r ## num | 83 | #define NUM_TO_REG(num) r ## num |
76 | 84 | ||
85 | #ifdef CONFIG_MMU | ||
86 | /* FIXME you can't change first load of MSR because there is | ||
87 | * hardcoded jump bri 4 */ | ||
88 | #define RESTORE_STATE \ | ||
89 | lwi r3, r1, PT_R3; \ | ||
90 | lwi r4, r1, PT_R4; \ | ||
91 | lwi r5, r1, PT_R5; \ | ||
92 | lwi r6, r1, PT_R6; \ | ||
93 | lwi r11, r1, PT_R11; \ | ||
94 | lwi r31, r1, PT_R31; \ | ||
95 | lwi r1, r0, TOPHYS(r0_ram + 0); | ||
96 | #endif /* CONFIG_MMU */ | ||
97 | |||
77 | #define LWREG_NOP \ | 98 | #define LWREG_NOP \ |
78 | bri ex_handler_unhandled; \ | 99 | bri ex_handler_unhandled; \ |
79 | nop; | 100 | nop; |
@@ -106,6 +127,54 @@ | |||
106 | or r3, r0, NUM_TO_REG (regnum); \ | 127 | or r3, r0, NUM_TO_REG (regnum); \ |
107 | bri ex_sw_tail; | 128 | bri ex_sw_tail; |
108 | 129 | ||
130 | #ifdef CONFIG_MMU | ||
131 | #define R3_TO_LWREG_VM_V(regnum) \ | ||
132 | brid ex_lw_end_vm; \ | ||
133 | swi r3, r7, 4 * regnum; | ||
134 | |||
135 | #define R3_TO_LWREG_VM(regnum) \ | ||
136 | brid ex_lw_end_vm; \ | ||
137 | or NUM_TO_REG (regnum), r0, r3; | ||
138 | |||
139 | #define SWREG_TO_R3_VM_V(regnum) \ | ||
140 | brid ex_sw_tail_vm; \ | ||
141 | lwi r3, r7, 4 * regnum; | ||
142 | |||
143 | #define SWREG_TO_R3_VM(regnum) \ | ||
144 | brid ex_sw_tail_vm; \ | ||
145 | or r3, r0, NUM_TO_REG (regnum); | ||
146 | |||
147 | /* Shift right instruction depending on available configuration */ | ||
148 | #if CONFIG_XILINX_MICROBLAZE0_USE_BARREL > 0 | ||
149 | #define BSRLI(rD, rA, imm) \ | ||
150 | bsrli rD, rA, imm | ||
151 | #elif CONFIG_XILINX_MICROBLAZE0_USE_DIV > 0 | ||
152 | #define BSRLI(rD, rA, imm) \ | ||
153 | ori rD, r0, (1 << imm); \ | ||
154 | idivu rD, rD, rA | ||
155 | #else | ||
156 | #define BSRLI(rD, rA, imm) BSRLI ## imm (rD, rA) | ||
157 | /* Only the used shift constants defined here - add more if needed */ | ||
158 | #define BSRLI2(rD, rA) \ | ||
159 | srl rD, rA; /* << 1 */ \ | ||
160 | srl rD, rD; /* << 2 */ | ||
161 | #define BSRLI10(rD, rA) \ | ||
162 | srl rD, rA; /* << 1 */ \ | ||
163 | srl rD, rD; /* << 2 */ \ | ||
164 | srl rD, rD; /* << 3 */ \ | ||
165 | srl rD, rD; /* << 4 */ \ | ||
166 | srl rD, rD; /* << 5 */ \ | ||
167 | srl rD, rD; /* << 6 */ \ | ||
168 | srl rD, rD; /* << 7 */ \ | ||
169 | srl rD, rD; /* << 8 */ \ | ||
170 | srl rD, rD; /* << 9 */ \ | ||
171 | srl rD, rD /* << 10 */ | ||
172 | #define BSRLI20(rD, rA) \ | ||
173 | BSRLI10(rD, rA); \ | ||
174 | BSRLI10(rD, rD) | ||
175 | #endif | ||
176 | #endif /* CONFIG_MMU */ | ||
177 | |||
109 | .extern other_exception_handler /* Defined in exception.c */ | 178 | .extern other_exception_handler /* Defined in exception.c */ |
110 | 179 | ||
111 | /* | 180 | /* |
@@ -163,34 +232,119 @@ | |||
163 | 232 | ||
164 | /* wrappers to restore state before coming to entry.S */ | 233 | /* wrappers to restore state before coming to entry.S */ |
165 | 234 | ||
235 | #ifdef CONFIG_MMU | ||
236 | .section .rodata | ||
237 | .align 4 | ||
238 | _MB_HW_ExceptionVectorTable: | ||
239 | /* 0 - Undefined */ | ||
240 | .long TOPHYS(ex_handler_unhandled) | ||
241 | /* 1 - Unaligned data access exception */ | ||
242 | .long TOPHYS(handle_unaligned_ex) | ||
243 | /* 2 - Illegal op-code exception */ | ||
244 | .long TOPHYS(full_exception_trapw) | ||
245 | /* 3 - Instruction bus error exception */ | ||
246 | .long TOPHYS(full_exception_trapw) | ||
247 | /* 4 - Data bus error exception */ | ||
248 | .long TOPHYS(full_exception_trapw) | ||
249 | /* 5 - Divide by zero exception */ | ||
250 | .long TOPHYS(full_exception_trapw) | ||
251 | /* 6 - Floating point unit exception */ | ||
252 | .long TOPHYS(full_exception_trapw) | ||
253 | /* 7 - Privileged instruction exception */ | ||
254 | .long TOPHYS(full_exception_trapw) | ||
255 | /* 8 - 15 - Undefined */ | ||
256 | .long TOPHYS(ex_handler_unhandled) | ||
257 | .long TOPHYS(ex_handler_unhandled) | ||
258 | .long TOPHYS(ex_handler_unhandled) | ||
259 | .long TOPHYS(ex_handler_unhandled) | ||
260 | .long TOPHYS(ex_handler_unhandled) | ||
261 | .long TOPHYS(ex_handler_unhandled) | ||
262 | .long TOPHYS(ex_handler_unhandled) | ||
263 | .long TOPHYS(ex_handler_unhandled) | ||
264 | /* 16 - Data storage exception */ | ||
265 | .long TOPHYS(handle_data_storage_exception) | ||
266 | /* 17 - Instruction storage exception */ | ||
267 | .long TOPHYS(handle_instruction_storage_exception) | ||
268 | /* 18 - Data TLB miss exception */ | ||
269 | .long TOPHYS(handle_data_tlb_miss_exception) | ||
270 | /* 19 - Instruction TLB miss exception */ | ||
271 | .long TOPHYS(handle_instruction_tlb_miss_exception) | ||
272 | /* 20 - 31 - Undefined */ | ||
273 | .long TOPHYS(ex_handler_unhandled) | ||
274 | .long TOPHYS(ex_handler_unhandled) | ||
275 | .long TOPHYS(ex_handler_unhandled) | ||
276 | .long TOPHYS(ex_handler_unhandled) | ||
277 | .long TOPHYS(ex_handler_unhandled) | ||
278 | .long TOPHYS(ex_handler_unhandled) | ||
279 | .long TOPHYS(ex_handler_unhandled) | ||
280 | .long TOPHYS(ex_handler_unhandled) | ||
281 | .long TOPHYS(ex_handler_unhandled) | ||
282 | .long TOPHYS(ex_handler_unhandled) | ||
283 | .long TOPHYS(ex_handler_unhandled) | ||
284 | .long TOPHYS(ex_handler_unhandled) | ||
285 | #endif | ||
286 | |||
166 | .global _hw_exception_handler | 287 | .global _hw_exception_handler |
167 | .section .text | 288 | .section .text |
168 | .align 4 | 289 | .align 4 |
169 | .ent _hw_exception_handler | 290 | .ent _hw_exception_handler |
170 | _hw_exception_handler: | 291 | _hw_exception_handler: |
292 | #ifndef CONFIG_MMU | ||
171 | addik r1, r1, -(EX_HANDLER_STACK_SIZ); /* Create stack frame */ | 293 | addik r1, r1, -(EX_HANDLER_STACK_SIZ); /* Create stack frame */ |
294 | #else | ||
295 | swi r1, r0, TOPHYS(r0_ram + 0); /* GET_SP */ | ||
296 | /* Save date to kernel memory. Here is the problem | ||
297 | * when you came from user space */ | ||
298 | ori r1, r0, TOPHYS(r0_ram + 28); | ||
299 | #endif | ||
172 | swi r3, r1, PT_R3 | 300 | swi r3, r1, PT_R3 |
173 | swi r4, r1, PT_R4 | 301 | swi r4, r1, PT_R4 |
174 | swi r5, r1, PT_R5 | 302 | swi r5, r1, PT_R5 |
175 | swi r6, r1, PT_R6 | 303 | swi r6, r1, PT_R6 |
176 | 304 | ||
177 | mfs r5, rmsr; | 305 | #ifdef CONFIG_MMU |
178 | nop | 306 | swi r11, r1, PT_R11 |
179 | swi r5, r1, 0; | 307 | swi r31, r1, PT_R31 |
180 | mfs r4, rbtr /* Save BTR before jumping to handler */ | 308 | lwi r31, r0, TOPHYS(PER_CPU(CURRENT_SAVE)) /* get saved current */ |
181 | nop | 309 | #endif |
310 | |||
182 | mfs r3, resr | 311 | mfs r3, resr |
183 | nop | 312 | nop |
313 | mfs r4, rear; | ||
314 | nop | ||
184 | 315 | ||
316 | #ifndef CONFIG_MMU | ||
185 | andi r5, r3, 0x1000; /* Check ESR[DS] */ | 317 | andi r5, r3, 0x1000; /* Check ESR[DS] */ |
186 | beqi r5, not_in_delay_slot; /* Branch if ESR[DS] not set */ | 318 | beqi r5, not_in_delay_slot; /* Branch if ESR[DS] not set */ |
187 | mfs r17, rbtr; /* ESR[DS] set - return address in BTR */ | 319 | mfs r17, rbtr; /* ESR[DS] set - return address in BTR */ |
188 | nop | 320 | nop |
189 | not_in_delay_slot: | 321 | not_in_delay_slot: |
190 | swi r17, r1, PT_R17 | 322 | swi r17, r1, PT_R17 |
323 | #endif | ||
191 | 324 | ||
192 | andi r5, r3, 0x1F; /* Extract ESR[EXC] */ | 325 | andi r5, r3, 0x1F; /* Extract ESR[EXC] */ |
193 | 326 | ||
327 | #ifdef CONFIG_MMU | ||
328 | /* Calculate exception vector offset = r5 << 2 */ | ||
329 | addk r6, r5, r5; /* << 1 */ | ||
330 | addk r6, r6, r6; /* << 2 */ | ||
331 | |||
332 | /* counting which exception happen */ | ||
333 | lwi r5, r0, 0x200 + TOPHYS(r0_ram) | ||
334 | addi r5, r5, 1 | ||
335 | swi r5, r0, 0x200 + TOPHYS(r0_ram) | ||
336 | lwi r5, r6, 0x200 + TOPHYS(r0_ram) | ||
337 | addi r5, r5, 1 | ||
338 | swi r5, r6, 0x200 + TOPHYS(r0_ram) | ||
339 | /* end */ | ||
340 | /* Load the HW Exception vector */ | ||
341 | lwi r6, r6, TOPHYS(_MB_HW_ExceptionVectorTable) | ||
342 | bra r6 | ||
343 | |||
344 | full_exception_trapw: | ||
345 | RESTORE_STATE | ||
346 | bri full_exception_trap | ||
347 | #else | ||
194 | /* Exceptions enabled here. This will allow nested exceptions */ | 348 | /* Exceptions enabled here. This will allow nested exceptions */ |
195 | mfs r6, rmsr; | 349 | mfs r6, rmsr; |
196 | nop | 350 | nop |
@@ -254,6 +408,7 @@ handle_other_ex: /* Handle Other exceptions here */ | |||
254 | lwi r18, r1, PT_R18 | 408 | lwi r18, r1, PT_R18 |
255 | 409 | ||
256 | bri ex_handler_done; /* Complete exception handling */ | 410 | bri ex_handler_done; /* Complete exception handling */ |
411 | #endif | ||
257 | 412 | ||
258 | /* 0x01 - Unaligned data access exception | 413 | /* 0x01 - Unaligned data access exception |
259 | * This occurs when a word access is not aligned on a word boundary, | 414 | * This occurs when a word access is not aligned on a word boundary, |
@@ -265,11 +420,28 @@ handle_other_ex: /* Handle Other exceptions here */ | |||
265 | handle_unaligned_ex: | 420 | handle_unaligned_ex: |
266 | /* Working registers already saved: R3, R4, R5, R6 | 421 | /* Working registers already saved: R3, R4, R5, R6 |
267 | * R3 = ESR | 422 | * R3 = ESR |
268 | * R4 = BTR | 423 | * R4 = EAR |
269 | */ | 424 | */ |
270 | mfs r4, rear; | 425 | #ifdef CONFIG_MMU |
426 | andi r6, r3, 0x1000 /* Check ESR[DS] */ | ||
427 | beqi r6, _no_delayslot /* Branch if ESR[DS] not set */ | ||
428 | mfs r17, rbtr; /* ESR[DS] set - return address in BTR */ | ||
271 | nop | 429 | nop |
430 | _no_delayslot: | ||
431 | #endif | ||
432 | |||
433 | #ifdef CONFIG_MMU | ||
434 | /* Check if unaligned address is last on a 4k page */ | ||
435 | andi r5, r4, 0xffc | ||
436 | xori r5, r5, 0xffc | ||
437 | bnei r5, _unaligned_ex2 | ||
438 | _unaligned_ex1: | ||
439 | RESTORE_STATE; | ||
440 | /* Another page must be accessed or physical address not in page table */ | ||
441 | bri unaligned_data_trap | ||
272 | 442 | ||
443 | _unaligned_ex2: | ||
444 | #endif | ||
273 | andi r6, r3, 0x3E0; /* Mask and extract the register operand */ | 445 | andi r6, r3, 0x3E0; /* Mask and extract the register operand */ |
274 | srl r6, r6; /* r6 >> 5 */ | 446 | srl r6, r6; /* r6 >> 5 */ |
275 | srl r6, r6; | 447 | srl r6, r6; |
@@ -278,6 +450,45 @@ handle_unaligned_ex: | |||
278 | srl r6, r6; | 450 | srl r6, r6; |
279 | /* Store the register operand in a temporary location */ | 451 | /* Store the register operand in a temporary location */ |
280 | sbi r6, r0, TOPHYS(ex_reg_op); | 452 | sbi r6, r0, TOPHYS(ex_reg_op); |
453 | #ifdef CONFIG_MMU | ||
454 | /* Get physical address */ | ||
455 | /* If we are faulting a kernel address, we have to use the | ||
456 | * kernel page tables. | ||
457 | */ | ||
458 | ori r5, r0, CONFIG_KERNEL_START | ||
459 | cmpu r5, r4, r5 | ||
460 | bgti r5, _unaligned_ex3 | ||
461 | ori r5, r0, swapper_pg_dir | ||
462 | bri _unaligned_ex4 | ||
463 | |||
464 | /* Get the PGD for the current thread. */ | ||
465 | _unaligned_ex3: /* user thread */ | ||
466 | addi r5 ,CURRENT_TASK, TOPHYS(0); /* get current task address */ | ||
467 | lwi r5, r5, TASK_THREAD + PGDIR | ||
468 | _unaligned_ex4: | ||
469 | tophys(r5,r5) | ||
470 | BSRLI(r6,r4,20) /* Create L1 (pgdir/pmd) address */ | ||
471 | andi r6, r6, 0xffc | ||
472 | /* Assume pgdir aligned on 4K boundary, no need for "andi r5,r5,0xfffff003" */ | ||
473 | or r5, r5, r6 | ||
474 | lwi r6, r5, 0 /* Get L1 entry */ | ||
475 | andi r5, r6, 0xfffff000 /* Extract L2 (pte) base address. */ | ||
476 | beqi r5, _unaligned_ex1 /* Bail if no table */ | ||
477 | |||
478 | tophys(r5,r5) | ||
479 | BSRLI(r6,r4,10) /* Compute PTE address */ | ||
480 | andi r6, r6, 0xffc | ||
481 | andi r5, r5, 0xfffff003 | ||
482 | or r5, r5, r6 | ||
483 | lwi r5, r5, 0 /* Get Linux PTE */ | ||
484 | |||
485 | andi r6, r5, _PAGE_PRESENT | ||
486 | beqi r6, _unaligned_ex1 /* Bail if no page */ | ||
487 | |||
488 | andi r5, r5, 0xfffff000 /* Extract RPN */ | ||
489 | andi r4, r4, 0x00000fff /* Extract offset */ | ||
490 | or r4, r4, r5 /* Create physical address */ | ||
491 | #endif /* CONFIG_MMU */ | ||
281 | 492 | ||
282 | andi r6, r3, 0x400; /* Extract ESR[S] */ | 493 | andi r6, r3, 0x400; /* Extract ESR[S] */ |
283 | bnei r6, ex_sw; | 494 | bnei r6, ex_sw; |
@@ -355,6 +566,7 @@ ex_shw: | |||
355 | ex_sw_end: /* Exception handling of store word, ends. */ | 566 | ex_sw_end: /* Exception handling of store word, ends. */ |
356 | 567 | ||
357 | ex_handler_done: | 568 | ex_handler_done: |
569 | #ifndef CONFIG_MMU | ||
358 | lwi r5, r1, 0 /* RMSR */ | 570 | lwi r5, r1, 0 /* RMSR */ |
359 | mts rmsr, r5 | 571 | mts rmsr, r5 |
360 | nop | 572 | nop |
@@ -366,13 +578,455 @@ ex_handler_done: | |||
366 | 578 | ||
367 | rted r17, 0 | 579 | rted r17, 0 |
368 | addik r1, r1, (EX_HANDLER_STACK_SIZ); /* Restore stack frame */ | 580 | addik r1, r1, (EX_HANDLER_STACK_SIZ); /* Restore stack frame */ |
581 | #else | ||
582 | RESTORE_STATE; | ||
583 | rted r17, 0 | ||
584 | nop | ||
585 | #endif | ||
586 | |||
587 | #ifdef CONFIG_MMU | ||
588 | /* Exception vector entry code. This code runs with address translation | ||
589 | * turned off (i.e. using physical addresses). */ | ||
590 | |||
591 | /* Exception vectors. */ | ||
592 | |||
593 | /* 0x10 - Data Storage Exception | ||
594 | * This happens for just a few reasons. U0 set (but we don't do that), | ||
595 | * or zone protection fault (user violation, write to protected page). | ||
596 | * If this is just an update of modified status, we do that quickly | ||
597 | * and exit. Otherwise, we call heavyweight functions to do the work. | ||
598 | */ | ||
599 | handle_data_storage_exception: | ||
600 | /* Working registers already saved: R3, R4, R5, R6 | ||
601 | * R3 = ESR | ||
602 | */ | ||
603 | mfs r11, rpid | ||
604 | nop | ||
605 | bri 4 | ||
606 | mfs r3, rear /* Get faulting address */ | ||
607 | nop | ||
608 | /* If we are faulting a kernel address, we have to use the | ||
609 | * kernel page tables. | ||
610 | */ | ||
611 | ori r4, r0, CONFIG_KERNEL_START | ||
612 | cmpu r4, r3, r4 | ||
613 | bgti r4, ex3 | ||
614 | /* First, check if it was a zone fault (which means a user | ||
615 | * tried to access a kernel or read-protected page - always | ||
616 | * a SEGV). All other faults here must be stores, so no | ||
617 | * need to check ESR_S as well. */ | ||
618 | mfs r4, resr | ||
619 | nop | ||
620 | andi r4, r4, 0x800 /* ESR_Z - zone protection */ | ||
621 | bnei r4, ex2 | ||
622 | |||
623 | ori r4, r0, swapper_pg_dir | ||
624 | mts rpid, r0 /* TLB will have 0 TID */ | ||
625 | nop | ||
626 | bri ex4 | ||
627 | |||
628 | /* Get the PGD for the current thread. */ | ||
629 | ex3: | ||
630 | /* First, check if it was a zone fault (which means a user | ||
631 | * tried to access a kernel or read-protected page - always | ||
632 | * a SEGV). All other faults here must be stores, so no | ||
633 | * need to check ESR_S as well. */ | ||
634 | mfs r4, resr | ||
635 | nop | ||
636 | andi r4, r4, 0x800 /* ESR_Z */ | ||
637 | bnei r4, ex2 | ||
638 | /* get current task address */ | ||
639 | addi r4 ,CURRENT_TASK, TOPHYS(0); | ||
640 | lwi r4, r4, TASK_THREAD+PGDIR | ||
641 | ex4: | ||
642 | tophys(r4,r4) | ||
643 | BSRLI(r5,r3,20) /* Create L1 (pgdir/pmd) address */ | ||
644 | andi r5, r5, 0xffc | ||
645 | /* Assume pgdir aligned on 4K boundary, no need for "andi r4,r4,0xfffff003" */ | ||
646 | or r4, r4, r5 | ||
647 | lwi r4, r4, 0 /* Get L1 entry */ | ||
648 | andi r5, r4, 0xfffff000 /* Extract L2 (pte) base address */ | ||
649 | beqi r5, ex2 /* Bail if no table */ | ||
650 | |||
651 | tophys(r5,r5) | ||
652 | BSRLI(r6,r3,10) /* Compute PTE address */ | ||
653 | andi r6, r6, 0xffc | ||
654 | andi r5, r5, 0xfffff003 | ||
655 | or r5, r5, r6 | ||
656 | lwi r4, r5, 0 /* Get Linux PTE */ | ||
657 | |||
658 | andi r6, r4, _PAGE_RW /* Is it writeable? */ | ||
659 | beqi r6, ex2 /* Bail if not */ | ||
660 | |||
661 | /* Update 'changed' */ | ||
662 | ori r4, r4, _PAGE_DIRTY|_PAGE_ACCESSED|_PAGE_HWWRITE | ||
663 | swi r4, r5, 0 /* Update Linux page table */ | ||
664 | |||
665 | /* Most of the Linux PTE is ready to load into the TLB LO. | ||
666 | * We set ZSEL, where only the LS-bit determines user access. | ||
667 | * We set execute, because we don't have the granularity to | ||
668 | * properly set this at the page level (Linux problem). | ||
669 | * If shared is set, we cause a zero PID->TID load. | ||
670 | * Many of these bits are software only. Bits we don't set | ||
671 | * here we (properly should) assume have the appropriate value. | ||
672 | */ | ||
673 | andni r4, r4, 0x0ce2 /* Make sure 20, 21 are zero */ | ||
674 | ori r4, r4, _PAGE_HWEXEC /* make it executable */ | ||
675 | |||
676 | /* find the TLB index that caused the fault. It has to be here*/ | ||
677 | mts rtlbsx, r3 | ||
678 | nop | ||
679 | mfs r5, rtlbx /* DEBUG: TBD */ | ||
680 | nop | ||
681 | mts rtlblo, r4 /* Load TLB LO */ | ||
682 | nop | ||
683 | /* Will sync shadow TLBs */ | ||
684 | |||
685 | /* Done...restore registers and get out of here. */ | ||
686 | mts rpid, r11 | ||
687 | nop | ||
688 | bri 4 | ||
689 | |||
690 | RESTORE_STATE; | ||
691 | rted r17, 0 | ||
692 | nop | ||
693 | ex2: | ||
694 | /* The bailout. Restore registers to pre-exception conditions | ||
695 | * and call the heavyweights to help us out. */ | ||
696 | mts rpid, r11 | ||
697 | nop | ||
698 | bri 4 | ||
699 | RESTORE_STATE; | ||
700 | bri page_fault_data_trap | ||
701 | |||
702 | |||
703 | /* 0x11 - Instruction Storage Exception | ||
704 | * This is caused by a fetch from non-execute or guarded pages. */ | ||
705 | handle_instruction_storage_exception: | ||
706 | /* Working registers already saved: R3, R4, R5, R6 | ||
707 | * R3 = ESR | ||
708 | */ | ||
709 | |||
710 | mfs r3, rear /* Get faulting address */ | ||
711 | nop | ||
712 | RESTORE_STATE; | ||
713 | bri page_fault_instr_trap | ||
714 | |||
715 | /* 0x12 - Data TLB Miss Exception | ||
716 | * As the name implies, translation is not in the MMU, so search the | ||
717 | * page tables and fix it. The only purpose of this function is to | ||
718 | * load TLB entries from the page table if they exist. | ||
719 | */ | ||
720 | handle_data_tlb_miss_exception: | ||
721 | /* Working registers already saved: R3, R4, R5, R6 | ||
722 | * R3 = ESR | ||
723 | */ | ||
724 | mfs r11, rpid | ||
725 | nop | ||
726 | bri 4 | ||
727 | mfs r3, rear /* Get faulting address */ | ||
728 | nop | ||
729 | |||
730 | /* If we are faulting a kernel address, we have to use the | ||
731 | * kernel page tables. */ | ||
732 | ori r4, r0, CONFIG_KERNEL_START | ||
733 | cmpu r4, r3, r4 | ||
734 | bgti r4, ex5 | ||
735 | ori r4, r0, swapper_pg_dir | ||
736 | mts rpid, r0 /* TLB will have 0 TID */ | ||
737 | nop | ||
738 | bri ex6 | ||
369 | 739 | ||
740 | /* Get the PGD for the current thread. */ | ||
741 | ex5: | ||
742 | /* get current task address */ | ||
743 | addi r4 ,CURRENT_TASK, TOPHYS(0); | ||
744 | lwi r4, r4, TASK_THREAD+PGDIR | ||
745 | ex6: | ||
746 | tophys(r4,r4) | ||
747 | BSRLI(r5,r3,20) /* Create L1 (pgdir/pmd) address */ | ||
748 | andi r5, r5, 0xffc | ||
749 | /* Assume pgdir aligned on 4K boundary, no need for "andi r4,r4,0xfffff003" */ | ||
750 | or r4, r4, r5 | ||
751 | lwi r4, r4, 0 /* Get L1 entry */ | ||
752 | andi r5, r4, 0xfffff000 /* Extract L2 (pte) base address */ | ||
753 | beqi r5, ex7 /* Bail if no table */ | ||
754 | |||
755 | tophys(r5,r5) | ||
756 | BSRLI(r6,r3,10) /* Compute PTE address */ | ||
757 | andi r6, r6, 0xffc | ||
758 | andi r5, r5, 0xfffff003 | ||
759 | or r5, r5, r6 | ||
760 | lwi r4, r5, 0 /* Get Linux PTE */ | ||
761 | |||
762 | andi r6, r4, _PAGE_PRESENT | ||
763 | beqi r6, ex7 | ||
764 | |||
765 | ori r4, r4, _PAGE_ACCESSED | ||
766 | swi r4, r5, 0 | ||
767 | |||
768 | /* Most of the Linux PTE is ready to load into the TLB LO. | ||
769 | * We set ZSEL, where only the LS-bit determines user access. | ||
770 | * We set execute, because we don't have the granularity to | ||
771 | * properly set this at the page level (Linux problem). | ||
772 | * If shared is set, we cause a zero PID->TID load. | ||
773 | * Many of these bits are software only. Bits we don't set | ||
774 | * here we (properly should) assume have the appropriate value. | ||
775 | */ | ||
776 | andni r4, r4, 0x0ce2 /* Make sure 20, 21 are zero */ | ||
777 | |||
778 | bri finish_tlb_load | ||
779 | ex7: | ||
780 | /* The bailout. Restore registers to pre-exception conditions | ||
781 | * and call the heavyweights to help us out. | ||
782 | */ | ||
783 | mts rpid, r11 | ||
784 | nop | ||
785 | bri 4 | ||
786 | RESTORE_STATE; | ||
787 | bri page_fault_data_trap | ||
788 | |||
789 | /* 0x13 - Instruction TLB Miss Exception | ||
790 | * Nearly the same as above, except we get our information from | ||
791 | * different registers and bailout to a different point. | ||
792 | */ | ||
793 | handle_instruction_tlb_miss_exception: | ||
794 | /* Working registers already saved: R3, R4, R5, R6 | ||
795 | * R3 = ESR | ||
796 | */ | ||
797 | mfs r11, rpid | ||
798 | nop | ||
799 | bri 4 | ||
800 | mfs r3, rear /* Get faulting address */ | ||
801 | nop | ||
802 | |||
803 | /* If we are faulting a kernel address, we have to use the | ||
804 | * kernel page tables. | ||
805 | */ | ||
806 | ori r4, r0, CONFIG_KERNEL_START | ||
807 | cmpu r4, r3, r4 | ||
808 | bgti r4, ex8 | ||
809 | ori r4, r0, swapper_pg_dir | ||
810 | mts rpid, r0 /* TLB will have 0 TID */ | ||
811 | nop | ||
812 | bri ex9 | ||
813 | |||
814 | /* Get the PGD for the current thread. */ | ||
815 | ex8: | ||
816 | /* get current task address */ | ||
817 | addi r4 ,CURRENT_TASK, TOPHYS(0); | ||
818 | lwi r4, r4, TASK_THREAD+PGDIR | ||
819 | ex9: | ||
820 | tophys(r4,r4) | ||
821 | BSRLI(r5,r3,20) /* Create L1 (pgdir/pmd) address */ | ||
822 | andi r5, r5, 0xffc | ||
823 | /* Assume pgdir aligned on 4K boundary, no need for "andi r4,r4,0xfffff003" */ | ||
824 | or r4, r4, r5 | ||
825 | lwi r4, r4, 0 /* Get L1 entry */ | ||
826 | andi r5, r4, 0xfffff000 /* Extract L2 (pte) base address */ | ||
827 | beqi r5, ex10 /* Bail if no table */ | ||
828 | |||
829 | tophys(r5,r5) | ||
830 | BSRLI(r6,r3,10) /* Compute PTE address */ | ||
831 | andi r6, r6, 0xffc | ||
832 | andi r5, r5, 0xfffff003 | ||
833 | or r5, r5, r6 | ||
834 | lwi r4, r5, 0 /* Get Linux PTE */ | ||
835 | |||
836 | andi r6, r4, _PAGE_PRESENT | ||
837 | beqi r6, ex7 | ||
838 | |||
839 | ori r4, r4, _PAGE_ACCESSED | ||
840 | swi r4, r5, 0 | ||
841 | |||
842 | /* Most of the Linux PTE is ready to load into the TLB LO. | ||
843 | * We set ZSEL, where only the LS-bit determines user access. | ||
844 | * We set execute, because we don't have the granularity to | ||
845 | * properly set this at the page level (Linux problem). | ||
846 | * If shared is set, we cause a zero PID->TID load. | ||
847 | * Many of these bits are software only. Bits we don't set | ||
848 | * here we (properly should) assume have the appropriate value. | ||
849 | */ | ||
850 | andni r4, r4, 0x0ce2 /* Make sure 20, 21 are zero */ | ||
851 | |||
852 | bri finish_tlb_load | ||
853 | ex10: | ||
854 | /* The bailout. Restore registers to pre-exception conditions | ||
855 | * and call the heavyweights to help us out. | ||
856 | */ | ||
857 | mts rpid, r11 | ||
858 | nop | ||
859 | bri 4 | ||
860 | RESTORE_STATE; | ||
861 | bri page_fault_instr_trap | ||
862 | |||
863 | /* Both the instruction and data TLB miss get to this point to load the TLB. | ||
864 | * r3 - EA of fault | ||
865 | * r4 - TLB LO (info from Linux PTE) | ||
866 | * r5, r6 - available to use | ||
867 | * PID - loaded with proper value when we get here | ||
868 | * Upon exit, we reload everything and RFI. | ||
869 | * A common place to load the TLB. | ||
870 | */ | ||
871 | tlb_index: | ||
872 | .long 1 /* MS: storing last used tlb index */ | ||
873 | finish_tlb_load: | ||
874 | /* MS: load the last used TLB index. */ | ||
875 | lwi r5, r0, TOPHYS(tlb_index) | ||
876 | addik r5, r5, 1 /* MS: inc tlb_index -> use next one */ | ||
877 | |||
878 | /* MS: FIXME this is potential fault, because this is mask not count */ | ||
879 | andi r5, r5, (MICROBLAZE_TLB_SIZE-1) | ||
880 | ori r6, r0, 1 | ||
881 | cmp r31, r5, r6 | ||
882 | blti r31, sem | ||
883 | addik r5, r6, 1 | ||
884 | sem: | ||
885 | /* MS: save back current TLB index */ | ||
886 | swi r5, r0, TOPHYS(tlb_index) | ||
887 | |||
888 | ori r4, r4, _PAGE_HWEXEC /* make it executable */ | ||
889 | mts rtlbx, r5 /* MS: save current TLB */ | ||
890 | nop | ||
891 | mts rtlblo, r4 /* MS: save to TLB LO */ | ||
892 | nop | ||
893 | |||
894 | /* Create EPN. This is the faulting address plus a static | ||
895 | * set of bits. These are size, valid, E, U0, and ensure | ||
896 | * bits 20 and 21 are zero. | ||
897 | */ | ||
898 | andi r3, r3, 0xfffff000 | ||
899 | ori r3, r3, 0x0c0 | ||
900 | mts rtlbhi, r3 /* Load TLB HI */ | ||
901 | nop | ||
902 | |||
903 | /* Done...restore registers and get out of here. */ | ||
904 | ex12: | ||
905 | mts rpid, r11 | ||
906 | nop | ||
907 | bri 4 | ||
908 | RESTORE_STATE; | ||
909 | rted r17, 0 | ||
910 | nop | ||
911 | |||
912 | /* extern void giveup_fpu(struct task_struct *prev) | ||
913 | * | ||
914 | * The MicroBlaze processor may have an FPU, so this should not just | ||
915 | * return: TBD. | ||
916 | */ | ||
917 | .globl giveup_fpu; | ||
918 | .align 4; | ||
919 | giveup_fpu: | ||
920 | bralid r15,0 /* TBD */ | ||
921 | nop | ||
922 | |||
923 | /* At present, this routine just hangs. - extern void abort(void) */ | ||
924 | .globl abort; | ||
925 | .align 4; | ||
926 | abort: | ||
927 | br r0 | ||
928 | |||
929 | .globl set_context; | ||
930 | .align 4; | ||
931 | set_context: | ||
932 | mts rpid, r5 /* Shadow TLBs are automatically */ | ||
933 | nop | ||
934 | bri 4 /* flushed by changing PID */ | ||
935 | rtsd r15,8 | ||
936 | nop | ||
937 | |||
938 | #endif | ||
370 | .end _hw_exception_handler | 939 | .end _hw_exception_handler |
371 | 940 | ||
941 | #ifdef CONFIG_MMU | ||
942 | /* Unaligned data access exception last on a 4k page for MMU. | ||
943 | * When this is called, we are in virtual mode with exceptions enabled | ||
944 | * and registers 1-13,15,17,18 saved. | ||
945 | * | ||
946 | * R3 = ESR | ||
947 | * R4 = EAR | ||
948 | * R7 = pointer to saved registers (struct pt_regs *regs) | ||
949 | * | ||
950 | * This handler perform the access, and returns via ret_from_exc. | ||
951 | */ | ||
952 | .global _unaligned_data_exception | ||
953 | .ent _unaligned_data_exception | ||
954 | _unaligned_data_exception: | ||
955 | andi r8, r3, 0x3E0; /* Mask and extract the register operand */ | ||
956 | BSRLI(r8,r8,2); /* r8 >> 2 = register operand * 8 */ | ||
957 | andi r6, r3, 0x400; /* Extract ESR[S] */ | ||
958 | bneid r6, ex_sw_vm; | ||
959 | andi r6, r3, 0x800; /* Extract ESR[W] - delay slot */ | ||
960 | ex_lw_vm: | ||
961 | beqid r6, ex_lhw_vm; | ||
962 | lbui r5, r4, 0; /* Exception address in r4 - delay slot */ | ||
963 | /* Load a word, byte-by-byte from destination address and save it in tmp space*/ | ||
964 | la r6, r0, ex_tmp_data_loc_0; | ||
965 | sbi r5, r6, 0; | ||
966 | lbui r5, r4, 1; | ||
967 | sbi r5, r6, 1; | ||
968 | lbui r5, r4, 2; | ||
969 | sbi r5, r6, 2; | ||
970 | lbui r5, r4, 3; | ||
971 | sbi r5, r6, 3; | ||
972 | brid ex_lw_tail_vm; | ||
973 | /* Get the destination register value into r3 - delay slot */ | ||
974 | lwi r3, r6, 0; | ||
975 | ex_lhw_vm: | ||
976 | /* Load a half-word, byte-by-byte from destination address and | ||
977 | * save it in tmp space */ | ||
978 | la r6, r0, ex_tmp_data_loc_0; | ||
979 | sbi r5, r6, 0; | ||
980 | lbui r5, r4, 1; | ||
981 | sbi r5, r6, 1; | ||
982 | lhui r3, r6, 0; /* Get the destination register value into r3 */ | ||
983 | ex_lw_tail_vm: | ||
984 | /* Form load_word jump table offset (lw_table_vm + (8 * regnum)) */ | ||
985 | addik r5, r8, lw_table_vm; | ||
986 | bra r5; | ||
987 | ex_lw_end_vm: /* Exception handling of load word, ends */ | ||
988 | brai ret_from_exc; | ||
989 | ex_sw_vm: | ||
990 | /* Form store_word jump table offset (sw_table_vm + (8 * regnum)) */ | ||
991 | addik r5, r8, sw_table_vm; | ||
992 | bra r5; | ||
993 | ex_sw_tail_vm: | ||
994 | la r5, r0, ex_tmp_data_loc_0; | ||
995 | beqid r6, ex_shw_vm; | ||
996 | swi r3, r5, 0; /* Get the word - delay slot */ | ||
997 | /* Store the word, byte-by-byte into destination address */ | ||
998 | lbui r3, r5, 0; | ||
999 | sbi r3, r4, 0; | ||
1000 | lbui r3, r5, 1; | ||
1001 | sbi r3, r4, 1; | ||
1002 | lbui r3, r5, 2; | ||
1003 | sbi r3, r4, 2; | ||
1004 | lbui r3, r5, 3; | ||
1005 | brid ret_from_exc; | ||
1006 | sbi r3, r4, 3; /* Delay slot */ | ||
1007 | ex_shw_vm: | ||
1008 | /* Store the lower half-word, byte-by-byte into destination address */ | ||
1009 | lbui r3, r5, 2; | ||
1010 | sbi r3, r4, 0; | ||
1011 | lbui r3, r5, 3; | ||
1012 | brid ret_from_exc; | ||
1013 | sbi r3, r4, 1; /* Delay slot */ | ||
1014 | ex_sw_end_vm: /* Exception handling of store word, ends. */ | ||
1015 | .end _unaligned_data_exception | ||
1016 | #endif /* CONFIG_MMU */ | ||
1017 | |||
372 | ex_handler_unhandled: | 1018 | ex_handler_unhandled: |
373 | /* FIXME add handle function for unhandled exception - dump register */ | 1019 | /* FIXME add handle function for unhandled exception - dump register */ |
374 | bri 0 | 1020 | bri 0 |
375 | 1021 | ||
1022 | /* | ||
1023 | * hw_exception_handler Jump Table | ||
1024 | * - Contains code snippets for each register that caused the unalign exception | ||
1025 | * - Hence exception handler is NOT self-modifying | ||
1026 | * - Separate table for load exceptions and store exceptions. | ||
1027 | * - Each table is of size: (8 * 32) = 256 bytes | ||
1028 | */ | ||
1029 | |||
376 | .section .text | 1030 | .section .text |
377 | .align 4 | 1031 | .align 4 |
378 | lw_table: | 1032 | lw_table: |
@@ -407,7 +1061,11 @@ lw_r27: R3_TO_LWREG (27); | |||
407 | lw_r28: R3_TO_LWREG (28); | 1061 | lw_r28: R3_TO_LWREG (28); |
408 | lw_r29: R3_TO_LWREG (29); | 1062 | lw_r29: R3_TO_LWREG (29); |
409 | lw_r30: R3_TO_LWREG (30); | 1063 | lw_r30: R3_TO_LWREG (30); |
1064 | #ifdef CONFIG_MMU | ||
1065 | lw_r31: R3_TO_LWREG_V (31); | ||
1066 | #else | ||
410 | lw_r31: R3_TO_LWREG (31); | 1067 | lw_r31: R3_TO_LWREG (31); |
1068 | #endif | ||
411 | 1069 | ||
412 | sw_table: | 1070 | sw_table: |
413 | sw_r0: SWREG_TO_R3 (0); | 1071 | sw_r0: SWREG_TO_R3 (0); |
@@ -441,7 +1099,81 @@ sw_r27: SWREG_TO_R3 (27); | |||
441 | sw_r28: SWREG_TO_R3 (28); | 1099 | sw_r28: SWREG_TO_R3 (28); |
442 | sw_r29: SWREG_TO_R3 (29); | 1100 | sw_r29: SWREG_TO_R3 (29); |
443 | sw_r30: SWREG_TO_R3 (30); | 1101 | sw_r30: SWREG_TO_R3 (30); |
1102 | #ifdef CONFIG_MMU | ||
1103 | sw_r31: SWREG_TO_R3_V (31); | ||
1104 | #else | ||
444 | sw_r31: SWREG_TO_R3 (31); | 1105 | sw_r31: SWREG_TO_R3 (31); |
1106 | #endif | ||
1107 | |||
1108 | #ifdef CONFIG_MMU | ||
1109 | lw_table_vm: | ||
1110 | lw_r0_vm: R3_TO_LWREG_VM (0); | ||
1111 | lw_r1_vm: R3_TO_LWREG_VM_V (1); | ||
1112 | lw_r2_vm: R3_TO_LWREG_VM_V (2); | ||
1113 | lw_r3_vm: R3_TO_LWREG_VM_V (3); | ||
1114 | lw_r4_vm: R3_TO_LWREG_VM_V (4); | ||
1115 | lw_r5_vm: R3_TO_LWREG_VM_V (5); | ||
1116 | lw_r6_vm: R3_TO_LWREG_VM_V (6); | ||
1117 | lw_r7_vm: R3_TO_LWREG_VM_V (7); | ||
1118 | lw_r8_vm: R3_TO_LWREG_VM_V (8); | ||
1119 | lw_r9_vm: R3_TO_LWREG_VM_V (9); | ||
1120 | lw_r10_vm: R3_TO_LWREG_VM_V (10); | ||
1121 | lw_r11_vm: R3_TO_LWREG_VM_V (11); | ||
1122 | lw_r12_vm: R3_TO_LWREG_VM_V (12); | ||
1123 | lw_r13_vm: R3_TO_LWREG_VM_V (13); | ||
1124 | lw_r14_vm: R3_TO_LWREG_VM (14); | ||
1125 | lw_r15_vm: R3_TO_LWREG_VM_V (15); | ||
1126 | lw_r16_vm: R3_TO_LWREG_VM (16); | ||
1127 | lw_r17_vm: R3_TO_LWREG_VM_V (17); | ||
1128 | lw_r18_vm: R3_TO_LWREG_VM_V (18); | ||
1129 | lw_r19_vm: R3_TO_LWREG_VM (19); | ||
1130 | lw_r20_vm: R3_TO_LWREG_VM (20); | ||
1131 | lw_r21_vm: R3_TO_LWREG_VM (21); | ||
1132 | lw_r22_vm: R3_TO_LWREG_VM (22); | ||
1133 | lw_r23_vm: R3_TO_LWREG_VM (23); | ||
1134 | lw_r24_vm: R3_TO_LWREG_VM (24); | ||
1135 | lw_r25_vm: R3_TO_LWREG_VM (25); | ||
1136 | lw_r26_vm: R3_TO_LWREG_VM (26); | ||
1137 | lw_r27_vm: R3_TO_LWREG_VM (27); | ||
1138 | lw_r28_vm: R3_TO_LWREG_VM (28); | ||
1139 | lw_r29_vm: R3_TO_LWREG_VM (29); | ||
1140 | lw_r30_vm: R3_TO_LWREG_VM (30); | ||
1141 | lw_r31_vm: R3_TO_LWREG_VM_V (31); | ||
1142 | |||
1143 | sw_table_vm: | ||
1144 | sw_r0_vm: SWREG_TO_R3_VM (0); | ||
1145 | sw_r1_vm: SWREG_TO_R3_VM_V (1); | ||
1146 | sw_r2_vm: SWREG_TO_R3_VM_V (2); | ||
1147 | sw_r3_vm: SWREG_TO_R3_VM_V (3); | ||
1148 | sw_r4_vm: SWREG_TO_R3_VM_V (4); | ||
1149 | sw_r5_vm: SWREG_TO_R3_VM_V (5); | ||
1150 | sw_r6_vm: SWREG_TO_R3_VM_V (6); | ||
1151 | sw_r7_vm: SWREG_TO_R3_VM_V (7); | ||
1152 | sw_r8_vm: SWREG_TO_R3_VM_V (8); | ||
1153 | sw_r9_vm: SWREG_TO_R3_VM_V (9); | ||
1154 | sw_r10_vm: SWREG_TO_R3_VM_V (10); | ||
1155 | sw_r11_vm: SWREG_TO_R3_VM_V (11); | ||
1156 | sw_r12_vm: SWREG_TO_R3_VM_V (12); | ||
1157 | sw_r13_vm: SWREG_TO_R3_VM_V (13); | ||
1158 | sw_r14_vm: SWREG_TO_R3_VM (14); | ||
1159 | sw_r15_vm: SWREG_TO_R3_VM_V (15); | ||
1160 | sw_r16_vm: SWREG_TO_R3_VM (16); | ||
1161 | sw_r17_vm: SWREG_TO_R3_VM_V (17); | ||
1162 | sw_r18_vm: SWREG_TO_R3_VM_V (18); | ||
1163 | sw_r19_vm: SWREG_TO_R3_VM (19); | ||
1164 | sw_r20_vm: SWREG_TO_R3_VM (20); | ||
1165 | sw_r21_vm: SWREG_TO_R3_VM (21); | ||
1166 | sw_r22_vm: SWREG_TO_R3_VM (22); | ||
1167 | sw_r23_vm: SWREG_TO_R3_VM (23); | ||
1168 | sw_r24_vm: SWREG_TO_R3_VM (24); | ||
1169 | sw_r25_vm: SWREG_TO_R3_VM (25); | ||
1170 | sw_r26_vm: SWREG_TO_R3_VM (26); | ||
1171 | sw_r27_vm: SWREG_TO_R3_VM (27); | ||
1172 | sw_r28_vm: SWREG_TO_R3_VM (28); | ||
1173 | sw_r29_vm: SWREG_TO_R3_VM (29); | ||
1174 | sw_r30_vm: SWREG_TO_R3_VM (30); | ||
1175 | sw_r31_vm: SWREG_TO_R3_VM_V (31); | ||
1176 | #endif /* CONFIG_MMU */ | ||
445 | 1177 | ||
446 | /* Temporary data structures used in the handler */ | 1178 | /* Temporary data structures used in the handler */ |
447 | .section .data | 1179 | .section .data |