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

x86/tdx: Make TDX_HYPERCALL asm similar to TDX_MODULE_CALL

Now the 'struct tdx_hypercall_args' and 'struct tdx_module_args' are
almost the same, and the TDX_HYPERCALL and TDX_MODULE_CALL asm macro
share similar code pattern too. The __tdx_hypercall() and __tdcall()
should be unified to use the same assembly code.

As a preparation to unify them, simplify the TDX_HYPERCALL to make it
more like the TDX_MODULE_CALL.

The TDX_HYPERCALL takes the pointer of 'struct tdx_hypercall_args' as
function call argument, and does below extra things comparing to the
TDX_MODULE_CALL:

1) It sets RAX to 0 (TDG.VP.VMCALL leaf) internally;
2) It sets RCX to the (fixed) bitmap of shared registers internally;
3) It calls __tdx_hypercall_failed() internally (and panics) when the
TDCALL instruction itself fails;
4) After TDCALL, it moves R10 to RAX to return the return code of the
VMCALL leaf, regardless the '\ret' asm macro argument;

Firstly, change the TDX_HYPERCALL to take the same function call
arguments as the TDX_MODULE_CALL does: TDCALL leaf ID, and the pointer
to 'struct tdx_module_args'. Then 1) and 2) can be moved to the
caller:

- TDG.VP.VMCALL leaf ID can be passed via the function call argument;
- 'struct tdx_module_args' is 'struct tdx_hypercall_args' + RCX, thus
the bitmap of shared registers can be passed via RCX in the
structure.

Secondly, to move 3) and 4) out of assembly, make the TDX_HYPERCALL
always save output registers to the structure. The caller then can:

- Call __tdx_hypercall_failed() when TDX_HYPERCALL returns error;
- Return R10 in the structure as the return code of the VMCALL leaf;

With above changes, change the asm function from __tdx_hypercall() to
__tdcall_hypercall(), and reimplement __tdx_hypercall() as the C wrapper
of it. This avoids having to add another wrapper of __tdx_hypercall()
(_tdx_hypercall() is already taken).

The __tdcall_hypercall() will be replaced with a __tdcall() variant
using TDX_MODULE_CALL in a later commit as the final goal is to have one
assembly to handle both TDCALL and TDVMCALL.

Currently, the __tdx_hypercall() asm is in '.noinstr.text'. To keep
this unchanged, annotate __tdx_hypercall(), which is a C function now,
as 'noinstr'.

Remove the __tdx_hypercall_ret() as __tdx_hypercall() already does so.

Implement __tdx_hypercall() in tdx-shared.c so it can be shared with the
compressed code.

Opportunistically fix a checkpatch error complaining using space around
parenthesis '(' and ')' while moving the bitmap of shared registers to
<asm/shared/tdx.h>.

[ dhansen: quash new calls of __tdx_hypercall_ret() that showed up ]

Suggested-by: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Kai Huang <kai.huang@intel.com>
Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com>
Reviewed-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lore.kernel.org/all/0cbf25e7aee3256288045023a31f65f0cef90af4.1692096753.git.kai.huang%40intel.com

authored by

Kai Huang and committed by
Dave Hansen
c641cfb5 12f34ed8

