Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/elf: Add a new FPU buffer layout info to x86 core files

Add a new .note section containing type, size, offset and flags of every
xfeature that is present.

This information will be used by debuggers to understand the XSAVE layout of
the machine where the core file has been dumped, and to read XSAVE registers,
especially during cross-platform debugging.

The XSAVE layouts of modern AMD and Intel CPUs differ, especially since
Memory Protection Keys and the AVX-512 features have been inculcated into
the AMD CPUs.

Since AMD never adopted (and hence never left room in the XSAVE layout for)
the Intel MPX feature, tools like GDB had assumed a fixed XSAVE layout
matching that of Intel (based on the XCR0 mask).

Hence, core dumps from AMD CPUs didn't match the known size for the XCR0 mask.
This resulted in GDB and other tools not being able to access the values of
the AVX-512 and PKRU registers on AMD CPUs.

To solve this, an interim solution has been accepted into GDB, and is already
a part of GDB 14, see

https://sourceware.org/pipermail/gdb-patches/2023-March/198081.html.

But it depends on heuristics based on the total XSAVE register set size
and the XCR0 mask to infer the layouts of the various register blocks
for core dumps, and hence, is not a foolproof mechanism to determine the
layout of the XSAVE area.

Therefore, add a new core dump note in order to allow GDB/LLDB and other
relevant tools to determine the layout of the XSAVE area of the machine where
the corefile was dumped.

The new core dump note (which is being proposed as a per-process .note
section), NT_X86_XSAVE_LAYOUT (0x205) contains an array of structures.

Each structure describes an individual extended feature containing
offset, size and flags in this format:

struct x86_xfeat_component {
u32 type;
u32 size;
u32 offset;
u32 flags;
};

and in an independent manner, allowing for future extensions without depending
on hw arch specifics like CPUID etc.

[ bp: Massage commit message, zap trailing whitespace. ]

Co-developed-by: Jini Susan George <jinisusan.george@amd.com>
Signed-off-by: Jini Susan George <jinisusan.george@amd.com>
Co-developed-by: Borislav Petkov (AMD) <bp@alien8.de>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Signed-off-by: Vignesh Balasubramanian <vigbalas@amd.com>
Link: https://lore.kernel.org/r/20240725161017.112111-2-vigbalas@amd.com

authored by

Vignesh Balasubramanian and committed by
Borislav Petkov (AMD)
ba386777 8400291e

