x86: Remap GDT tables in the fixmap section
authorThomas Garnier <thgarnie@google.com>
Tue, 14 Mar 2017 17:05:07 +0000 (10:05 -0700)
committerIngo Molnar <mingo@kernel.org>
Thu, 16 Mar 2017 08:06:35 +0000 (09:06 +0100)
Each processor holds a GDT in its per-cpu structure. The sgdt
instruction gives the base address of the current GDT. This address can
be used to bypass KASLR memory randomization. With another bug, an
attacker could target other per-cpu structures or deduce the base of
the main memory section (PAGE_OFFSET).

This patch relocates the GDT table for each processor inside the
fixmap section. The space is reserved based on number of supported
processors.

For consistency, the remapping is done by default on 32 and 64-bit.

Each processor switches to its remapped GDT at the end of
initialization. For hibernation, the main processor returns with the
original GDT and switches back to the remapping at completion.

This patch was tested on both architectures. Hibernation and KVM were
both tested specially for their usage of the GDT.

Thanks to Boris Ostrovsky <boris.ostrovsky@oracle.com> for testing and
recommending changes for Xen support.

Signed-off-by: Thomas Garnier <thgarnie@google.com>
Cc: Alexander Potapenko <glider@google.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Andrey Ryabinin <aryabinin@virtuozzo.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Cc: Boris Ostrovsky <boris.ostrovsky@oracle.com>
Cc: Borislav Petkov <bp@suse.de>
Cc: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Christian Borntraeger <borntraeger@de.ibm.com>
Cc: Dmitry Vyukov <dvyukov@google.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Jiri Kosina <jikos@kernel.org>
Cc: Joerg Roedel <joro@8bytes.org>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Juergen Gross <jgross@suse.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Len Brown <len.brown@intel.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Lorenzo Stoakes <lstoakes@gmail.com>
Cc: Luis R . Rodriguez <mcgrof@kernel.org>
Cc: Matt Fleming <matt@codeblueprint.co.uk>
Cc: Michal Hocko <mhocko@suse.com>
Cc: Paolo Bonzini <pbonzini@redhat.com>
Cc: Paul Gortmaker <paul.gortmaker@windriver.com>
Cc: Pavel Machek <pavel@ucw.cz>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Radim Krčmář <rkrcmar@redhat.com>
Cc: Rafael J . Wysocki <rjw@rjwysocki.net>
Cc: Rusty Russell <rusty@rustcorp.com.au>
Cc: Stanislaw Gruszka <sgruszka@redhat.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Tim Chen <tim.c.chen@linux.intel.com>
Cc: Vitaly Kuznetsov <vkuznets@redhat.com>
Cc: kasan-dev@googlegroups.com
Cc: kernel-hardening@lists.openwall.com
Cc: kvm@vger.kernel.org
Cc: lguest@lists.ozlabs.org
Cc: linux-doc@vger.kernel.org
Cc: linux-efi@vger.kernel.org
Cc: linux-mm@kvack.org
Cc: linux-pm@vger.kernel.org
Cc: xen-devel@lists.xenproject.org
Cc: zijun_hu <zijun_hu@htc.com>
Link: http://lkml.kernel.org/r/20170314170508.100882-2-thgarnie@google.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
17 files changed:
arch/x86/entry/vdso/vma.c
arch/x86/include/asm/desc.h
arch/x86/include/asm/fixmap.h
arch/x86/include/asm/processor.h
arch/x86/include/asm/stackprotector.h
arch/x86/kernel/acpi/sleep.c
arch/x86/kernel/apm_32.c
arch/x86/kernel/cpu/common.c
arch/x86/kernel/setup_percpu.c
arch/x86/kernel/smpboot.c
arch/x86/platform/efi/efi_32.c
arch/x86/power/cpu.c
arch/x86/xen/enlighten.c
arch/x86/xen/mmu.c
arch/x86/xen/smp.c
drivers/lguest/x86/core.c
drivers/pnp/pnpbios/bioscalls.c

index 226ca70dc6bd43b04e15477c8574b5d48d0d4ff8..5c5d4d7618e6dac3ccd376fce7eb53a161de626d 100644 (file)
@@ -354,7 +354,7 @@ static void vgetcpu_cpu_init(void *arg)
        d.p = 1;                /* Present */
        d.d = 1;                /* 32-bit */
 