+176 -168
+1 -1
arch/x86/boot/compressed/tdx.c
··· 26 26 .r14 = port, 27 27 }; 28 28 29 - if (__tdx_hypercall_ret(&args)) 29 + if (__tdx_hypercall(&args)) 30 30 return UINT_MAX; 31 31 32 32 return args.r11;
+48 -98
arch/x86/coco/tdx/tdcall.S
··· 2 2 #include <asm/asm-offsets.h> 3 3 #include <asm/asm.h> 4 4 #include <asm/frame.h> 5 - #include <asm/unwind_hints.h> 6 5 7 6 #include <linux/linkage.h> 8 - #include <linux/bits.h> 9 7 #include <linux/errno.h> 10 8 11 9 #include "../../virt/vmx/tdx/tdxcall.S" 12 - 13 - /* 14 - * Bitmasks of exposed registers (with VMM). 15 - */ 16 - #define TDX_RDX BIT(2) 17 - #define TDX_RBX BIT(3) 18 - #define TDX_RSI BIT(6) 19 - #define TDX_RDI BIT(7) 20 - #define TDX_R8 BIT(8) 21 - #define TDX_R9 BIT(9) 22 - #define TDX_R10 BIT(10) 23 - #define TDX_R11 BIT(11) 24 - #define TDX_R12 BIT(12) 25 - #define TDX_R13 BIT(13) 26 - #define TDX_R14 BIT(14) 27 - #define TDX_R15 BIT(15) 28 - 29 - /* 30 - * These registers are clobbered to hold arguments for each 31 - * TDVMCALL. They are safe to expose to the VMM. 32 - * Each bit in this mask represents a register ID. Bit field 33 - * details can be found in TDX GHCI specification, section 34 - * titled "TDCALL [TDG.VP.VMCALL] leaf". 35 - */ 36 - #define TDVMCALL_EXPOSE_REGS_MASK \ 37 - ( TDX_RDX | TDX_RBX | TDX_RSI | TDX_RDI | TDX_R8 | TDX_R9 | \ 38 - TDX_R10 | TDX_R11 | TDX_R12 | TDX_R13 | TDX_R14 | TDX_R15 ) 39 10 40 11 .section .noinstr.text, "ax" 41 12 ··· 49 78 * TDX_HYPERCALL - Make hypercalls to a TDX VMM using TDVMCALL leaf of TDCALL 50 79 * instruction 51 80 * 52 - * Transforms values in function call argument struct tdx_hypercall_args @args 81 + * Transforms values in function call argument struct tdx_module_args @args 53 82 * into the TDCALL register ABI. After TDCALL operation, VMM output is saved 54 83 * back in @args, if \ret is 1. 84 + * 85 + * Depends on the caller to pass TDG.VP.VMCALL as the TDCALL leaf, and set 86 + * @args::rcx to TDVMCALL_EXPOSE_REGS_MASK. 55 87 * 56 88 *------------------------------------------------------------------------- 57 89 * TD VMCALL ABI: ··· 80 106 * R8-R15 - Same as above. 81 107 * 82 108 */ 83 - .macro TDX_HYPERCALL ret:req 109 + .macro TDX_HYPERCALL 84 110 FRAME_BEGIN 85 111 86 112 /* Save callee-saved GPRs as mandated by the x86_64 ABI */ ··· 90 116 push %r12 91 117 push %rbx 92 118 93 - /* Free RDI to be used as TDVMCALL arguments */ 119 + /* Move Leaf ID to RAX */ 94 120 movq %rdi, %rax 95 121 122 + /* Move bitmap of shared registers to RCX */ 123 + movq TDX_MODULE_rcx(%rsi), %rcx 124 + 96 125 /* Copy hypercall registers from arg struct: */ 97 - movq TDX_HYPERCALL_r8(%rax), %r8 98 - movq TDX_HYPERCALL_r9(%rax), %r9 99 - movq TDX_HYPERCALL_r10(%rax), %r10 100 - movq TDX_HYPERCALL_r11(%rax), %r11 101 - movq TDX_HYPERCALL_r12(%rax), %r12 102 - movq TDX_HYPERCALL_r13(%rax), %r13 103 - movq TDX_HYPERCALL_r14(%rax), %r14 104 - movq TDX_HYPERCALL_r15(%rax), %r15 105 - movq TDX_HYPERCALL_rdi(%rax), %rdi 106 - movq TDX_HYPERCALL_rsi(%rax), %rsi 107 - movq TDX_HYPERCALL_rbx(%rax), %rbx 108 - movq TDX_HYPERCALL_rdx(%rax), %rdx 126 + movq TDX_MODULE_r8(%rsi), %r8 127 + movq TDX_MODULE_r9(%rsi), %r9 128 + movq TDX_MODULE_r10(%rsi), %r10 129 + movq TDX_MODULE_r11(%rsi), %r11 130 + movq TDX_MODULE_r12(%rsi), %r12 131 + movq TDX_MODULE_r13(%rsi), %r13 132 + movq TDX_MODULE_r14(%rsi), %r14 133 + movq TDX_MODULE_r15(%rsi), %r15 134 + movq TDX_MODULE_rdi(%rsi), %rdi 135 + movq TDX_MODULE_rbx(%rsi), %rbx 136 + movq TDX_MODULE_rdx(%rsi), %rdx 109 137 110 - push %rax 111 - 112 - /* Mangle function call ABI into TDCALL ABI: */ 113 - /* Set TDCALL leaf ID (TDVMCALL (0)) in RAX */ 114 - xor %eax, %eax 115 - 116 - movl $TDVMCALL_EXPOSE_REGS_MASK, %ecx 138 + pushq %rsi 139 + movq TDX_MODULE_rsi(%rsi), %rsi 117 140 118 141 tdcall 119 142 120 143 /* 121 - * RAX!=0 indicates a failure of the TDVMCALL mechanism itself and that 122 - * something has gone horribly wrong with the TDX module. 144 + * Restore the pointer of the structure to save output registers. 123 145 * 124 - * The return status of the hypercall operation is in a separate 125 - * register (in R10). Hypercall errors are a part of normal operation 126 - * and are handled by callers. 146 + * RCX is used as bitmap of shared registers and doesn't hold any 147 + * value provided by the VMM, thus it can be used as spare to 148 + * restore the structure pointer. 127 149 */ 128 - testq %rax, %rax 129 - jne .Lpanic\@ 150 + popq %rcx 151 + movq %rsi, TDX_MODULE_rsi(%rcx) 152 + movq %rcx, %rsi 130 153 131 - pop %rax 132 - 133 - .if \ret 134 - movq %r8, TDX_HYPERCALL_r8(%rax) 135 - movq %r9, TDX_HYPERCALL_r9(%rax) 136 - movq %r10, TDX_HYPERCALL_r10(%rax) 137 - movq %r11, TDX_HYPERCALL_r11(%rax) 138 - movq %r12, TDX_HYPERCALL_r12(%rax) 139 - movq %r13, TDX_HYPERCALL_r13(%rax) 140 - movq %r14, TDX_HYPERCALL_r14(%rax) 141 - movq %r15, TDX_HYPERCALL_r15(%rax) 142 - movq %rdi, TDX_HYPERCALL_rdi(%rax) 143 - movq %rsi, TDX_HYPERCALL_rsi(%rax) 144 - movq %rbx, TDX_HYPERCALL_rbx(%rax) 145 - movq %rdx, TDX_HYPERCALL_rdx(%rax) 146 - .endif 147 - 148 - /* TDVMCALL leaf return code is in R10 */ 149 - movq %r10, %rax 154 + movq %r8, TDX_MODULE_r8(%rsi) 155 + movq %r9, TDX_MODULE_r9(%rsi) 156 + movq %r10, TDX_MODULE_r10(%rsi) 157 + movq %r11, TDX_MODULE_r11(%rsi) 158 + movq %r12, TDX_MODULE_r12(%rsi) 159 + movq %r13, TDX_MODULE_r13(%rsi) 160 + movq %r14, TDX_MODULE_r14(%rsi) 161 + movq %r15, TDX_MODULE_r15(%rsi) 162 + movq %rdi, TDX_MODULE_rdi(%rsi) 163 + movq %rbx, TDX_MODULE_rbx(%rsi) 164 + movq %rdx, TDX_MODULE_rdx(%rsi) 150 165 151 166 /* 152 167 * Zero out registers exposed to the VMM to avoid speculative execution ··· 161 198 FRAME_END 162 199 163 200 RET 164 - .Lpanic\@: 165 - call __tdx_hypercall_failed 166 - /* __tdx_hypercall_failed never returns */ 167 - REACHABLE 168 - jmp .Lpanic\@ 169 201 .endm 170 202 171 203 /* 172 204 * 173 - * __tdx_hypercall() function ABI: 205 + * __tdcall_hypercall() function ABI: 174 206 * 175 - * @args (RDI) - struct tdx_hypercall_args for input 207 + * @fn (RDI) - TDCALL leaf ID, moved to RAX 208 + * @args (RSI) - struct tdx_module_args for input/output 176 209 * 177 - * On successful completion, return the hypercall error code. 178 - */ 179 - SYM_FUNC_START(__tdx_hypercall) 180 - TDX_HYPERCALL ret=0 181 - SYM_FUNC_END(__tdx_hypercall) 182 - 183 - /* 184 - * 185 - * __tdx_hypercall_ret() function ABI: 186 - * 187 - * @args (RDI) - struct tdx_hypercall_args for input and output 210 + * @fn and @args::rcx from the caller must be TDG_VP_VMCALL and 211 + * TDVMCALL_EXPOSE_REGS_MASK respectively. 188 212 * 189 213 * On successful completion, return the hypercall error code. 190 214 */ 191 - SYM_FUNC_START(__tdx_hypercall_ret) 192 - TDX_HYPERCALL ret=1 193 - SYM_FUNC_END(__tdx_hypercall_ret) 215 + SYM_FUNC_START(__tdcall_hypercall) 216 + TDX_HYPERCALL 217 + SYM_FUNC_END(__tdcall_hypercall)
+43
arch/x86/coco/tdx/tdx-shared.c
··· 69 69 70 70 return true; 71 71 } 72 + 73 + noinstr u64 __tdx_hypercall(struct tdx_hypercall_args *args) 74 + { 75 + struct tdx_module_args margs = { 76 + .rcx = TDVMCALL_EXPOSE_REGS_MASK, 77 + .rdx = args->rdx, 78 + .r8 = args->r8, 79 + .r9 = args->r9, 80 + .r10 = args->r10, 81 + .r11 = args->r11, 82 + .r12 = args->r12, 83 + .r13 = args->r13, 84 + .r14 = args->r14, 85 + .r15 = args->r15, 86 + .rbx = args->rbx, 87 + .rdi = args->rdi, 88 + .rsi = args->rsi, 89 + }; 90 + 91 + /* 92 + * Failure of __tdcall_hypercall() indicates a failure of the TDVMCALL 93 + * mechanism itself and that something has gone horribly wrong with 94 + * the TDX module. __tdx_hypercall_failed() never returns. 95 + */ 96 + if (__tdcall_hypercall(TDG_VP_VMCALL, &margs)) 97 + __tdx_hypercall_failed(); 98 + 99 + args->r8 = margs.r8; 100 + args->r9 = margs.r9; 101 + args->r10 = margs.r10; 102 + args->r11 = margs.r11; 103 + args->r12 = margs.r12; 104 + args->r13 = margs.r13; 105 + args->r14 = margs.r14; 106 + args->r15 = margs.r15; 107 + args->rdi = margs.rdi; 108 + args->rsi = margs.rsi; 109 + args->rbx = margs.rbx; 110 + args->rdx = margs.rdx; 111 + 112 + /* TDVMCALL leaf return code is in R10 */ 113 + return args->r10; 114 + }
+7 -6
arch/x86/coco/tdx/tdx.c
··· 38 38 #define TDREPORT_SUBTYPE_0 0 39 39 40 40 /* Called from __tdx_hypercall() for unrecoverable failure */ 41 - noinstr void __tdx_hypercall_failed(void) 41 + noinstr void __noreturn __tdx_hypercall_failed(void) 42 42 { 43 43 instrumentation_begin(); 44 44 panic("TDVMCALL failed. TDX module bug?"); ··· 285 285 * can be found in TDX Guest-Host-Communication Interface 286 286 * (GHCI), section titled "TDG.VP.VMCALL<Instruction.RDMSR>". 287 287 */ 288 - if (__tdx_hypercall_ret(&args)) 288 + if (__tdx_hypercall(&args)) 289 289 return -EIO; 290 290 291 291 regs->ax = lower_32_bits(args.r11); ··· 339 339 * ABI can be found in TDX Guest-Host-Communication Interface 340 340 * (GHCI), section titled "VP.VMCALL<Instruction.CPUID>". 341 341 */ 342 - if (__tdx_hypercall_ret(&args)) 342 + if (__tdx_hypercall(&args)) 343 343 return -EIO; 344 344 345 345 /* ··· 366 366 .r15 = *val, 367 367 }; 368 368 369 - if (__tdx_hypercall_ret(&args)) 369 + if (__tdx_hypercall(&args)) 370 370 return false; 371 + 371 372 *val = args.r11; 372 373 return true; 373 374 } ··· 501 500 * in TDX Guest-Host-Communication Interface (GHCI) section titled 502 501 * "TDG.VP.VMCALL<Instruction.IO>". 503 502 */ 504 - success = !__tdx_hypercall_ret(&args); 503 + success = !__tdx_hypercall(&args); 505 504 506 505 /* Update part of the register affected by the emulated instruction */ 507 506 regs->ax &= ~mask; ··· 730 729 .r13 = end - start }; 731 730 732 731 u64 map_fail_paddr; 733 - u64 ret = __tdx_hypercall_ret(&args); 732 + u64 ret = __tdx_hypercall(&args); 734 733 735 734 if (ret != TDVMCALL_STATUS_RETRY) 736 735 return !ret;
+2 -2
arch/x86/hyperv/ivm.c
··· 404 404 .r12 = msr, 405 405 }; 406 406 407 - u64 ret = __tdx_hypercall_ret(&args); 407 + u64 ret = __tdx_hypercall(&args); 408 408 409 409 if (WARN_ONCE(ret, "Failed to emulate MSR read: %lld\n", ret)) 410 410 *val = 0; ··· 420 420 args.rdx = param1; 421 421 args.r8 = param2; 422 422 423 - (void)__tdx_hypercall_ret(&args); 423 + (void)__tdx_hypercall(&args); 424 424 425 425 return args.r11; 426 426 }
+75 -47
arch/x86/include/asm/shared/tdx.h
··· 11 11 #define TDX_IDENT "IntelTDX " 12 12 13 13 /* TDX module Call Leaf IDs */ 14 + #define TDG_VP_VMCALL 0 14 15 #define TDG_VP_INFO 1 15 16 #define TDG_VP_VEINFO_GET 3 16 17 #define TDG_MR_REPORT 4 ··· 27 26 28 27 #define TDVMCALL_STATUS_RETRY 1 29 28 29 + /* 30 + * Bitmasks of exposed registers (with VMM). 31 + */ 32 + #define TDX_RDX BIT(2) 33 + #define TDX_RBX BIT(3) 34 + #define TDX_RSI BIT(6) 35 + #define TDX_RDI BIT(7) 36 + #define TDX_R8 BIT(8) 37 + #define TDX_R9 BIT(9) 38 + #define TDX_R10 BIT(10) 39 + #define TDX_R11 BIT(11) 40 + #define TDX_R12 BIT(12) 41 + #define TDX_R13 BIT(13) 42 + #define TDX_R14 BIT(14) 43 + #define TDX_R15 BIT(15) 44 + 45 + /* 46 + * These registers are clobbered to hold arguments for each 47 + * TDVMCALL. They are safe to expose to the VMM. 48 + * Each bit in this mask represents a register ID. Bit field 49 + * details can be found in TDX GHCI specification, section 50 + * titled "TDCALL [TDG.VP.VMCALL] leaf". 51 + */ 52 + #define TDVMCALL_EXPOSE_REGS_MASK \ 53 + (TDX_RDX | TDX_RBX | TDX_RSI | TDX_RDI | TDX_R8 | TDX_R9 | \ 54 + TDX_R10 | TDX_R11 | TDX_R12 | TDX_R13 | TDX_R14 | TDX_R15) 55 + 30 56 #ifndef __ASSEMBLY__ 31 - 32 - /* 33 - * Used in __tdx_hypercall() to pass down and get back registers' values of 34 - * the TDCALL instruction when requesting services from the VMM. 35 - * 36 - * This is a software only structure and not part of the TDX module/VMM ABI. 37 - */ 38 - struct tdx_hypercall_args { 39 - u64 r8; 40 - u64 r9; 41 - u64 r10; 42 - u64 r11; 43 - u64 r12; 44 - u64 r13; 45 - u64 r14; 46 - u64 r15; 47 - u64 rdi; 48 - u64 rsi; 49 - u64 rbx; 50 - u64 rdx; 51 - }; 52 - 53 - /* Used to request services from the VMM */ 54 - u64 __tdx_hypercall(struct tdx_hypercall_args *args); 55 - u64 __tdx_hypercall_ret(struct tdx_hypercall_args *args); 56 - 57 - /* 58 - * Wrapper for standard use of __tdx_hypercall with no output aside from 59 - * return code. 60 - */ 61 - static inline u64 _tdx_hypercall(u64 fn, u64 r12, u64 r13, u64 r14, u64 r15) 62 - { 63 - struct tdx_hypercall_args args = { 64 - .r10 = TDX_HYPERCALL_STANDARD, 65 - .r11 = fn, 66 - .r12 = r12, 67 - .r13 = r13, 68 - .r14 = r14, 69 - .r15 = r15, 70 - }; 71 - 72 - return __tdx_hypercall(&args); 73 - } 74 - 75 - 76 - /* Called from __tdx_hypercall() for unrecoverable failure */ 77 - void __tdx_hypercall_failed(void); 78 57 79 58 /* 80 59 * Used in __tdcall*() to gather the input/output registers' values of the ··· 83 102 /* Used to communicate with the TDX module */ 84 103 u64 __tdcall(u64 fn, struct tdx_module_args *args); 85 104 u64 __tdcall_ret(u64 fn, struct tdx_module_args *args); 105 + 106 + /* 107 + * Used in __tdx_hypercall() to pass down and get back registers' values of 108 + * the TDCALL instruction when requesting services from the VMM. 109 + * 110 + * This is a software only structure and not part of the TDX module/VMM ABI. 111 + */ 112 + struct tdx_hypercall_args { 113 + u64 r8; 114 + u64 r9; 115 + u64 r10; 116 + u64 r11; 117 + u64 r12; 118 + u64 r13; 119 + u64 r14; 120 + u64 r15; 121 + u64 rdi; 122 + u64 rsi; 123 + u64 rbx; 124 + u64 rdx; 125 + }; 126 + 127 + /* Used to request services from the VMM */ 128 + u64 __tdcall_hypercall(u64 fn, struct tdx_module_args *args); 129 + u64 __tdx_hypercall(struct tdx_hypercall_args *args); 130 + 131 + /* 132 + * Wrapper for standard use of __tdx_hypercall with no output aside from 133 + * return code. 134 + */ 135 + static inline u64 _tdx_hypercall(u64 fn, u64 r12, u64 r13, u64 r14, u64 r15) 136 + { 137 + struct tdx_hypercall_args args = { 138 + .r10 = TDX_HYPERCALL_STANDARD, 139 + .r11 = fn, 140 + .r12 = r12, 141 + .r13 = r13, 142 + .r14 = r14, 143 + .r15 = r15, 144 + }; 145 + 146 + return __tdx_hypercall(&args); 147 + } 148 + 149 + 150 + /* Called from __tdx_hypercall() for unrecoverable failure */ 151 + void __tdx_hypercall_failed(void); 86 152 87 153 bool tdx_accept_memory(phys_addr_t start, phys_addr_t end); 88 154
-14
arch/x86/kernel/asm-offsets.c
··· 83 83 OFFSET(TDX_MODULE_rsi, tdx_module_args, rsi); 84 84 85 85 BLANK(); 86 - OFFSET(TDX_HYPERCALL_r8, tdx_hypercall_args, r8); 87 - OFFSET(TDX_HYPERCALL_r9, tdx_hypercall_args, r9); 88 - OFFSET(TDX_HYPERCALL_r10, tdx_hypercall_args, r10); 89 - OFFSET(TDX_HYPERCALL_r11, tdx_hypercall_args, r11); 90 - OFFSET(TDX_HYPERCALL_r12, tdx_hypercall_args, r12); 91 - OFFSET(TDX_HYPERCALL_r13, tdx_hypercall_args, r13); 92 - OFFSET(TDX_HYPERCALL_r14, tdx_hypercall_args, r14); 93 - OFFSET(TDX_HYPERCALL_r15, tdx_hypercall_args, r15); 94 - OFFSET(TDX_HYPERCALL_rdi, tdx_hypercall_args, rdi); 95 - OFFSET(TDX_HYPERCALL_rsi, tdx_hypercall_args, rsi); 96 - OFFSET(TDX_HYPERCALL_rbx, tdx_hypercall_args, rbx); 97 - OFFSET(TDX_HYPERCALL_rdx, tdx_hypercall_args, rdx); 98 - 99 - BLANK(); 100 86 OFFSET(BP_scratch, boot_params, scratch); 101 87 OFFSET(BP_secure_boot, boot_params, secure_boot); 102 88 OFFSET(BP_loadflags, boot_params, hdr.loadflags);