x86 fixes
Subsystems (10)
Linus,
Please pull the latest x86-urgent-for-linus git tree from:
git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git x86-urgent-for-linus
# HEAD: f4eafd8bcd5229e998aa252627703b8462c3b90f x86/mm: Fix vmalloc_fault() to handle large pages properly
This is unusually large, partly due to the EFI fixes that prevent accidental deletion of EFI variables through efivarfs that may brick machines. These fixes are somewhat involved to maintain compatibility with existing install methods and other usage modes, while trying to turn off the 'rm -rf' bricking vector.
Other fixes are for large page ioremap()s and for non-temporal user-memcpy()s.
out-of-topic modifications in x86-urgent-for-linus:
-----------------------------------------------------
drivers/char/hpet.c # 4e7f9df25874: hpet: Drop stale URLs
drivers/firmware/efi/efivars.c # 8282f5d9c17f: efi: Make our variable valid
# e0d64e6a880e: efi: Use ucs2_as_utf8 in efi
drivers/firmware/efi/vars.c # e246eb568bc4: efi: Add pstore variables to
# ed8b0de5a33d: efi: Make efivarfs entries i
# 8282f5d9c17f: efi: Make our variable valid
# 3dcb1f55dfc7: efi: Do variable name valida
fs/efivarfs/file.c # ed8b0de5a33d: efi: Make efivarfs entries i
fs/efivarfs/inode.c # ed8b0de5a33d: efi: Make efivarfs entries i
fs/efivarfs/internal.h # ed8b0de5a33d: efi: Make efivarfs entries i
fs/efivarfs/super.c # ed8b0de5a33d: efi: Make efivarfs entries i
# e0d64e6a880e: efi: Use ucs2_as_utf8 in efi
include/linux/ucs2_string.h # 73500267c930: lib/ucs2_string: Add ucs2 ->
lib/ucs2_string.c # a68075908a37: lib/ucs2_string: Correct ucs
# 73500267c930: lib/ucs2_string: Add ucs2 -> tools/testing/selftests/efivarfs/efivarfs.sh# ed8b0de5a33d: efi: Make efivarfs entries i tools/testing/selftests/efivarfs/open-unlink.c# ed8b0de5a33d: efi: Make efivarfs entries i
Thanks,
Ingo
------------------>
Jason Andryuk (1):
lib/ucs2_string: Correct ucs2 -> utf8 conversion
Matt Fleming (1):
efi: Add pstore variables to the deletion whitelist
Michael S. Tsirkin (1):
hpet: Drop stale URLs
Peter Jones (5):
lib/ucs2_string: Add ucs2 -> utf8 helper functions
efi: Use ucs2_as_utf8 in efivarfs instead of open coding a bad version
efi: Do variable name validation tests in utf8
efi: Make our variable validation list include the guid
efi: Make efivarfs entries immutable by default
Toshi Kani (3):
x86/uaccess/64: Make the __copy_user_nocache() assembly code more readable x86/uaccess/64: Handle the caching of 4-byte nocache copies properly in __copy_user_nocache() x86/mm: Fix vmalloc_fault() to handle large pages properly
Documentation/filesystems/efivarfs.txt | 7 ++ Documentation/timers/hpet.txt | 4 +- arch/x86/Kconfig | 4 +- arch/x86/lib/copy_user_64.S | 142 +++++++++++++++++------- arch/x86/mm/fault.c | 15 ++- drivers/char/hpet.c | 2 +- drivers/firmware/efi/efivars.c | 35 +++--- drivers/firmware/efi/vars.c | 144 ++++++++++++++++++------- fs/efivarfs/file.c | 70 ++++++++++++ fs/efivarfs/inode.c | 30 ++++-- fs/efivarfs/internal.h | 3 +- fs/efivarfs/super.c | 16 +-- include/linux/efi.h | 5 +- include/linux/ucs2_string.h | 4 + lib/ucs2_string.c | 62 +++++++++++ tools/testing/selftests/efivarfs/efivarfs.sh | 19 +++- tools/testing/selftests/efivarfs/open-unlink.c | 72 ++++++++++++- 17 files changed, 500 insertions(+), 134 deletions(-)
Documentation/filesystems/efivarfs.txt +7 -0
@@ -14,3 +14,10 @@ filesystem. efivarfs is typically mounted like this, mount -t efivarfs none /sys/firmware/efi/efivars + +Due to the presence of numerous firmware bugs where removing non-standard +UEFI variables causes the system firmware to fail to POST, efivarfs +files that are not well-known standardized variables are created +as immutable files. This doesn't prevent removal - "chattr -i" will work - +but it does prevent this kind of failure from being accomplished +accidentally.
Documentation/timers/hpet.txt +1 -3
@@ -1,9 +1,7 @@ High Precision Event Timer Driver for Linux The High Precision Event Timer (HPET) hardware follows a specification -by Intel and Microsoft which can be found at - - http://www.intel.com/hardwaredesign/hpetspec_1.pdf +by Intel and Microsoft, revision 1. Each HPET has one fixed-rate counter (at 10+ MHz, hence "High Precision") and up to 32 comparators. Normally three or more comparators are provided,
arch/x86/Kconfig +2 -2
@@ -778,8 +778,8 @@ config HPET_TIMER HPET is the next generation timer replacing legacy 8254s. The HPET provides a stable time base on SMP systems, unlike the TSC, but it is more expensive to access, - as it is off-chip. You can find the HPET spec at - <http://www.intel.com/hardwaredesign/hpetspec_1.pdf>. + as it is off-chip. The interface used is documented + in the HPET spec, revision 1. You can safely choose Y here. However, HPET will only be activated if the platform and the BIOS support this feature.
arch/x86/lib/copy_user_64.S +101 -41
178 lines not on this page · raw message
arch/x86/mm/fault.c +11 -4
@@ -287,6 +287,9 @@ static noinline int vmalloc_fault(unsigned long address)if (!pmd_k)return -1 ; +if (pmd_huge (*pmd_k)) +return 0 ; + pte_k =pte_offset_kernel (pmd_k, address);if (!pte_present (*pte_k))return -1 ;
@@ -360,8 +363,6 @@ void vmalloc_sync_all(void)* 64-bit: * * Handle a fault on the vmalloc area -* -* This assumes no large pages in there. */ static noinline int vmalloc_fault(unsigned long address) {
@@ -403,17 +404,23 @@ static noinline int vmalloc_fault(unsigned long address)if (pud_none (*pud_ref))return -1 ; -if (pud_none (*pud) ||pud_page_vaddr (*pud) !=pud_page_vaddr (*pud_ref)) +if (pud_none (*pud) ||pud_pfn (*pud) !=pud_pfn (*pud_ref))BUG (); +if (pud_huge (*pud)) +return 0 ; + pmd =pmd_offset (pud, address); pmd_ref =pmd_offset (pud_ref, address);if (pmd_none (*pmd_ref))return -1 ; -if (pmd_none (*pmd) ||pmd_page (*pmd) !=pmd_page (*pmd_ref)) +if (pmd_none (*pmd) ||pmd_pfn (*pmd) !=pmd_pfn (*pmd_ref))BUG (); +if (pmd_huge (*pmd)) +return 0 ; + pte_ref =pte_offset_kernel (pmd_ref, address);if (!pte_present (*pte_ref))return -1 ;
drivers/char/hpet.c +1 -1
@@ -42,7 +42,7 @@/* * The High Precision Event Timer driver. * This driver is closely modelled after the rtc.c driver. -* http://www.intel.com/hardwaredesign/hpetspec_1.pdf +* See HPET spec revision 1. */ #define HPET_USER_FREQ (64)#define HPET_DRIFT (500)
drivers/firmware/efi/efivars.c +14 -21
@@ -221,7 +221,7 @@ sanity_check(struct efi_variable *var, efi_char16_t *name, efi_guid_t vendor, }if ((attributes & ~EFI_VARIABLE_MASK ) !=0 || -efivar_validate (name, data, size) == false) { +efivar_validate (vendor, name, data, size) == false) {printk (KERN_ERR "efivars: Malformed variable content\n" );return -EINVAL ; }
@@ -447,7 +447,8 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj, }if ((attributes & ~EFI_VARIABLE_MASK ) !=0 || -efivar_validate (name, data, size) == false) { +efivar_validate (new_var->VendorGuid, name, data, + size) == false) {printk (KERN_ERR "efivars: Malformed variable content\n" );return -EINVAL ; }
@@ -540,38 +541,30 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,static int efivar_create_sysfs_entry (struct efivar_entry *new_var) { -int i, short_name_size; +int short_name_size;char *short_name; -unsigned long variable_name_size; -efi_char16_t *variable_name; +unsigned long utf8_name_size; +efi_char16_t *variable_name = new_var->var.VariableName;int ret; - variable_name = new_var->var.VariableName; - variable_name_size =ucs2_strlen (variable_name) *sizeof (efi_char16_t); -/* -* Length of the variable bytes in ASCII, plus the '-' separator, +* Length of the variable bytes in UTF8, plus the '-' separator, * plus the GUID, plus trailing NUL */ - short_name_size = variable_name_size /sizeof (efi_char16_t) - +1 +EFI_VARIABLE_GUID_LEN +1 ; - - short_name =kzalloc (short_name_size,GFP_KERNEL ); + utf8_name_size =ucs2_utf8size (variable_name); + short_name_size = utf8_name_size +1 +EFI_VARIABLE_GUID_LEN +1 ; + short_name =kmalloc (short_name_size,GFP_KERNEL );if (!short_name)return -ENOMEM ; -/* Convert Unicode to normal chars (assume top bits are 0), -ala UTF-8 */ -for (i=0 ; i < (int )(variable_name_size /sizeof (efi_char16_t)); i++) { - short_name[i] = variable_name[i] &0xFF ; - } +ucs2_as_utf8 (short_name, variable_name, short_name_size); +/* This is ugly, but necessary to separate one vendor's private variables from another's. */ - - *(short_name +strlen (short_name)) ='-' ; + short_name[utf8_name_size] ='-' ;efi_guid_to_str (&new_var->var.VendorGuid, - short_name +strlen (short_name)); + short_name + utf8_name_size +1 ); new_var->kobj.kset = efivars_kset;
drivers/firmware/efi/vars.c +105 -39
179 lines not on this page · raw message
fs/efivarfs/file.c +70 -0
85 lines not on this page · raw message
fs/efivarfs/inode.c +19 -11
@@ -15,7 +15,8 @@#include "internal.h" struct inode *efivarfs_get_inode (struct super_block *sb, -const struct inode *dir,int mode,dev_t dev) +const struct inode *dir,int mode, +dev_t dev,bool is_removable) {struct inode *inode =new_inode (sb);
@@ -23,6 +24,7 @@ struct inode *efivarfs_get_inode(struct super_block *sb, inode->i_ino =get_next_ino (); inode->i_mode = mode; inode->i_atime = inode->i_mtime = inode->i_ctime =CURRENT_TIME ; + inode->i_flags = is_removable ?0 :S_IMMUTABLE ;switch (mode &S_IFMT ) {case S_IFREG : inode->i_fop = &efivarfs_file_operations;
@@ -102,22 +104,17 @@ static void efivarfs_hex_to_guid(const char *str, efi_guid_t *guid)static int efivarfs_create (struct inode *dir,struct dentry *dentry,umode_t mode,bool excl) { -struct inode *inode; +struct inode *inode =NULL ;struct efivar_entry *var;int namelen, i =0 , err =0 ; +bool is_removable = false;if (!efivarfs_valid_name (dentry->d_name.name, dentry->d_name.len))return -EINVAL ; - inode =efivarfs_get_inode (dir->i_sb, dir, mode,0 ); -if (!inode) -return -ENOMEM ; - var =kzalloc (sizeof (struct efivar_entry ),GFP_KERNEL ); -if (!var) { - err = -ENOMEM ; - goto out; - } +if (!var) +return -ENOMEM ;/* length of the variable name itself: remove GUID and separator */ namelen = dentry->d_name.len -EFI_VARIABLE_GUID_LEN -1 ;
@@ -125,6 +122,16 @@ static int efivarfs_create(struct inode *dir, struct dentry *dentry,efivarfs_hex_to_guid (dentry->d_name.name + namelen +1 , &var->var.VendorGuid); +if (efivar_variable_is_removable (var->var.VendorGuid, + dentry->d_name.name, namelen)) + is_removable = true; + + inode =efivarfs_get_inode (dir->i_sb, dir, mode,0 , is_removable); +if (!inode) { + err = -ENOMEM ; + goto out; + } +for (i =0 ; i < namelen; i++) var->var.VariableName[i] = dentry->d_name.name[i];
@@ -138,7 +145,8 @@ static int efivarfs_create(struct inode *dir, struct dentry *dentry, out:if (err) {kfree (var); -iput (inode); +if (inode) +iput (inode); }return err; }
fs/efivarfs/internal.h +2 -1
@@ -15,7 +15,8 @@ extern const struct file_operations efivarfs_file_operations;extern const struct inode_operations efivarfs_dir_inode_operations;extern bool efivarfs_valid_name (const char *str,int len);extern struct inode *efivarfs_get_inode (struct super_block *sb, -const struct inode *dir,int mode,dev_t dev); +const struct inode *dir,int mode,dev_t dev, +bool is_removable);extern struct list_head efivarfs_list;
fs/efivarfs/super.c +10 -6
@@ -118,8 +118,9 @@ static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor,struct dentry *dentry, *root = sb->s_root;unsigned long size =0 ;char *name; -int len, i; +int len;int err = -ENOMEM ; +bool is_removable = false; entry =kzalloc (sizeof (*entry),GFP_KERNEL );if (!entry)
@@ -128,15 +129,17 @@ static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor,memcpy (entry->var.VariableName, name16, name_size);memcpy (&(entry->var.VendorGuid), &vendor,sizeof (efi_guid_t)); - len =ucs2_strlen (entry->var.VariableName); + len =ucs2_utf8size (entry->var.VariableName);/* name, plus '-', plus GUID, plus NUL*/ name =kmalloc (len +1 +EFI_VARIABLE_GUID_LEN +1 ,GFP_KERNEL );if (!name) goto fail; -for (i =0 ; i < len; i++) - name[i] = entry->var.VariableName[i] &0xFF ; +ucs2_as_utf8 (name, entry->var.VariableName, len); + +if (efivar_variable_is_removable (entry->var.VendorGuid, name, len)) + is_removable = true; name[len] ='-' ;
@@ -144,7 +147,8 @@ static int efivarfs_callback(efi_char16_t *name16, efi_guid_t vendor, name[len +EFI_VARIABLE_GUID_LEN +1 ] ='\0' ; - inode =efivarfs_get_inode (sb,d_inode (root),S_IFREG |0644 ,0 ); + inode =efivarfs_get_inode (sb,d_inode (root),S_IFREG |0644 ,0 , + is_removable);if (!inode) goto fail_name;
@@ -200,7 +204,7 @@ static int efivarfs_fill_super(struct super_block *sb, void *data, int silent) sb->s_d_op = &efivarfs_d_ops; sb->s_time_gran =1 ; - inode =efivarfs_get_inode (sb,NULL ,S_IFDIR |0755 ,0 ); + inode =efivarfs_get_inode (sb,NULL ,S_IFDIR |0755 ,0 , true);if (!inode)return -ENOMEM ; inode->i_op = &efivarfs_dir_inode_operations;
include/linux/efi.h +4 -1
@@ -1199,7 +1199,10 @@ int efivar_entry_iter(int (*func)(struct efivar_entry *, void *),struct efivar_entry *efivar_entry_find (efi_char16_t *name,efi_guid_t guid,struct list_head *head,bool remove); -bool efivar_validate (efi_char16_t *var_name,u8 *data,unsigned long len); +bool efivar_validate (efi_guid_t vendor,efi_char16_t *var_name,u8 *data, +unsigned long data_size); +bool efivar_variable_is_removable (efi_guid_t vendor,const char *name, +size_t len);extern struct work_struct efivar_work;void efivar_run_worker (void );
include/linux/ucs2_string.h +4 -0
@@ -11,4 +11,8 @@ unsigned long ucs2_strlen(const ucs2_char_t *s);unsigned long ucs2_strsize (const ucs2_char_t *data,unsigned long maxlength);int ucs2_strncmp (const ucs2_char_t *a,const ucs2_char_t *b,size_t len); +unsigned long ucs2_utf8size (const ucs2_char_t *src); +unsigned long ucs2_as_utf8 (u8 *dest,const ucs2_char_t *src, +unsigned long maxlength); +#endif /* _LINUX_UCS2_STRING_H_ */
lib/ucs2_string.c +62 -0
65 lines not on this page · raw message
tools/testing/selftests/efivarfs/efivarfs.sh +16 -3
@@ -88,7 +88,11 @@ test_delete() exit 1 fi - rm $file + rm $file 2>/dev/null + if [ $? -ne 0 ]; then + chattr -i $file + rm $file + fi if [ -e $file ]; then echo "$file couldn't be deleted" >&2
@@ -111,6 +115,7 @@ test_zero_size_delete() exit 1 fi + chattr -i $file printf "$attrs" > $file if [ -e $file ]; then
@@ -141,7 +146,11 @@ test_valid_filenames() echo "$file could not be created" >&2 ret=1 else - rm $file + rm $file 2>/dev/null + if [ $? -ne 0 ]; then + chattr -i $file + rm $file + fi fi done
@@ -174,7 +183,11 @@ test_invalid_filenames() if [ -e $file ]; then echo "Creating $file should have failed" >&2 - rm $file + rm $file 2>/dev/null + if [ $? -ne 0 ]; then + chattr -i $file + rm $file + fi ret=1 fi done
tools/testing/selftests/efivarfs/open-unlink.c +71 -1
94 lines not on this page · raw message