Skip to content

Commit 9f159ae

Browse files
committed
Merge branch 'x86-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull x86 fixes from Thomas Gleixner: "A set of fixes for x86: - Fix the bogus detection of 32bit user mode for uretprobes which caused corruption of the user return address resulting in application crashes. In the uprobes handler in_ia32_syscall() is obviously always returning false on a 64bit kernel. Use user_64bit_mode() instead which works correctly. - Prevent large page splitting when ftrace flips RW/RO on the kernel text which caused iTLB performance issues. Ftrace wants to be converted to text_poke() which avoids the problem, but for now allow large page preservation in the static protections check when the change request spawns a full large page. - Prevent arch_dynirq_lower_bound() from returning 0 when the IOAPIC is configured via device tree. In the device tree case the GSI 1:1 mapping is meaningless therefore the lower bound which protects the GSI range on ACPI machines is irrelevant. Return the lower bound which the core hands to the function instead of blindly returning 0 which causes the core to allocate the invalid virtual interupt number 0 which in turn prevents all drivers from allocating and requesting an interrupt. - Remove the bogus initialization of LDR and DFR in the 32bit bigsmp APIC driver. That uses physical destination mode where LDR/DFR are ignored, but the initialization and the missing clear of LDR caused the APIC to be left in a inconsistent state on kexec/reboot. - Clear LDR when clearing the APIC registers so the APIC is in a well defined state. - Initialize variables proper in the find_trampoline_placement() code. - Silence GCC( build warning for the real mode part of the build" * 'x86-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: x86/mm/cpa: Prevent large page split when ftrace flips RW on kernel text x86/build: Add -Wnoaddress-of-packed-member to REALMODE_CFLAGS, to silence GCC9 build warning x86/boot/compressed/64: Fix missing initialization in find_trampoline_placement() x86/apic: Include the LDR when clearing out APIC registers x86/apic: Do not initialize LDR and DFR for bigsmp uprobes/x86: Fix detection of 32-bit user mode x86/apic: Fix arch_dynirq_lower_bound() bug for DT enabled machines
2 parents 5fb181c + 7af0145 commit 9f159ae

File tree

7 files changed

+43
-39
lines changed

7 files changed

+43
-39
lines changed

arch/x86/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ REALMODE_CFLAGS := $(M16_CFLAGS) -g -Os -DDISABLE_BRANCH_PROFILING \
3838

3939
REALMODE_CFLAGS += $(call __cc-option, $(CC), $(REALMODE_CFLAGS), -ffreestanding)
4040
REALMODE_CFLAGS += $(call __cc-option, $(CC), $(REALMODE_CFLAGS), -fno-stack-protector)
41+
REALMODE_CFLAGS += $(call __cc-option, $(CC), $(REALMODE_CFLAGS), -Wno-address-of-packed-member)
4142
REALMODE_CFLAGS += $(call __cc-option, $(CC), $(REALMODE_CFLAGS), $(cc_stack_align4))
4243
export REALMODE_CFLAGS
4344

arch/x86/boot/compressed/pgtable_64.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ static unsigned long find_trampoline_placement(void)
7272

