| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * linux/arch/arm/kernel/traps.c |
|---|
| 3 | 4 | * |
|---|
| 4 | 5 | * Copyright (C) 1995-2009 Russell King |
|---|
| 5 | 6 | * Fragments that appear the same as linux/arch/i386/kernel/traps.c (C) Linus Torvalds |
|---|
| 6 | | - * |
|---|
| 7 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 8 | | - * it under the terms of the GNU General Public License version 2 as |
|---|
| 9 | | - * published by the Free Software Foundation. |
|---|
| 10 | 7 | * |
|---|
| 11 | 8 | * 'traps.c' handles hardware exceptions after we have saved some state in |
|---|
| 12 | 9 | * 'linux/arch/arm/lib/traps.S'. Mostly a debugging aid, but will probably |
|---|
| .. | .. |
|---|
| 33 | 30 | #include <linux/atomic.h> |
|---|
| 34 | 31 | #include <asm/cacheflush.h> |
|---|
| 35 | 32 | #include <asm/exception.h> |
|---|
| 33 | +#include <asm/spectre.h> |
|---|
| 36 | 34 | #include <asm/unistd.h> |
|---|
| 37 | 35 | #include <asm/traps.h> |
|---|
| 38 | 36 | #include <asm/ptrace.h> |
|---|
| .. | .. |
|---|
| 65 | 63 | |
|---|
| 66 | 64 | static void dump_mem(const char *, const char *, unsigned long, unsigned long); |
|---|
| 67 | 65 | |
|---|
| 68 | | -void dump_backtrace_entry(unsigned long where, unsigned long from, unsigned long frame) |
|---|
| 66 | +void dump_backtrace_entry(unsigned long where, unsigned long from, |
|---|
| 67 | + unsigned long frame, const char *loglvl) |
|---|
| 69 | 68 | { |
|---|
| 70 | 69 | unsigned long end = frame + 4 + sizeof(struct pt_regs); |
|---|
| 71 | 70 | |
|---|
| 72 | 71 | #ifdef CONFIG_KALLSYMS |
|---|
| 73 | | - printk("[<%08lx>] (%ps) from [<%08lx>] (%pS)\n", where, (void *)where, from, (void *)from); |
|---|
| 72 | + printk("%s[<%08lx>] (%ps) from [<%08lx>] (%pS)\n", |
|---|
| 73 | + loglvl, where, (void *)where, from, (void *)from); |
|---|
| 74 | 74 | #else |
|---|
| 75 | | - printk("Function entered at [<%08lx>] from [<%08lx>]\n", where, from); |
|---|
| 75 | + printk("%sFunction entered at [<%08lx>] from [<%08lx>]\n", |
|---|
| 76 | + loglvl, where, from); |
|---|
| 76 | 77 | #endif |
|---|
| 77 | 78 | |
|---|
| 78 | 79 | if (in_entry_text(from) && end <= ALIGN(frame, THREAD_SIZE)) |
|---|
| 79 | | - dump_mem("", "Exception stack", frame + 4, end); |
|---|
| 80 | + dump_mem(loglvl, "Exception stack", frame + 4, end); |
|---|
| 80 | 81 | } |
|---|
| 81 | 82 | |
|---|
| 82 | | -void dump_backtrace_stm(u32 *stack, u32 instruction) |
|---|
| 83 | +void dump_backtrace_stm(u32 *stack, u32 instruction, const char *loglvl) |
|---|
| 83 | 84 | { |
|---|
| 84 | 85 | char str[80], *p; |
|---|
| 85 | 86 | unsigned int x; |
|---|
| .. | .. |
|---|
| 91 | 92 | if (++x == 6) { |
|---|
| 92 | 93 | x = 0; |
|---|
| 93 | 94 | p = str; |
|---|
| 94 | | - printk("%s\n", str); |
|---|
| 95 | + printk("%s%s\n", loglvl, str); |
|---|
| 95 | 96 | } |
|---|
| 96 | 97 | } |
|---|
| 97 | 98 | } |
|---|
| 98 | 99 | if (p != str) |
|---|
| 99 | | - printk("%s\n", str); |
|---|
| 100 | + printk("%s%s\n", loglvl, str); |
|---|
| 100 | 101 | } |
|---|
| 101 | 102 | |
|---|
| 102 | 103 | #ifndef CONFIG_ARM_UNWIND |
|---|
| .. | .. |
|---|
| 204 | 205 | } |
|---|
| 205 | 206 | |
|---|
| 206 | 207 | #ifdef CONFIG_ARM_UNWIND |
|---|
| 207 | | -static inline void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk) |
|---|
| 208 | +static inline void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk, |
|---|
| 209 | + const char *loglvl) |
|---|
| 208 | 210 | { |
|---|
| 209 | | - unwind_backtrace(regs, tsk); |
|---|
| 211 | + unwind_backtrace(regs, tsk, loglvl); |
|---|
| 210 | 212 | } |
|---|
| 211 | 213 | #else |
|---|
| 212 | | -static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk) |
|---|
| 214 | +static void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk, |
|---|
| 215 | + const char *loglvl) |
|---|
| 213 | 216 | { |
|---|
| 214 | 217 | unsigned int fp, mode; |
|---|
| 215 | 218 | int ok = 1; |
|---|
| 216 | 219 | |
|---|
| 217 | | - printk("Backtrace: "); |
|---|
| 220 | + printk("%sBacktrace: ", loglvl); |
|---|
| 218 | 221 | |
|---|
| 219 | 222 | if (!tsk) |
|---|
| 220 | 223 | tsk = current; |
|---|
| .. | .. |
|---|
| 241 | 244 | pr_cont("\n"); |
|---|
| 242 | 245 | |
|---|
| 243 | 246 | if (ok) |
|---|
| 244 | | - c_backtrace(fp, mode); |
|---|
| 247 | + c_backtrace(fp, mode, loglvl); |
|---|
| 245 | 248 | } |
|---|
| 246 | 249 | #endif |
|---|
| 247 | 250 | |
|---|
| 248 | | -void show_stack(struct task_struct *tsk, unsigned long *sp) |
|---|
| 251 | +void show_stack(struct task_struct *tsk, unsigned long *sp, const char *loglvl) |
|---|
| 249 | 252 | { |
|---|
| 250 | | - dump_backtrace(NULL, tsk); |
|---|
| 253 | + dump_backtrace(NULL, tsk, loglvl); |
|---|
| 251 | 254 | barrier(); |
|---|
| 252 | 255 | } |
|---|
| 253 | 256 | |
|---|
| 254 | 257 | #ifdef CONFIG_PREEMPT |
|---|
| 255 | 258 | #define S_PREEMPT " PREEMPT" |
|---|
| 259 | +#elif defined(CONFIG_PREEMPT_RT) |
|---|
| 260 | +#define S_PREEMPT " PREEMPT_RT" |
|---|
| 256 | 261 | #else |
|---|
| 257 | 262 | #define S_PREEMPT "" |
|---|
| 258 | 263 | #endif |
|---|
| .. | .. |
|---|
| 289 | 294 | if (!user_mode(regs) || in_interrupt()) { |
|---|
| 290 | 295 | dump_mem(KERN_EMERG, "Stack: ", regs->ARM_sp, |
|---|
| 291 | 296 | THREAD_SIZE + (unsigned long)task_stack_page(tsk)); |
|---|
| 292 | | - dump_backtrace(regs, tsk); |
|---|
| 297 | + dump_backtrace(regs, tsk, KERN_EMERG); |
|---|
| 293 | 298 | dump_instr(KERN_EMERG, regs); |
|---|
| 294 | 299 | } |
|---|
| 295 | 300 | |
|---|
| .. | .. |
|---|
| 343 | 348 | if (panic_on_oops) |
|---|
| 344 | 349 | panic("Fatal exception"); |
|---|
| 345 | 350 | if (signr) |
|---|
| 346 | | - do_exit(signr); |
|---|
| 351 | + make_task_dead(signr); |
|---|
| 347 | 352 | } |
|---|
| 348 | 353 | |
|---|
| 349 | 354 | /* |
|---|
| .. | .. |
|---|
| 367 | 372 | } |
|---|
| 368 | 373 | |
|---|
| 369 | 374 | void arm_notify_die(const char *str, struct pt_regs *regs, |
|---|
| 370 | | - struct siginfo *info, unsigned long err, unsigned long trap) |
|---|
| 375 | + int signo, int si_code, void __user *addr, |
|---|
| 376 | + unsigned long err, unsigned long trap) |
|---|
| 371 | 377 | { |
|---|
| 372 | 378 | if (user_mode(regs)) { |
|---|
| 373 | 379 | current->thread.error_code = err; |
|---|
| 374 | 380 | current->thread.trap_no = trap; |
|---|
| 375 | 381 | |
|---|
| 376 | | - force_sig_info(info->si_signo, info, current); |
|---|
| 382 | + force_sig_fault(signo, si_code, addr); |
|---|
| 377 | 383 | } else { |
|---|
| 378 | 384 | die(str, regs, err); |
|---|
| 379 | 385 | } |
|---|
| .. | .. |
|---|
| 391 | 397 | u32 insn = __opcode_to_mem_arm(BUG_INSTR_VALUE); |
|---|
| 392 | 398 | #endif |
|---|
| 393 | 399 | |
|---|
| 394 | | - if (probe_kernel_address((unsigned *)pc, bkpt)) |
|---|
| 400 | + if (get_kernel_nofault(bkpt, (void *)pc)) |
|---|
| 395 | 401 | return 0; |
|---|
| 396 | 402 | |
|---|
| 397 | 403 | return bkpt == insn; |
|---|
| .. | .. |
|---|
| 440 | 446 | asmlinkage void do_undefinstr(struct pt_regs *regs) |
|---|
| 441 | 447 | { |
|---|
| 442 | 448 | unsigned int instr; |
|---|
| 443 | | - siginfo_t info; |
|---|
| 444 | 449 | void __user *pc; |
|---|
| 445 | 450 | |
|---|
| 446 | | - clear_siginfo(&info); |
|---|
| 447 | 451 | pc = (void __user *)instruction_pointer(regs); |
|---|
| 448 | 452 | |
|---|
| 449 | 453 | if (processor_mode(regs) == SVC_MODE) { |
|---|
| .. | .. |
|---|
| 487 | 491 | dump_instr(KERN_INFO, regs); |
|---|
| 488 | 492 | } |
|---|
| 489 | 493 | #endif |
|---|
| 490 | | - |
|---|
| 491 | | - info.si_signo = SIGILL; |
|---|
| 492 | | - info.si_errno = 0; |
|---|
| 493 | | - info.si_code = ILL_ILLOPC; |
|---|
| 494 | | - info.si_addr = pc; |
|---|
| 495 | | - |
|---|
| 496 | | - arm_notify_die("Oops - undefined instruction", regs, &info, 0, 6); |
|---|
| 494 | + arm_notify_die("Oops - undefined instruction", regs, |
|---|
| 495 | + SIGILL, ILL_ILLOPC, pc, 0, 6); |
|---|
| 497 | 496 | } |
|---|
| 498 | 497 | NOKPROBE_SYMBOL(do_undefinstr) |
|---|
| 499 | 498 | |
|---|
| .. | .. |
|---|
| 541 | 540 | |
|---|
| 542 | 541 | static int bad_syscall(int n, struct pt_regs *regs) |
|---|
| 543 | 542 | { |
|---|
| 544 | | - siginfo_t info; |
|---|
| 545 | | - |
|---|
| 546 | | - clear_siginfo(&info); |
|---|
| 547 | 543 | if ((current->personality & PER_MASK) != PER_LINUX) { |
|---|
| 548 | 544 | send_sig(SIGSEGV, current, 1); |
|---|
| 549 | 545 | return regs->ARM_r0; |
|---|
| .. | .. |
|---|
| 557 | 553 | } |
|---|
| 558 | 554 | #endif |
|---|
| 559 | 555 | |
|---|
| 560 | | - info.si_signo = SIGILL; |
|---|
| 561 | | - info.si_errno = 0; |
|---|
| 562 | | - info.si_code = ILL_ILLTRP; |
|---|
| 563 | | - info.si_addr = (void __user *)instruction_pointer(regs) - |
|---|
| 564 | | - (thumb_mode(regs) ? 2 : 4); |
|---|
| 565 | | - |
|---|
| 566 | | - arm_notify_die("Oops - bad syscall", regs, &info, n, 0); |
|---|
| 556 | + arm_notify_die("Oops - bad syscall", regs, SIGILL, ILL_ILLTRP, |
|---|
| 557 | + (void __user *)instruction_pointer(regs) - |
|---|
| 558 | + (thumb_mode(regs) ? 2 : 4), |
|---|
| 559 | + n, 0); |
|---|
| 567 | 560 | |
|---|
| 568 | 561 | return regs->ARM_r0; |
|---|
| 569 | 562 | } |
|---|
| .. | .. |
|---|
| 579 | 572 | if (fatal_signal_pending(current)) |
|---|
| 580 | 573 | return 0; |
|---|
| 581 | 574 | |
|---|
| 582 | | - ret = flush_cache_user_range(start, start + chunk); |
|---|
| 575 | + ret = flush_icache_user_range(start, start + chunk); |
|---|
| 583 | 576 | if (ret) |
|---|
| 584 | 577 | return ret; |
|---|
| 585 | 578 | |
|---|
| .. | .. |
|---|
| 596 | 589 | if (end < start || flags) |
|---|
| 597 | 590 | return -EINVAL; |
|---|
| 598 | 591 | |
|---|
| 599 | | - if (!access_ok(VERIFY_READ, start, end - start)) |
|---|
| 592 | + if (!access_ok((void __user *)start, end - start)) |
|---|
| 600 | 593 | return -EFAULT; |
|---|
| 601 | 594 | |
|---|
| 602 | 595 | return __do_cache_op(start, end); |
|---|
| .. | .. |
|---|
| 609 | 602 | #define NR(x) ((__ARM_NR_##x) - __ARM_NR_BASE) |
|---|
| 610 | 603 | asmlinkage int arm_syscall(int no, struct pt_regs *regs) |
|---|
| 611 | 604 | { |
|---|
| 612 | | - siginfo_t info; |
|---|
| 613 | | - |
|---|
| 614 | | - clear_siginfo(&info); |
|---|
| 615 | 605 | if ((no >> 16) != (__ARM_NR_BASE>> 16)) |
|---|
| 616 | 606 | return bad_syscall(no, regs); |
|---|
| 617 | 607 | |
|---|
| 618 | 608 | switch (no & 0xffff) { |
|---|
| 619 | 609 | case 0: /* branch through 0 */ |
|---|
| 620 | | - info.si_signo = SIGSEGV; |
|---|
| 621 | | - info.si_errno = 0; |
|---|
| 622 | | - info.si_code = SEGV_MAPERR; |
|---|
| 623 | | - info.si_addr = NULL; |
|---|
| 624 | | - |
|---|
| 625 | | - arm_notify_die("branch through zero", regs, &info, 0, 0); |
|---|
| 610 | + arm_notify_die("branch through zero", regs, |
|---|
| 611 | + SIGSEGV, SEGV_MAPERR, NULL, 0, 0); |
|---|
| 626 | 612 | return 0; |
|---|
| 627 | 613 | |
|---|
| 628 | 614 | case NR(breakpoint): /* SWI BREAK_POINT */ |
|---|
| 629 | 615 | regs->ARM_pc -= thumb_mode(regs) ? 2 : 4; |
|---|
| 630 | | - ptrace_break(current, regs); |
|---|
| 616 | + ptrace_break(regs); |
|---|
| 631 | 617 | return regs->ARM_r0; |
|---|
| 632 | 618 | |
|---|
| 633 | 619 | /* |
|---|
| .. | .. |
|---|
| 683 | 669 | if (user_debug & UDBG_SYSCALL) { |
|---|
| 684 | 670 | pr_err("[%d] %s: arm syscall %d\n", |
|---|
| 685 | 671 | task_pid_nr(current), current->comm, no); |
|---|
| 686 | | - dump_instr("", regs); |
|---|
| 672 | + dump_instr(KERN_ERR, regs); |
|---|
| 687 | 673 | if (user_mode(regs)) { |
|---|
| 688 | 674 | __show_regs(regs); |
|---|
| 689 | | - c_backtrace(frame_pointer(regs), processor_mode(regs)); |
|---|
| 675 | + c_backtrace(frame_pointer(regs), processor_mode(regs), KERN_ERR); |
|---|
| 690 | 676 | } |
|---|
| 691 | 677 | } |
|---|
| 692 | 678 | #endif |
|---|
| 693 | | - info.si_signo = SIGILL; |
|---|
| 694 | | - info.si_errno = 0; |
|---|
| 695 | | - info.si_code = ILL_ILLTRP; |
|---|
| 696 | | - info.si_addr = (void __user *)instruction_pointer(regs) - |
|---|
| 697 | | - (thumb_mode(regs) ? 2 : 4); |
|---|
| 698 | | - |
|---|
| 699 | | - arm_notify_die("Oops - bad syscall(2)", regs, &info, no, 0); |
|---|
| 679 | + arm_notify_die("Oops - bad syscall(2)", regs, SIGILL, ILL_ILLTRP, |
|---|
| 680 | + (void __user *)instruction_pointer(regs) - |
|---|
| 681 | + (thumb_mode(regs) ? 2 : 4), |
|---|
| 682 | + no, 0); |
|---|
| 700 | 683 | return 0; |
|---|
| 701 | 684 | } |
|---|
| 702 | 685 | |
|---|
| .. | .. |
|---|
| 746 | 729 | baddataabort(int code, unsigned long instr, struct pt_regs *regs) |
|---|
| 747 | 730 | { |
|---|
| 748 | 731 | unsigned long addr = instruction_pointer(regs); |
|---|
| 749 | | - siginfo_t info; |
|---|
| 750 | | - |
|---|
| 751 | | - clear_siginfo(&info); |
|---|
| 752 | 732 | |
|---|
| 753 | 733 | #ifdef CONFIG_DEBUG_USER |
|---|
| 754 | 734 | if (user_debug & UDBG_BADABORT) { |
|---|
| 735 | + pr_err("8<--- cut here ---\n"); |
|---|
| 755 | 736 | pr_err("[%d] %s: bad data abort: code %d instr 0x%08lx\n", |
|---|
| 756 | 737 | task_pid_nr(current), current->comm, code, instr); |
|---|
| 757 | 738 | dump_instr(KERN_ERR, regs); |
|---|
| 758 | | - show_pte(current->mm, addr); |
|---|
| 739 | + show_pte(KERN_ERR, current->mm, addr); |
|---|
| 759 | 740 | } |
|---|
| 760 | 741 | #endif |
|---|
| 761 | 742 | |
|---|
| 762 | | - info.si_signo = SIGILL; |
|---|
| 763 | | - info.si_errno = 0; |
|---|
| 764 | | - info.si_code = ILL_ILLOPC; |
|---|
| 765 | | - info.si_addr = (void __user *)addr; |
|---|
| 766 | | - |
|---|
| 767 | | - arm_notify_die("unknown data abort code", regs, &info, instr, 0); |
|---|
| 743 | + arm_notify_die("unknown data abort code", regs, |
|---|
| 744 | + SIGILL, ILL_ILLOPC, (void __user *)addr, instr, 0); |
|---|
| 768 | 745 | } |
|---|
| 769 | 746 | |
|---|
| 770 | 747 | void __readwrite_bug(const char *fn) |
|---|
| .. | .. |
|---|
| 830 | 807 | } |
|---|
| 831 | 808 | #endif |
|---|
| 832 | 809 | |
|---|
| 810 | +#ifndef CONFIG_CPU_V7M |
|---|
| 811 | +static void copy_from_lma(void *vma, void *lma_start, void *lma_end) |
|---|
| 812 | +{ |
|---|
| 813 | + memcpy(vma, lma_start, lma_end - lma_start); |
|---|
| 814 | +} |
|---|
| 815 | + |
|---|
| 816 | +static void flush_vectors(void *vma, size_t offset, size_t size) |
|---|
| 817 | +{ |
|---|
| 818 | + unsigned long start = (unsigned long)vma + offset; |
|---|
| 819 | + unsigned long end = start + size; |
|---|
| 820 | + |
|---|
| 821 | + flush_icache_range(start, end); |
|---|
| 822 | +} |
|---|
| 823 | + |
|---|
| 824 | +#ifdef CONFIG_HARDEN_BRANCH_HISTORY |
|---|
| 825 | +int spectre_bhb_update_vectors(unsigned int method) |
|---|
| 826 | +{ |
|---|
| 827 | + extern char __vectors_bhb_bpiall_start[], __vectors_bhb_bpiall_end[]; |
|---|
| 828 | + extern char __vectors_bhb_loop8_start[], __vectors_bhb_loop8_end[]; |
|---|
| 829 | + void *vec_start, *vec_end; |
|---|
| 830 | + |
|---|
| 831 | + if (system_state > SYSTEM_SCHEDULING) { |
|---|
| 832 | + pr_err("CPU%u: Spectre BHB workaround too late - system vulnerable\n", |
|---|
| 833 | + smp_processor_id()); |
|---|
| 834 | + return SPECTRE_VULNERABLE; |
|---|
| 835 | + } |
|---|
| 836 | + |
|---|
| 837 | + switch (method) { |
|---|
| 838 | + case SPECTRE_V2_METHOD_LOOP8: |
|---|
| 839 | + vec_start = __vectors_bhb_loop8_start; |
|---|
| 840 | + vec_end = __vectors_bhb_loop8_end; |
|---|
| 841 | + break; |
|---|
| 842 | + |
|---|
| 843 | + case SPECTRE_V2_METHOD_BPIALL: |
|---|
| 844 | + vec_start = __vectors_bhb_bpiall_start; |
|---|
| 845 | + vec_end = __vectors_bhb_bpiall_end; |
|---|
| 846 | + break; |
|---|
| 847 | + |
|---|
| 848 | + default: |
|---|
| 849 | + pr_err("CPU%u: unknown Spectre BHB state %d\n", |
|---|
| 850 | + smp_processor_id(), method); |
|---|
| 851 | + return SPECTRE_VULNERABLE; |
|---|
| 852 | + } |
|---|
| 853 | + |
|---|
| 854 | + copy_from_lma(vectors_page, vec_start, vec_end); |
|---|
| 855 | + flush_vectors(vectors_page, 0, vec_end - vec_start); |
|---|
| 856 | + |
|---|
| 857 | + return SPECTRE_MITIGATED; |
|---|
| 858 | +} |
|---|
| 859 | +#endif |
|---|
| 860 | + |
|---|
| 833 | 861 | void __init early_trap_init(void *vectors_base) |
|---|
| 834 | 862 | { |
|---|
| 835 | | -#ifndef CONFIG_CPU_V7M |
|---|
| 836 | | - unsigned long vectors = (unsigned long)vectors_base; |
|---|
| 837 | 863 | extern char __stubs_start[], __stubs_end[]; |
|---|
| 838 | 864 | extern char __vectors_start[], __vectors_end[]; |
|---|
| 839 | 865 | unsigned i; |
|---|
| .. | .. |
|---|
| 854 | 880 | * into the vector page, mapped at 0xffff0000, and ensure these |
|---|
| 855 | 881 | * are visible to the instruction stream. |
|---|
| 856 | 882 | */ |
|---|
| 857 | | - memcpy((void *)vectors, __vectors_start, __vectors_end - __vectors_start); |
|---|
| 858 | | - memcpy((void *)vectors + 0x1000, __stubs_start, __stubs_end - __stubs_start); |
|---|
| 883 | + copy_from_lma(vectors_base, __vectors_start, __vectors_end); |
|---|
| 884 | + copy_from_lma(vectors_base + 0x1000, __stubs_start, __stubs_end); |
|---|
| 859 | 885 | |
|---|
| 860 | 886 | kuser_init(vectors_base); |
|---|
| 861 | 887 | |
|---|
| 862 | | - flush_icache_range(vectors, vectors + PAGE_SIZE * 2); |
|---|
| 888 | + flush_vectors(vectors_base, 0, PAGE_SIZE * 2); |
|---|
| 889 | +} |
|---|
| 863 | 890 | #else /* ifndef CONFIG_CPU_V7M */ |
|---|
| 891 | +void __init early_trap_init(void *vectors_base) |
|---|
| 892 | +{ |
|---|
| 864 | 893 | /* |
|---|
| 865 | 894 | * on V7-M there is no need to copy the vector table to a dedicated |
|---|
| 866 | 895 | * memory area. The address is configurable and so a table in the kernel |
|---|
| 867 | 896 | * image can be used. |
|---|
| 868 | 897 | */ |
|---|
| 869 | | -#endif |
|---|
| 870 | 898 | } |
|---|
| 899 | +#endif |
|---|