-       write_gdt_entry(get_cpu_gdt_table(cpu), GDT_ENTRY_PER_CPU, &d, DESCTYPE_S);
+       write_gdt_entry(get_cpu_gdt_rw(cpu), GDT_ENTRY_PER_CPU, &d, DESCTYPE_S);
 }
 
 static int vgetcpu_online(unsigned int cpu)
index 1548ca92ad3f620d48bce51537d24e0701212a42..4b5ef0c64291506e97ee2ed92bbf6677aa690b77 100644 (file)
@@ -4,6 +4,7 @@
 #include <asm/desc_defs.h>
 #include <asm/ldt.h>
 #include <asm/mmu.h>
+#include <asm/fixmap.h>
 
 #include <linux/smp.h>
 #include <linux/percpu.h>
@@ -38,6 +39,7 @@ extern struct desc_ptr idt_descr;
 extern gate_desc idt_table[];
 extern const struct desc_ptr debug_idt_descr;
 extern gate_desc debug_idt_table[];
+extern pgprot_t pg_fixmap_gdt_flags;
 
 struct gdt_page {
        struct desc_struct gdt[GDT_ENTRIES];
@@ -45,11 +47,57 @@ struct gdt_page {
 
 DECLARE_PER_CPU_PAGE_ALIGNED(struct gdt_page, gdt_page);
 
-static inline struct desc_struct *get_cpu_gdt_table(unsigned int cpu)
+/* Provide the original GDT */
+static inline struct desc_struct *get_cpu_gdt_rw(unsigned int cpu)
 {
        return per_cpu(gdt_page, cpu).gdt;
 }
 
+static inline unsigned long get_cpu_gdt_rw_vaddr(unsigned int cpu)
+{
+       return (unsigned long)get_cpu_gdt_rw(cpu);
+}
+
+/* Provide the current original GDT */
+static inline struct desc_struct *get_current_gdt_rw(void)
+{
+       return this_cpu_ptr(&gdt_page)->gdt;
+}
+
+static inline unsigned long get_current_gdt_rw_vaddr(void)
+{
+       return (unsigned long)get_current_gdt_rw();
+}
+
+/* Get the fixmap index for a specific processor */
+static inline unsigned int get_cpu_gdt_ro_index(int cpu)
+{
+       return FIX_GDT_REMAP_BEGIN + cpu;
+}
+
+/* Provide the fixmap address of the remapped GDT */
+static inline struct desc_struct *get_cpu_gdt_ro(int cpu)
+{
+       unsigned int idx = get_cpu_gdt_ro_index(cpu);
+       return (struct desc_struct *)__fix_to_virt(idx);
+}
+
+static inline unsigned long get_cpu_gdt_ro_vaddr(int cpu)
+{
+       return (unsigned long)get_cpu_gdt_ro(cpu);
+}
+
+/* Provide the current read-only GDT */
+static inline struct desc_struct *get_current_gdt_ro(void)
+{
+       return get_cpu_gdt_ro(smp_processor_id());
+}
+
+static inline unsigned long get_current_gdt_ro_vaddr(void)
+{
+       return (unsigned long)get_current_gdt_ro();
+}
+
 #ifdef CONFIG_X86_64
 
 static inline void pack_gate(gate_desc *gate, unsigned type, unsigned long func,
@@ -174,7 +222,7 @@ static inline void set_tssldt_descriptor(void *d, unsigned long addr, unsigned t
 
 static inline void __set_tss_desc(unsigned cpu, unsigned int entry, void *addr)
 {
-       struct desc_struct *d = get_cpu_gdt_table(cpu);
+       struct desc_struct *d = get_cpu_gdt_rw(cpu);
        tss_desc tss;
 
        set_tssldt_descriptor(&tss, (unsigned long)addr, DESC_TSS,
@@ -194,7 +242,7 @@ static inline void native_set_ldt(const void *addr, unsigned int entries)
 
                set_tssldt_descriptor(&ldt, (unsigned long)addr, DESC_LDT,
                                      entries * LDT_ENTRY_SIZE - 1);
-               write_gdt_entry(get_cpu_gdt_table(cpu), GDT_ENTRY_LDT,
+               write_gdt_entry(get_cpu_gdt_rw(cpu), GDT_ENTRY_LDT,
                                &ldt, DESC_LDT);
                asm volatile("lldt %w0"::"q" (GDT_ENTRY_LDT*8));
        }
@@ -209,7 +257,7 @@ DECLARE_PER_CPU(bool, __tss_limit_invalid);
 
 static inline void force_reload_TR(void)
 {
-       struct desc_struct *d = get_cpu_gdt_table(smp_processor_id());
+       struct desc_struct *d = get_current_gdt_rw();
        tss_desc tss;
 
        memcpy(&tss, &d[GDT_ENTRY_TSS], sizeof(tss_desc));
@@ -288,7 +336,7 @@ static inline unsigned long native_store_tr(void)
 
 static inline void native_load_tls(struct thread_struct *t, unsigned int cpu)
 {
-       struct desc_struct *gdt = get_cpu_gdt_table(cpu);
+       struct desc_struct *gdt = get_cpu_gdt_rw(cpu);
        unsigned int i;
 
        for (i = 0; i < GDT_ENTRY_TLS_ENTRIES; i++)
index 8554f960e21b7ce0d05dfb67fa36f6cff974650e..b65155cc3760a72b49b680c3f70923ddedf684d2 100644 (file)
@@ -100,6 +100,10 @@ enum fixed_addresses {
 #ifdef CONFIG_X86_INTEL_MID
        FIX_LNW_VRTC,
 #endif
+       /* Fixmap entries to remap the GDTs, one per processor. */
+       FIX_GDT_REMAP_BEGIN,
+       FIX_GDT_REMAP_END = FIX_GDT_REMAP_BEGIN + NR_CPUS - 1,
+
        __end_of_permanent_fixed_addresses,
 
        /*
index 7caa2ac50ea2571a50bc932b2bcc14b8faca6c55..1150e1b21b0d313ec19b88a955a30036398bc823 100644 (file)
@@ -716,6 +716,7 @@ extern struct desc_ptr              early_gdt_descr;
 
 extern void cpu_set_gdt(int);
 extern void switch_to_new_gdt(int);
+extern void load_fixmap_gdt(int);
 extern void load_percpu_segment(int);
 extern void cpu_init(void);
 
index 58505f01962f31f80f2e99c5b0bb9e5b2c8405b8..dcbd9bcce71443eb64325051faa532a68bf50c00 100644 (file)
@@ -87,7 +87,7 @@ static inline void setup_stack_canary_segment(int cpu)
 {
 #ifdef CONFIG_X86_32
        unsigned long canary = (unsigned long)&per_cpu(stack_canary, cpu);
-       struct desc_struct *gdt_table = get_cpu_gdt_table(cpu);
+       struct desc_struct *gdt_table = get_cpu_gdt_rw(cpu);
        struct desc_struct desc;
 
        desc = gdt_table[GDT_ENTRY_STACK_CANARY];
index 48587335ede8e2296b80ff991d1bf4e8e155ad46..ed014814ea35fb46ddc14b95b7d030e0baaaf82c 100644 (file)
@@ -101,7 +101,7 @@ int x86_acpi_suspend_lowlevel(void)
 #ifdef CONFIG_SMP
        initial_stack = (unsigned long)temp_stack + sizeof(temp_stack);
        early_gdt_descr.address =
-                       (unsigned long)get_cpu_gdt_table(smp_processor_id());
+                       (unsigned long)get_cpu_gdt_rw(smp_processor_id());
        initial_gs = per_cpu_offset(smp_processor_id());
 #endif
        initial_code = (unsigned long)wakeup_long64;
index 5a414545e8a39001a1679b40012adad2ac908d7b..446b0d3d4932fbe30f6d067cfd09bf65a5ac6709 100644 (file)
@@ -609,7 +609,7 @@ static long __apm_bios_call(void *_call)
 
        cpu = get_cpu();
        BUG_ON(cpu != 0);
-       gdt = get_cpu_gdt_table(cpu);
+       gdt = get_cpu_gdt_rw(cpu);
        save_desc_40 = gdt[0x40 / 8];
        gdt[0x40 / 8] = bad_bios_desc;
 
@@ -685,7 +685,7 @@ static long __apm_bios_call_simple(void *_call)
 
        cpu = get_cpu();
        BUG_ON(cpu != 0);
-       gdt = get_cpu_gdt_table(cpu);
+       gdt = get_cpu_gdt_rw(cpu);
        save_desc_40 = gdt[0x40 / 8];
        gdt[0x40 / 8] = bad_bios_desc;
 
@@ -2352,7 +2352,7 @@ static int __init apm_init(void)
         * Note we only set APM segments on CPU zero, since we pin the APM
         * code to that CPU.
         */
-       gdt = get_cpu_gdt_table(0);
+       gdt = get_cpu_gdt_rw(0);
        set_desc_base(&gdt[APM_CS >> 3],
                 (unsigned long)__va((unsigned long)apm_info.bios.cseg << 4));
        set_desc_base(&gdt[APM_CS_16 >> 3],
index 58094a1f9e9d301e11d2c93a1ecc126e1715002e..3cf1590ec9ceb64f360447204c82b0737286f2aa 100644 (file)
@@ -448,6 +448,26 @@ void load_percpu_segment(int cpu)
        load_stack_canary_segment();
 }
 
+/* Used by XEN to force the GDT read-only when required */
+pgprot_t pg_fixmap_gdt_flags = PAGE_KERNEL;
+
+/* Setup the fixmap mapping only once per-processor */
+static inline void setup_fixmap_gdt(int cpu)
+{
+       __set_fixmap(get_cpu_gdt_ro_index(cpu),
+                    __pa(get_cpu_gdt_rw(cpu)), pg_fixmap_gdt_flags);
+}
+
+/* Load a fixmap remapping of the per-cpu GDT */
+void load_fixmap_gdt(int cpu)
+{
+       struct desc_ptr gdt_descr;
+
+       gdt_descr.address = (long)get_cpu_gdt_ro(cpu);
+       gdt_descr.size = GDT_SIZE - 1;
+       load_gdt(&gdt_descr);
+}
+
 /*
  * Current gdt points %fs at the "master" per-cpu area: after this,
  * it's on the real one.
@@ -456,11 +476,10 @@ void switch_to_new_gdt(int cpu)
 {
        struct desc_ptr gdt_descr;
 
-       gdt_descr.address = (long)get_cpu_gdt_table(cpu);
+       gdt_descr.address = (long)get_cpu_gdt_rw(cpu);
        gdt_descr.size = GDT_SIZE - 1;
        load_gdt(&gdt_descr);
        /* Reload the per-cpu base */
-
        load_percpu_segment(cpu);
 }
 
@@ -1526,6 +1545,9 @@ void cpu_init(void)
 
        if (is_uv_system())
                uv_cpu_init();
+
+       setup_fixmap_gdt(cpu);
+       load_fixmap_gdt(cpu);
 }
 
 #else
@@ -1581,6 +1603,9 @@ void cpu_init(void)
        dbg_restore_debug_regs();
 
        fpu__init_cpu();
+
+       setup_fixmap_gdt(cpu);
+       load_fixmap_gdt(cpu);
 }
 #endif
 
index 9820d6d977c6646f9471229063688f3f3d24eaec..11338b0b3ad272fe5d879d985602b6082ab210ef 100644 (file)
@@ -160,7 +160,7 @@ static inline void setup_percpu_segment(int cpu)
        pack_descriptor(&gdt, per_cpu_offset(cpu), 0xFFFFF,
                        0x2 | DESCTYPE_S, 0x8);
        gdt.s = 1;
-       write_gdt_entry(get_cpu_gdt_table(cpu),
+       write_gdt_entry(get_cpu_gdt_rw(cpu),
                        GDT_ENTRY_PERCPU, &gdt, DESCTYPE_S);
 #endif
 }
index bd1f1ad3528420578ae2f5331bc23269b4eb89c6..f04479a8f74f5598c8ee42bae66b17cee469ba49 100644 (file)
@@ -983,7 +983,7 @@ static int do_boot_cpu(int apicid, int cpu, struct task_struct *idle)
        unsigned long timeout;
 
        idle->thread.sp = (unsigned long)task_pt_regs(idle);
-       early_gdt_descr.address = (unsigned long)get_cpu_gdt_table(cpu);
+       early_gdt_descr.address = (unsigned long)get_cpu_gdt_rw(cpu);
        initial_code = (unsigned long)start_secondary;
        initial_stack  = idle->thread.sp;
 
index cef39b0976498ccd6ea0b5dc1dc69acd516e9ea6..9500711714362b6640e3b6fee3a08b0b43e32356 100644 (file)
@@ -68,7 +68,7 @@ pgd_t * __init efi_call_phys_prolog(void)
        load_cr3(initial_page_table);
        __flush_tlb_all();
 
-       gdt_descr.address = __pa(get_cpu_gdt_table(0));
+       gdt_descr.address = __pa(get_cpu_gdt_rw(0));
        gdt_descr.size = GDT_SIZE - 1;
        load_gdt(&gdt_descr);
 
@@ -79,7 +79,7 @@ void __init efi_call_phys_epilog(pgd_t *save_pgd)
 {
        struct desc_ptr gdt_descr;
 
-       gdt_descr.address = (unsigned long)get_cpu_gdt_table(0);
+       gdt_descr.address = (unsigned long)get_cpu_gdt_rw(0);
        gdt_descr.size = GDT_SIZE - 1;
        load_gdt(&gdt_descr);
 
index 66ade16c769363ecd1bef435eccfd4977ffd5228..6b05a9219ea2c69abb871d10576e8420ec97eb34 100644 (file)
@@ -95,7 +95,7 @@ static void __save_processor_state(struct saved_context *ctxt)
         * 'pmode_gdt' in wakeup_start.
         */
        ctxt->gdt_desc.size = GDT_SIZE - 1;
-       ctxt->gdt_desc.address = (unsigned long)get_cpu_gdt_table(smp_processor_id());
+       ctxt->gdt_desc.address = (unsigned long)get_cpu_gdt_rw(smp_processor_id());
 
        store_tr(ctxt->tr);
 
@@ -162,7 +162,7 @@ static void fix_processor_context(void)
        int cpu = smp_processor_id();
        struct tss_struct *t = &per_cpu(cpu_tss, cpu);
 #ifdef CONFIG_X86_64
-       struct desc_struct *desc = get_cpu_gdt_table(cpu);
+       struct desc_struct *desc = get_cpu_gdt_rw(cpu);
        tss_desc tss;
 #endif
        set_tss_desc(cpu, t);   /*
@@ -183,6 +183,9 @@ static void fix_processor_context(void)
        load_mm_ldt(current->active_mm);        /* This does lldt */
 
        fpu__resume_cpu();
+
+       /* The processor is back on the direct GDT, load back the fixmap */
+       load_fixmap_gdt(cpu);
 }
 
 /**
index ec1d5c46e58f7cd0719c84686b627e10b7b18dcd..08faa61de5f7709637236ca9418800e5ce087eb5 100644 (file)
@@ -710,7 +710,7 @@ static void load_TLS_descriptor(struct thread_struct *t,
 
        *shadow = t->tls_array[i];
 
-       gdt = get_cpu_gdt_table(cpu);
+       gdt = get_cpu_gdt_rw(cpu);
        maddr = arbitrary_virt_to_machine(&gdt[GDT_ENTRY_TLS_MIN+i]);
        mc = __xen_mc_entry(0);
 
@@ -1545,6 +1545,9 @@ asmlinkage __visible void __init xen_start_kernel(void)
         */
        xen_initial_gdt = &per_cpu(gdt_page, 0);
 
+       /* GDT can only be remapped RO */
+       pg_fixmap_gdt_flags = PAGE_KERNEL_RO;
+
        xen_smp_init();
 
 #ifdef CONFIG_ACPI_NUMA
index 37cb5aad71de364fa50974e236e2d001f51ea687..ebbfe00133f7c5e691b833fecfa5d749a53e9dbe 100644 (file)
@@ -2326,6 +2326,7 @@ static void xen_set_fixmap(unsigned idx, phys_addr_t phys, pgprot_t prot)
 #endif
        case FIX_TEXT_POKE0:
        case FIX_TEXT_POKE1:
+       case FIX_GDT_REMAP_BEGIN ... FIX_GDT_REMAP_END:
                /* All local page mappings */
                pte = pfn_pte(phys, prot);
                break;
index 7ff2f1bfb7ec0592de8466a48c0350f72ec6816e..eaa36162ed4a996e35250037b77add28d19c7867 100644 (file)
@@ -392,7 +392,7 @@ cpu_initialize_context(unsigned int cpu, struct task_struct *idle)
        if (ctxt == NULL)
                return -ENOMEM;
 
-       gdt = get_cpu_gdt_table(cpu);
+       gdt = get_cpu_gdt_rw(cpu);
 
 #ifdef CONFIG_X86_32
        ctxt->user_regs.fs = __KERNEL_PERCPU;
index d71f6323ac001e0e55c50023521c8d7119087de3..b4f79b923aea5d25ea0baae037d5d89fcfd5a659 100644 (file)
@@ -504,7 +504,7 @@ void __init lguest_arch_host_init(void)
                 * byte, not the size, hence the "-1").
                 */
                state->host_gdt_desc.size = GDT_SIZE-1;
-               state->host_gdt_desc.address = (long)get_cpu_gdt_table(i);
+               state->host_gdt_desc.address = (long)get_cpu_gdt_rw(i);
 
                /*
                 * All CPUs on the Host use the same Interrupt Descriptor
@@ -554,8 +554,8 @@ void __init lguest_arch_host_init(void)
                 * The Host needs to be able to use the LGUEST segments on this
                 * CPU, too, so put them in the Host GDT.
                 */
-               get_cpu_gdt_table(i)[GDT_ENTRY_LGUEST_CS] = FULL_EXEC_SEGMENT;
-               get_cpu_gdt_table(i)[GDT_ENTRY_LGUEST_DS] = FULL_SEGMENT;
+               get_cpu_gdt_rw(i)[GDT_ENTRY_LGUEST_CS] = FULL_EXEC_SEGMENT;
+               get_cpu_gdt_rw(i)[GDT_ENTRY_LGUEST_DS] = FULL_SEGMENT;
        }
 
        /*
index 438d4c72c7b36c27982ed8e60b9a42cbff58dc8f..ff563db025b3eb6afa8b5f4345281573328695de 100644 (file)
@@ -54,7 +54,7 @@ __asm__(".text                        \n"
 
 #define Q2_SET_SEL(cpu, selname, address, size) \
 do { \
-       struct desc_struct *gdt = get_cpu_gdt_table((cpu)); \
+       struct desc_struct *gdt = get_cpu_gdt_rw((cpu)); \
        set_desc_base(&gdt[(selname) >> 3], (u32)(address)); \
        set_desc_limit(&gdt[(selname) >> 3], (size) - 1); \
 } while(0)
@@ -95,8 +95,8 @@ static inline u16 call_pnp_bios(u16 func, u16 arg1, u16 arg2, u16 arg3,
                return PNP_FUNCTION_NOT_SUPPORTED;
 
        cpu = get_cpu();
-       save_desc_40 = get_cpu_gdt_table(cpu)[0x40 / 8];
-       get_cpu_gdt_table(cpu)[0x40 / 8] = bad_bios_desc;
+       save_desc_40 = get_cpu_gdt_rw(cpu)[0x40 / 8];
+       get_cpu_gdt_rw(cpu)[0x40 / 8] = bad_bios_desc;
 
        /* On some boxes IRQ's during PnP BIOS calls are deadly.  */
        spin_lock_irqsave(&pnp_bios_lock, flags);
@@ -134,7 +134,7 @@ static inline u16 call_pnp_bios(u16 func, u16 arg1, u16 arg2, u16 arg3,
                             :"memory");
        spin_unlock_irqrestore(&pnp_bios_lock, flags);
 
-       get_cpu_gdt_table(cpu)[0x40 / 8] = save_desc_40;
+       get_cpu_gdt_rw(cpu)[0x40 / 8] = save_desc_40;
        put_cpu();
 
        /* If we get here and this is set then the PnP BIOS faulted on us. */
@@ -477,7 +477,7 @@ void pnpbios_calls_init(union pnp_bios_install_struct *header)
        pnp_bios_callpoint.segment = PNP_CS16;
 
        for_each_possible_cpu(i) {
-               struct desc_struct *gdt = get_cpu_gdt_table(i);
+               struct desc_struct *gdt = get_cpu_gdt_rw(i);
                if (!gdt)
                        continue;
                set_desc_base(&gdt[GDT_ENTRY_PNPBIOS_CS32],