7373
/* Find the first usable memory region under bios_start. */
7474
for (i = boot_params->e820_entries - 1; i >= 0; i--) {
75-
unsigned long new;
75+
unsigned long new = bios_start;
7676

7777
entry = &boot_params->e820_table[i];
7878

arch/x86/kernel/apic/apic.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1179,6 +1179,10 @@ void clear_local_APIC(void)
11791179
apic_write(APIC_LVT0, v | APIC_LVT_MASKED);
11801180
v = apic_read(APIC_LVT1);
11811181
apic_write(APIC_LVT1, v | APIC_LVT_MASKED);
1182+
if (!x2apic_enabled()) {
1183+
v = apic_read(APIC_LDR) & ~APIC_LDR_MASK;
1184+
apic_write(APIC_LDR, v);
1185+
}
11821186
if (maxlvt >= 4) {
11831187
v = apic_read(APIC_LVTPC);
11841188
apic_write(APIC_LVTPC, v | APIC_LVT_MASKED);

arch/x86/kernel/apic/bigsmp_32.c

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -38,32 +38,12 @@ static int bigsmp_early_logical_apicid(int cpu)
3838
return early_per_cpu(x86_cpu_to_apicid, cpu);
3939
}
4040

41-
static inline unsigned long calculate_ldr(int cpu)
42-
{
43-
unsigned long val, id;
44-
45-
val = apic_read(APIC_LDR) & ~APIC_LDR_MASK;
46-
id = per_cpu(x86_bios_cpu_apicid, cpu);
47-
val |= SET_APIC_LOGICAL_ID(id);
48-
49-
return val;
50-
}
51-
5241
/*
53-
* Set up the logical destination ID.
54-
*
55-
* Intel recommends to set DFR, LDR and TPR before enabling
56-
* an APIC. See e.g. "AP-388 82489DX User's Manual" (Intel
57-
* document number 292116). So here it goes...
42+
* bigsmp enables physical destination mode
43+
* and doesn't use LDR and DFR
5844
*/
5945
static void bigsmp_init_apic_ldr(void)
6046
{
61-
unsigned long val;
62-
int cpu = smp_processor_id();
63-
64-
apic_write(APIC_DFR, APIC_DFR_FLAT);
65-
val = calculate_ldr(cpu);
66-
apic_write(APIC_LDR, val);
6747
}
6848

6949
static void bigsmp_setup_apic_routing(void)

arch/x86/kernel/apic/io_apic.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2438,7 +2438,13 @@ unsigned int arch_dynirq_lower_bound(unsigned int from)
24382438
* dmar_alloc_hwirq() may be called before setup_IO_APIC(), so use
24392439
* gsi_top if ioapic_dynirq_base hasn't been initialized yet.
24402440
*/
2441-
return ioapic_initialized ? ioapic_dynirq_base : gsi_top;
2441+
if (!ioapic_initialized)
2442+
return gsi_top;
2443+
/*
2444+
* For DT enabled machines ioapic_dynirq_base is irrelevant and not
2445+
* updated. So simply return @from if ioapic_dynirq_base == 0.
2446+
*/
2447+
return ioapic_dynirq_base ? : from;
24422448
}
24432449

24442450
#ifdef CONFIG_X86_32

arch/x86/kernel/uprobes.c

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -508,9 +508,12 @@ struct uprobe_xol_ops {
508508
void (*abort)(struct arch_uprobe *, struct pt_regs *);
509509
};
510510

511-
static inline int sizeof_long(void)
511+
static inline int sizeof_long(struct pt_regs *regs)
512512
{
513-
return in_ia32_syscall() ? 4 : 8;
513+
/*
514+
* Check registers for mode as in_xxx_syscall() does not apply here.
515+
*/
516+
return user_64bit_mode(regs) ? 8 : 4;
514517
}
515518

516519
static int default_pre_xol_op(struct arch_uprobe *auprobe, struct pt_regs *regs)
@@ -521,9 +524,9 @@ static int default_pre_xol_op(struct arch_uprobe *auprobe, struct pt_regs *regs)
521524

522525
static int emulate_push_stack(struct pt_regs *regs, unsigned long val)
523526
{
524-
unsigned long new_sp = regs->sp - sizeof_long();
527+
unsigned long new_sp = regs->sp - sizeof_long(regs);
525528

526-
if (copy_to_user((void __user *)new_sp, &val, sizeof_long()))
529+
if (copy_to_user((void __user *)new_sp, &val, sizeof_long(regs)))
527530
return -EFAULT;
528531

529532
regs->sp = new_sp;
@@ -556,7 +559,7 @@ static int default_post_xol_op(struct arch_uprobe *auprobe, struct pt_regs *regs
556559
long correction = utask->vaddr - utask->xol_vaddr;
557560
regs->ip += correction;
558561
} else if (auprobe->defparam.fixups & UPROBE_FIX_CALL) {
559-
regs->sp += sizeof_long(); /* Pop incorrect return address */
562+
regs->sp += sizeof_long(regs); /* Pop incorrect return address */
560563
if (emulate_push_stack(regs, utask->vaddr + auprobe->defparam.ilen))
561564
return -ERESTART;
562565
}
@@ -675,7 +678,7 @@ static int branch_post_xol_op(struct arch_uprobe *auprobe, struct pt_regs *regs)
675678
* "call" insn was executed out-of-line. Just restore ->sp and restart.
676679
* We could also restore ->ip and try to call branch_emulate_op() again.
677680
*/
678-
regs->sp += sizeof_long();
681+
regs->sp += sizeof_long(regs);
679682
return -ERESTART;
680683
}
681684

