--- /dev/null
+From ff7a167961d1b97e0e205f245f806e564d3505e7 Mon Sep 17 00:00:00 2001
+From: Ard Biesheuvel <ardb@kernel.org>
+Date: Mon, 5 Dec 2022 11:31:25 +0100
+Subject: arm64: efi: Execute runtime services from a dedicated stack
+
+From: Ard Biesheuvel <ardb@kernel.org>
+
+commit ff7a167961d1b97e0e205f245f806e564d3505e7 upstream.
+
+With the introduction of PRMT in the ACPI subsystem, the EFI rts
+workqueue is no longer the only caller of efi_call_virt_pointer() in the
+kernel. This means the EFI runtime services lock is no longer sufficient
+to manage concurrent calls into firmware, but also that firmware calls
+may occur that are not marshalled via the workqueue mechanism, but
+originate directly from the caller context.
+
+For added robustness, and to ensure that the runtime services have 8 KiB
+of stack space available as per the EFI spec, introduce a spinlock
+protected EFI runtime stack of 8 KiB, where the spinlock also ensures
+serialization between the EFI rts workqueue (which itself serializes EFI
+runtime calls) and other callers of efi_call_virt_pointer().
+
+While at it, use the stack pivot to avoid reloading the shadow call
+stack pointer from the ordinary stack, as doing so could produce a
+gadget to defeat it.
+
+Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
+Cc: Lee Jones <lee@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/arm64/include/asm/efi.h | 3 +++
+ arch/arm64/kernel/efi-rt-wrapper.S | 13 ++++++++++++-
+ arch/arm64/kernel/efi.c | 27 +++++++++++++++++++++++++++
+ 3 files changed, 42 insertions(+), 1 deletion(-)
+
+--- a/arch/arm64/include/asm/efi.h
++++ b/arch/arm64/include/asm/efi.h
+@@ -25,6 +25,7 @@ int efi_set_mapping_permissions(struct m
+ ({ \
+ efi_virtmap_load(); \
+ __efi_fpsimd_begin(); \
++ spin_lock(&efi_rt_lock); \
+ })
+
+ #undef arch_efi_call_virt
+@@ -33,10 +34,12 @@ int efi_set_mapping_permissions(struct m
+
+ #define arch_efi_call_virt_teardown() \
+ ({ \
++ spin_unlock(&efi_rt_lock); \
+ __efi_fpsimd_end(); \
+ efi_virtmap_unload(); \
+ })
+
++extern spinlock_t efi_rt_lock;
+ efi_status_t __efi_rt_asm_wrapper(void *, const char *, ...);
+
+ #define ARCH_EFI_IRQ_FLAGS_MASK (PSR_D_BIT | PSR_A_BIT | PSR_I_BIT | PSR_F_BIT)
+--- a/arch/arm64/kernel/efi-rt-wrapper.S
++++ b/arch/arm64/kernel/efi-rt-wrapper.S
+@@ -16,6 +16,12 @@ SYM_FUNC_START(__efi_rt_asm_wrapper)
+ */
+ stp x1, x18, [sp, #16]
+
++ ldr_l x16, efi_rt_stack_top
++ mov sp, x16
++#ifdef CONFIG_SHADOW_CALL_STACK
++ str x18, [sp, #-16]!
++#endif
++
+ /*
+ * We are lucky enough that no EFI runtime services take more than
+ * 5 arguments, so all are passed in registers rather than via the
+@@ -29,6 +35,7 @@ SYM_FUNC_START(__efi_rt_asm_wrapper)
+ mov x4, x6
+ blr x8
+
++ mov sp, x29
+ ldp x1, x2, [sp, #16]
+ cmp x2, x18
+ ldp x29, x30, [sp], #32
+@@ -42,6 +49,10 @@ SYM_FUNC_START(__efi_rt_asm_wrapper)
+ * called with preemption disabled and a separate shadow stack is used
+ * for interrupts.
+ */
+- mov x18, x2
++#ifdef CONFIG_SHADOW_CALL_STACK
++ ldr_l x18, efi_rt_stack_top
++ ldr x18, [x18, #-16]
++#endif
++
+ b efi_handle_corrupted_x18 // tail call
+ SYM_FUNC_END(__efi_rt_asm_wrapper)
+--- a/arch/arm64/kernel/efi.c
++++ b/arch/arm64/kernel/efi.c
+@@ -144,3 +144,30 @@ asmlinkage efi_status_t efi_handle_corru
+ pr_err_ratelimited(FW_BUG "register x18 corrupted by EFI %s\n", f);
+ return s;
+ }
++
++DEFINE_SPINLOCK(efi_rt_lock);
++
++asmlinkage u64 *efi_rt_stack_top __ro_after_init;
++
++/* EFI requires 8 KiB of stack space for runtime services */
++static_assert(THREAD_SIZE >= SZ_8K);
++
++static int __init arm64_efi_rt_init(void)
++{
++ void *p;
++
++ if (!efi_enabled(EFI_RUNTIME_SERVICES))
++ return 0;
++
++ p = __vmalloc_node(THREAD_SIZE, THREAD_ALIGN, GFP_KERNEL,
++ NUMA_NO_NODE, &&l);
++l: if (!p) {
++ pr_warn("Failed to allocate EFI runtime stack\n");
++ clear_bit(EFI_RUNTIME_SERVICES, &efi.flags);
++ return -ENOMEM;
++ }
++
++ efi_rt_stack_top = p + THREAD_SIZE;
++ return 0;
++}
++core_initcall(arm64_efi_rt_init);
--- /dev/null
+From c877ce47e1378dbafa6f1bf84c0c83a05ca8972a Mon Sep 17 00:00:00 2001
+From: Paulo Alcantara <pc@cjr.nz>
+Date: Mon, 12 Dec 2022 23:39:37 -0300
+Subject: cifs: reduce roundtrips on create/qinfo requests
+
+From: Paulo Alcantara <pc@cjr.nz>
+
+commit c877ce47e1378dbafa6f1bf84c0c83a05ca8972a upstream.
+
+To work around some Window servers that return
+STATUS_OBJECT_NAME_INVALID on query infos under DFS namespaces that
+contain non-ASCII characters, we started checking for -ENOENT on every
+file open, and if so, then send additional requests to figure out
+whether it is a DFS link or not. It means that all those requests
+will be sent to every non-existing file.
+
+So, in order to reduce the number of roundtrips, check earlier whether
+status code is STATUS_OBJECT_NAME_INVALID and tcon supports dfs, and
+if so, then map -ENOENT to -EREMOTE so mount or automount will take
+care of chasing the DFS link -- if it isn't an DFS link, then -ENOENT
+will be returned appropriately.
+
+Before patch
+
+ SMB2 438 Create Request File: ada.test\dfs\foo;GetInfo Request...
+ SMB2 310 Create Response, Error: STATUS_OBJECT_NAME_NOT_FOUND;...
+ SMB2 228 Ioctl Request FSCTL_DFS_GET_REFERRALS, File: \ada.test\dfs\foo
+ SMB2 143 Ioctl Response, Error: STATUS_OBJECT_PATH_NOT_FOUND
+ SMB2 438 Create Request File: ada.test\dfs\foo;GetInfo Request...
+ SMB2 310 Create Response, Error: STATUS_OBJECT_NAME_NOT_FOUND;...
+ SMB2 228 Ioctl Request FSCTL_DFS_GET_REFERRALS, File: \ada.test\dfs\foo
+ SMB2 143 Ioctl Response, Error: STATUS_OBJECT_PATH_NOT_FOUND
+
+After patch
+
+ SMB2 438 Create Request File: ada.test\dfs\foo;GetInfo Request...
+ SMB2 310 Create Response, Error: STATUS_OBJECT_NAME_NOT_FOUND;...
+ SMB2 438 Create Request File: ada.test\dfs\foo;GetInfo Request...
+ SMB2 310 Create Response, Error: STATUS_OBJECT_NAME_NOT_FOUND;...
+
+Signed-off-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ fs/cifs/connect.c | 16 ----------------
+ fs/cifs/inode.c | 6 ------
+ fs/cifs/misc.c | 45 ---------------------------------------------
+ fs/cifs/smb2inode.c | 45 ++++++++++++++++++++++++++++++++-------------
+ fs/cifs/smb2ops.c | 28 ++++++++++++++++++++++++----
+ 5 files changed, 56 insertions(+), 84 deletions(-)
+
+--- a/fs/cifs/connect.c
++++ b/fs/cifs/connect.c
+@@ -3554,9 +3554,6 @@ static int is_path_remote(struct mount_c
+ struct cifs_tcon *tcon = mnt_ctx->tcon;
+ struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
+ char *full_path;
+-#ifdef CONFIG_CIFS_DFS_UPCALL
+- bool nodfs = cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS;
+-#endif
+
+ if (!server->ops->is_path_accessible)
+ return -EOPNOTSUPP;
+@@ -3573,19 +3570,6 @@ static int is_path_remote(struct mount_c
+
+ rc = server->ops->is_path_accessible(xid, tcon, cifs_sb,
+ full_path);
+-#ifdef CONFIG_CIFS_DFS_UPCALL
+- if (nodfs) {
+- if (rc == -EREMOTE)
+- rc = -EOPNOTSUPP;
+- goto out;
+- }
+-
+- /* path *might* exist with non-ASCII characters in DFS root
+- * try again with full path (only if nodfs is not set) */
+- if (rc == -ENOENT && is_tcon_dfs(tcon))
+- rc = cifs_dfs_query_info_nonascii_quirk(xid, tcon, cifs_sb,
+- full_path);
+-#endif
+ if (rc != 0 && rc != -EREMOTE)
+ goto out;
+
+--- a/fs/cifs/inode.c
++++ b/fs/cifs/inode.c
+@@ -991,12 +991,6 @@ int cifs_get_inode_info(struct inode **i
+ }
+ rc = server->ops->query_path_info(xid, tcon, cifs_sb, full_path, &tmp_data,
+ &adjust_tz, &is_reparse_point);
+-#ifdef CONFIG_CIFS_DFS_UPCALL
+- if (rc == -ENOENT && is_tcon_dfs(tcon))
+- rc = cifs_dfs_query_info_nonascii_quirk(xid, tcon,
+- cifs_sb,
+- full_path);
+-#endif
+ data = &tmp_data;
+ }
+
+--- a/fs/cifs/misc.c
++++ b/fs/cifs/misc.c
+@@ -1314,49 +1314,4 @@ int cifs_update_super_prepath(struct cif
+ cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
+ return 0;
+ }
+-
+-/** cifs_dfs_query_info_nonascii_quirk
+- * Handle weird Windows SMB server behaviour. It responds with
+- * STATUS_OBJECT_NAME_INVALID code to SMB2 QUERY_INFO request
+- * for "\<server>\<dfsname>\<linkpath>" DFS reference,
+- * where <dfsname> contains non-ASCII unicode symbols.
+- *
+- * Check such DFS reference.
+- */
+-int cifs_dfs_query_info_nonascii_quirk(const unsigned int xid,
+- struct cifs_tcon *tcon,
+- struct cifs_sb_info *cifs_sb,
+- const char *linkpath)
+-{
+- char *treename, *dfspath, sep;
+- int treenamelen, linkpathlen, rc;
+-
+- treename = tcon->tree_name;
+- /* MS-DFSC: All paths in REQ_GET_DFS_REFERRAL and RESP_GET_DFS_REFERRAL
+- * messages MUST be encoded with exactly one leading backslash, not two
+- * leading backslashes.
+- */
+- sep = CIFS_DIR_SEP(cifs_sb);
+- if (treename[0] == sep && treename[1] == sep)
+- treename++;
+- linkpathlen = strlen(linkpath);
+- treenamelen = strnlen(treename, MAX_TREE_SIZE + 1);
+- dfspath = kzalloc(treenamelen + linkpathlen + 1, GFP_KERNEL);
+- if (!dfspath)
+- return -ENOMEM;
+- if (treenamelen)
+- memcpy(dfspath, treename, treenamelen);
+- memcpy(dfspath + treenamelen, linkpath, linkpathlen);
+- rc = dfs_cache_find(xid, tcon->ses, cifs_sb->local_nls,
+- cifs_remap(cifs_sb), dfspath, NULL, NULL);
+- if (rc == 0) {
+- cifs_dbg(FYI, "DFS ref '%s' is found, emulate -EREMOTE\n",
+- dfspath);
+- rc = -EREMOTE;
+- } else {
+- cifs_dbg(FYI, "%s: dfs_cache_find returned %d\n", __func__, rc);
+- }
+- kfree(dfspath);
+- return rc;
+-}
+ #endif
+--- a/fs/cifs/smb2inode.c
++++ b/fs/cifs/smb2inode.c
+@@ -540,22 +540,41 @@ int smb2_query_path_info(const unsigned
+ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES, FILE_OPEN,
+ create_options, ACL_NO_MODE, data, SMB2_OP_QUERY_INFO, cfile,
+ err_iov, err_buftype);
+- if (rc == -EOPNOTSUPP) {
+- if (err_iov[0].iov_base && err_buftype[0] != CIFS_NO_BUFFER &&
+- ((struct smb2_hdr *)err_iov[0].iov_base)->Command == SMB2_CREATE &&
+- ((struct smb2_hdr *)err_iov[0].iov_base)->Status == STATUS_STOPPED_ON_SYMLINK) {
+- rc = smb2_parse_symlink_response(cifs_sb, err_iov, &data->symlink_target);
++ if (rc) {
++ struct smb2_hdr *hdr = err_iov[0].iov_base;
++
++ if (unlikely(!hdr || err_buftype[0] == CIFS_NO_BUFFER))
++ goto out;
++ if (rc == -EOPNOTSUPP && hdr->Command == SMB2_CREATE &&
++ hdr->Status == STATUS_STOPPED_ON_SYMLINK) {
++ rc = smb2_parse_symlink_response(cifs_sb, err_iov,
++ &data->symlink_target);
+ if (rc)
+ goto out;
+- }
+- *reparse = true;
+- create_options |= OPEN_REPARSE_POINT;
+
+- /* Failed on a symbolic link - query a reparse point info */
+- cifs_get_readable_path(tcon, full_path, &cfile);
+- rc = smb2_compound_op(xid, tcon, cifs_sb, full_path, FILE_READ_ATTRIBUTES,
+- FILE_OPEN, create_options, ACL_NO_MODE, data,
+- SMB2_OP_QUERY_INFO, cfile, NULL, NULL);
++ *reparse = true;
++ create_options |= OPEN_REPARSE_POINT;
++
++ /* Failed on a symbolic link - query a reparse point info */
++ cifs_get_readable_path(tcon, full_path, &cfile);
++ rc = smb2_compound_op(xid, tcon, cifs_sb, full_path,
++ FILE_READ_ATTRIBUTES, FILE_OPEN,
++ create_options, ACL_NO_MODE, data,
++ SMB2_OP_QUERY_INFO, cfile, NULL, NULL);
++ goto out;
++ } else if (rc != -EREMOTE && IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) &&
++ hdr->Status == STATUS_OBJECT_NAME_INVALID) {
++ /*
++ * Handle weird Windows SMB server behaviour. It responds with
++ * STATUS_OBJECT_NAME_INVALID code to SMB2 QUERY_INFO request
++ * for "\<server>\<dfsname>\<linkpath>" DFS reference,
++ * where <dfsname> contains non-ASCII unicode symbols.
++ */
++ rc = -EREMOTE;
++ }
++ if (rc == -EREMOTE && IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) && cifs_sb &&
++ (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS))
++ rc = -EOPNOTSUPP;
+ }
+
+ out:
+--- a/fs/cifs/smb2ops.c
++++ b/fs/cifs/smb2ops.c
+@@ -797,7 +797,9 @@ smb2_is_path_accessible(const unsigned i
+ int rc;
+ __le16 *utf16_path;
+ __u8 oplock = SMB2_OPLOCK_LEVEL_NONE;
++ int err_buftype = CIFS_NO_BUFFER;
+ struct cifs_open_parms oparms;
++ struct kvec err_iov = {};
+ struct cifs_fid fid;
+ struct cached_fid *cfid;
+
+@@ -821,14 +823,32 @@ smb2_is_path_accessible(const unsigned i
+ oparms.fid = &fid;
+ oparms.reconnect = false;
+
+- rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL, NULL,
+- NULL);
++ rc = SMB2_open(xid, &oparms, utf16_path, &oplock, NULL, NULL,
++ &err_iov, &err_buftype);
+ if (rc) {
+- kfree(utf16_path);
+- return rc;
++ struct smb2_hdr *hdr = err_iov.iov_base;
++
++ if (unlikely(!hdr || err_buftype == CIFS_NO_BUFFER))
++ goto out;
++ /*
++ * Handle weird Windows SMB server behaviour. It responds with
++ * STATUS_OBJECT_NAME_INVALID code to SMB2 QUERY_INFO request
++ * for "\<server>\<dfsname>\<linkpath>" DFS reference,
++ * where <dfsname> contains non-ASCII unicode symbols.
++ */
++ if (rc != -EREMOTE && IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) &&
++ hdr->Status == STATUS_OBJECT_NAME_INVALID)
++ rc = -EREMOTE;
++ if (rc == -EREMOTE && IS_ENABLED(CONFIG_CIFS_DFS_UPCALL) && cifs_sb &&
++ (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS))
++ rc = -EOPNOTSUPP;
++ goto out;
+ }
+
+ rc = SMB2_close(xid, tcon, fid.persistent_fid, fid.volatile_fid);
++
++out:
++ free_rsp_buf(err_buftype, err_iov.iov_base);
+ kfree(utf16_path);
+ return rc;
+ }