+109 -2
+1
arch/x86/Kconfig
··· 107 107 select ARCH_HAS_DEBUG_WX 108 108 select ARCH_HAS_ZONE_DMA_SET if EXPERT 109 109 select ARCH_HAVE_NMI_SAFE_CMPXCHG 110 + select ARCH_HAVE_EXTRA_ELF_NOTES 110 111 select ARCH_MHP_MEMMAP_ON_MEMORY_ENABLE 111 112 select ARCH_MIGHT_HAVE_ACPI_PDC if ACPI 112 113 select ARCH_MIGHT_HAVE_PC_PARPORT
+16
arch/x86/include/uapi/asm/elf.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ 2 + #ifndef _UAPI_ASM_X86_ELF_H 3 + #define _UAPI_ASM_X86_ELF_H 4 + 5 + #include <linux/types.h> 6 + 7 + struct x86_xfeat_component { 8 + __u32 type; 9 + __u32 size; 10 + __u32 offset; 11 + __u32 flags; 12 + } __packed; 13 + 14 + _Static_assert(sizeof(struct x86_xfeat_component) % 4 == 0, "x86_xfeat_component is not aligned"); 15 + 16 + #endif /* _UAPI_ASM_X86_ELF_H */
+89
arch/x86/kernel/fpu/xstate.c
··· 13 13 #include <linux/seq_file.h> 14 14 #include <linux/proc_fs.h> 15 15 #include <linux/vmalloc.h> 16 + #include <linux/coredump.h> 16 17 17 18 #include <asm/fpu/api.h> 18 19 #include <asm/fpu/regset.h> ··· 23 22 #include <asm/tlbflush.h> 24 23 #include <asm/prctl.h> 25 24 #include <asm/elf.h> 25 + 26 + #include <uapi/asm/elf.h> 26 27 27 28 #include "context.h" 28 29 #include "internal.h" ··· 1841 1838 return 0; 1842 1839 } 1843 1840 #endif /* CONFIG_PROC_PID_ARCH_STATUS */ 1841 + 1842 + #ifdef CONFIG_COREDUMP 1843 + static const char owner_name[] = "LINUX"; 1844 + 1845 + /* 1846 + * Dump type, size, offset and flag values for every xfeature that is present. 1847 + */ 1848 + static int dump_xsave_layout_desc(struct coredump_params *cprm) 1849 + { 1850 + int num_records = 0; 1851 + int i; 1852 + 1853 + for_each_extended_xfeature(i, fpu_user_cfg.max_features) { 1854 + struct x86_xfeat_component xc = { 1855 + .type = i, 1856 + .size = xstate_sizes[i], 1857 + .offset = xstate_offsets[i], 1858 + /* reserved for future use */ 1859 + .flags = 0, 1860 + }; 1861 + 1862 + if (!dump_emit(cprm, &xc, sizeof(xc))) 1863 + return 0; 1864 + 1865 + num_records++; 1866 + } 1867 + return num_records; 1868 + } 1869 + 1870 + static u32 get_xsave_desc_size(void) 1871 + { 1872 + u32 cnt = 0; 1873 + u32 i; 1874 + 1875 + for_each_extended_xfeature(i, fpu_user_cfg.max_features) 1876 + cnt++; 1877 + 1878 + return cnt * (sizeof(struct x86_xfeat_component)); 1879 + } 1880 + 1881 + int elf_coredump_extra_notes_write(struct coredump_params *cprm) 1882 + { 1883 + int num_records = 0; 1884 + struct elf_note en; 1885 + 1886 + if (!fpu_user_cfg.max_features) 1887 + return 0; 1888 + 1889 + en.n_namesz = sizeof(owner_name); 1890 + en.n_descsz = get_xsave_desc_size(); 1891 + en.n_type = NT_X86_XSAVE_LAYOUT; 1892 + 1893 + if (!dump_emit(cprm, &en, sizeof(en))) 1894 + return 1; 1895 + if (!dump_emit(cprm, owner_name, en.n_namesz)) 1896 + return 1; 1897 + if (!dump_align(cprm, 4)) 1898 + return 1; 1899 + 1900 + num_records = dump_xsave_layout_desc(cprm); 1901 + if (!num_records) 1902 + return 1; 1903 + 1904 + /* Total size should be equal to the number of records */ 1905 + if ((sizeof(struct x86_xfeat_component) * num_records) != en.n_descsz) 1906 + return 1; 1907 + 1908 + return 0; 1909 + } 1910 + 1911 + int elf_coredump_extra_notes_size(void) 1912 + { 1913 + int size; 1914 + 1915 + if (!fpu_user_cfg.max_features) 1916 + return 0; 1917 + 1918 + /* .note header */ 1919 + size = sizeof(struct elf_note); 1920 + /* Name plus alignment to 4 bytes */ 1921 + size += roundup(sizeof(owner_name), 4); 1922 + size += get_xsave_desc_size(); 1923 + 1924 + return size; 1925 + } 1926 + #endif /* CONFIG_COREDUMP */
+2 -2
fs/binfmt_elf.c
··· 2039 2039 { 2040 2040 size_t sz = info.size; 2041 2041 2042 - /* For cell spufs */ 2042 + /* For cell spufs and x86 xstate */ 2043 2043 sz += elf_coredump_extra_notes_size(); 2044 2044 2045 2045 phdr4note = kmalloc(sizeof(*phdr4note), GFP_KERNEL); ··· 2103 2103 if (!write_note_info(&info, cprm)) 2104 2104 goto end_coredump; 2105 2105 2106 - /* For cell spufs */ 2106 + /* For cell spufs and x86 xstate */ 2107 2107 if (elf_coredump_extra_notes_write(cprm)) 2108 2108 goto end_coredump; 2109 2109
+1
include/uapi/linux/elf.h
··· 411 411 #define NT_X86_XSTATE 0x202 /* x86 extended state using xsave */ 412 412 /* Old binutils treats 0x203 as a CET state */ 413 413 #define NT_X86_SHSTK 0x204 /* x86 SHSTK state */ 414 + #define NT_X86_XSAVE_LAYOUT 0x205 /* XSAVE layout description */ 414 415 #define NT_S390_HIGH_GPRS 0x300 /* s390 upper register halves */ 415 416 #define NT_S390_TIMER 0x301 /* s390 timer register */ 416 417 #define NT_S390_TODCMP 0x302 /* s390 TOD clock comparator register */