@@ -1056,7 +1059,7 @@ bool arch_uprobe_skip_sstep(struct arch_uprobe *auprobe, struct pt_regs *regs)
10561059
unsigned long
10571060
arch_uretprobe_hijack_return_addr(unsigned long trampoline_vaddr, struct pt_regs *regs)
10581061
{
1059-
int rasize = sizeof_long(), nleft;
1062+
int rasize = sizeof_long(regs), nleft;
10601063
unsigned long orig_ret_vaddr = 0; /* clear high bits for 32-bit apps */
10611064

10621065
if (copy_from_user(&orig_ret_vaddr, (void __user *)regs->sp, rasize))

arch/x86/mm/pageattr.c

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,7 @@ static inline void check_conflict(int warnlvl, pgprot_t prot, pgprotval_t val,
516516
*/
517517
static inline pgprot_t static_protections(pgprot_t prot, unsigned long start,
518518
unsigned long pfn, unsigned long npg,
519-
int warnlvl)
519+
unsigned long lpsize, int warnlvl)
520520
{
521521
pgprotval_t forbidden, res;
522522
unsigned long end;
@@ -535,9 +535,17 @@ static inline pgprot_t static_protections(pgprot_t prot, unsigned long start,
535535
check_conflict(warnlvl, prot, res, start, end, pfn, "Text NX");
536536
forbidden = res;
537537

538-
res = protect_kernel_text_ro(start, end);
539-
check_conflict(warnlvl, prot, res, start, end, pfn, "Text RO");
540-
forbidden |= res;
538+
/*
539+
* Special case to preserve a large page. If the change spawns the
540+
* full large page mapping then there is no point to split it
541+
* up. Happens with ftrace and is going to be removed once ftrace
542+
* switched to text_poke().
543+
*/
544+
if (lpsize != (npg * PAGE_SIZE) || (start & (lpsize - 1))) {
545+
res = protect_kernel_text_ro(start, end);
546+
check_conflict(warnlvl, prot, res, start, end, pfn, "Text RO");
547+
forbidden |= res;
548+
}
541549

542550
/* Check the PFN directly */
543551
res = protect_pci_bios(pfn, pfn + npg - 1);
@@ -819,7 +827,7 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address,
819827
* extra conditional required here.
820828
*/
821829
chk_prot = static_protections(old_prot, lpaddr, old_pfn, numpages,
822-
CPA_CONFLICT);
830+
psize, CPA_CONFLICT);
823831

824832
if (WARN_ON_ONCE(pgprot_val(chk_prot) != pgprot_val(old_prot))) {
825833
/*
@@ -855,7 +863,7 @@ static int __should_split_large_page(pte_t *kpte, unsigned long address,
855863
* protection requirement in the large page.
856864
*/
857865
new_prot = static_protections(req_prot, lpaddr, old_pfn, numpages,
858-
CPA_DETECT);
866+
psize, CPA_DETECT);
859867

860868
/*
861869
* If there is a conflict, split the large page.
@@ -906,7 +914,8 @@ static void split_set_pte(struct cpa_data *cpa, pte_t *pte, unsigned long pfn,
906914
if (!cpa->force_static_prot)
907915
goto set;
908916

909-
prot = static_protections(ref_prot, address, pfn, npg, CPA_PROTECT);
917+
/* Hand in lpsize = 0 to enforce the protection mechanism */
918+
prot = static_protections(ref_prot, address, pfn, npg, 0, CPA_PROTECT);
910919

911920
if (pgprot_val(prot) == pgprot_val(ref_prot))
912921
goto set;
@@ -1503,7 +1512,8 @@ static int __change_page_attr(struct cpa_data *cpa, int primary)
15031512
pgprot_val(new_prot) |= pgprot_val(cpa->mask_set);
15041513

15051514
cpa_inc_4k_install();
1506-
new_prot = static_protections(new_prot, address, pfn, 1,
1515+
/* Hand in lpsize = 0 to enforce the protection mechanism */
1516+
new_prot = static_protections(new_prot, address, pfn, 1, 0,
15071517
CPA_PROTECT);
15081518

15091519
new_prot = pgprot_clear_protnone_bits(new_prot);

0 commit comments

Comments
 (0)