diff options
| -rw-r--r-- | arch/x86/entry/entry_64.S | 39 |
1 files changed, 31 insertions, 8 deletions
diff --git a/arch/x86/entry/entry_64.S b/arch/x86/entry/entry_64.S index bd97161f90cb..3bb2c4302df1 100644 --- a/arch/x86/entry/entry_64.S +++ b/arch/x86/entry/entry_64.S | |||
| @@ -1135,7 +1135,7 @@ END(paranoid_exit) | |||
| 1135 | 1135 | ||
| 1136 | /* | 1136 | /* |
| 1137 | * Save all registers in pt_regs, and switch gs if needed. | 1137 | * Save all registers in pt_regs, and switch gs if needed. |
| 1138 | * Return: ebx=0: need swapgs on exit, ebx=1: otherwise | 1138 | * Return: EBX=0: came from user mode; EBX=1: otherwise |
| 1139 | */ | 1139 | */ |
| 1140 | ENTRY(error_entry) | 1140 | ENTRY(error_entry) |
| 1141 | cld | 1141 | cld |
| @@ -1144,9 +1144,11 @@ ENTRY(error_entry) | |||
| 1144 | xorl %ebx, %ebx | 1144 | xorl %ebx, %ebx |
| 1145 | testb $3, CS+8(%rsp) | 1145 | testb $3, CS+8(%rsp) |
| 1146 | jz error_kernelspace | 1146 | jz error_kernelspace |
| 1147 | error_swapgs: | 1147 | |
| 1148 | /* We entered from user mode */ | ||
| 1148 | SWAPGS | 1149 | SWAPGS |
| 1149 | error_sti: | 1150 | |
| 1151 | error_entry_done: | ||
| 1150 | TRACE_IRQS_OFF | 1152 | TRACE_IRQS_OFF |
| 1151 | ret | 1153 | ret |
| 1152 | 1154 | ||
| @@ -1165,8 +1167,15 @@ error_kernelspace: | |||
| 1165 | cmpq %rax, RIP+8(%rsp) | 1167 | cmpq %rax, RIP+8(%rsp) |
| 1166 | je bstep_iret | 1168 | je bstep_iret |
| 1167 | cmpq $gs_change, RIP+8(%rsp) | 1169 | cmpq $gs_change, RIP+8(%rsp) |
| 1168 | je error_swapgs | 1170 | jne error_entry_done |
| 1169 | jmp error_sti | 1171 | |
| 1172 | /* | ||
| 1173 | * hack: gs_change can fail with user gsbase. If this happens, fix up | ||
| 1174 | * gsbase and proceed. We'll fix up the exception and land in | ||
| 1175 | * gs_change's error handler with kernel gsbase. | ||
| 1176 | */ | ||
| 1177 | SWAPGS | ||
| 1178 | jmp error_entry_done | ||
| 1170 | 1179 | ||
| 1171 | bstep_iret: | 1180 | bstep_iret: |
| 1172 | /* Fix truncated RIP */ | 1181 | /* Fix truncated RIP */ |
| @@ -1174,16 +1183,30 @@ bstep_iret: | |||
| 1174 | /* fall through */ | 1183 | /* fall through */ |
| 1175 | 1184 | ||
| 1176 | error_bad_iret: | 1185 | error_bad_iret: |
| 1186 | /* | ||
| 1187 | * We came from an IRET to user mode, so we have user gsbase. | ||
| 1188 | * Switch to kernel gsbase: | ||
| 1189 | */ | ||
| 1177 | SWAPGS | 1190 | SWAPGS |
| 1191 | |||
| 1192 | /* | ||
| 1193 | * Pretend that the exception came from user mode: set up pt_regs | ||
| 1194 | * as if we faulted immediately after IRET and clear EBX so that | ||
| 1195 | * error_exit knows that we will be returning to user mode. | ||
| 1196 | */ | ||
| 1178 | mov %rsp, %rdi | 1197 | mov %rsp, %rdi |
| 1179 | call fixup_bad_iret | 1198 | call fixup_bad_iret |
| 1180 | mov %rax, %rsp | 1199 | mov %rax, %rsp |
| 1181 | decl %ebx /* Return to usergs */ | 1200 | decl %ebx |
| 1182 | jmp error_sti | 1201 | jmp error_entry_done |
| 1183 | END(error_entry) | 1202 | END(error_entry) |
| 1184 | 1203 | ||
| 1185 | 1204 | ||
| 1186 | /* On entry, ebx is "no swapgs" flag (1: don't need swapgs, 0: need it) */ | 1205 | /* |
| 1206 | * On entry, EBS is a "return to kernel mode" flag: | ||
| 1207 | * 1: already in kernel mode, don't need SWAPGS | ||
| 1208 | * 0: user gsbase is loaded, we need SWAPGS and standard preparation for return to usermode | ||
| 1209 | */ | ||
| 1187 | ENTRY(error_exit) | 1210 | ENTRY(error_exit) |
| 1188 | movl %ebx, %eax | 1211 | movl %ebx, %eax |
| 1189 | RESTORE_EXTRA_REGS | 1212 | RESTORE_EXTRA_REGS |
