32位保护模式切用户态触发QEMU invalid tss type错误求助
问题:32位无分页保护模式下用户态触发中断报"invalid tss type"错误
错误场景
在未启用分页的32位保护模式下,完成内核态到用户态的切换后,用户空间调用int 2中断,QEMU抛出以下错误:
This may indicate that pixbuf loaders or the mime database could not be found. qemu: fatal: invalid tss type EAX=00003fec EBX=00001000 ECX=00000002 EDX=00001810 ESI=00000000 EDI=00000000 EBP=00003ff8 ESP=00003fec EIP=00000000 EFL=00000006 [-----P-] CPL=3 II=0 A20=1 SMM=0 HLT=0 ES =0023 00006000 7fffffff 00c7f300 DPL=3 DS [-WA] CS =001b 00006000 7fffffff 00c7fa00 DPL=3 CS32 [-R-] SS =0023 00006000 7fffffff 00c7f300 DPL=3 DS [-WA] DS =0023 00006000 7fffffff 00c7f300 DPL=3 DS [-WA] FS =0023 00006000 7fffffff 00c7f300 DPL=3 DS [-WA] GS =0023 00006000 7fffffff 00c7f300 DPL=3 DS [-WA] LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy GDT= 00007c47 00000027 IDT= 00003000 0000ffff CR0=00000011 CR2=00000000 CR3=00000000 CR4=00000000 DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000 DR6=ffff0ff0 DR7=00000400 CCS=00000004 CCD=00003fe8 CCO=EFLAGS EFER=0000000000000000 FCW=037f FSW=0000 [ST=0] FTW=00 MXCSR=00001f80 FPR0=0000000000000000 0000 FPR1=0000000000000000 0000 FPR2=0000000000000000 0000 FPR3=0000000000000000 0000 FPR4=0000000000000000 0000 FPR5=0000000000000000 0000 FPR6=0000000000000000 0000 FPR7=0000000000000000 0000 XMM00=0000000000000000 0000000000000000 XMM01=0000000000000000 0000000000000000 XMM02=0000000000000000 0000000000000000 XMM03=0000000000000000 0000000000000000 XMM04=0000000000000000 0000000000000000 XMM05=0000000000000000 0000000000000000 XMM06=0000000000000000 0000000000000000 XMM07=0000000000000000 0000000000000000
相关代码
内核态切换到用户态的代码
switchSegments: mov ax, 0x23 mov ds, ax mov es, ax mov fs, ax mov gs, ax mov eax, esp push dword 0x23 push dword eax pushf push dword 0x1B push dword 0x0 iret
GDT配置
gdt_start: ;first entry is always null (each entry is 8 bytes or 64 bits) dd 0x0 ; 4 byte dd 0x0 ; 4 byte ; GDT for code segment. base = 0x00000000, length = 0xfffff ; for flags, refer to os-dev.pdf document, page 36 gdt_code: dw 0xFFFF ; segment length, bits 0-15 dw 0x0 ; segment base, bits 0-15 db 0x0 ; segment base, bits 16-23 db 10011010b ; flags (8 bits) db 11000000b ; flags (4 bits) + segment length, bits 16-19 db 0x0 ; segment base, bits 24-31 ; GDT for data segment. base and length identical to code segment ; some flags changed, again, refer to os-dev.pdf gdt_data: dw 0xFFFF dw 0x0 db 0x0 db 10010010b db 11000000b db 0x0 U_code: dw 0xFFFF ; segment length, bits 0-15 dw 0x6000 ; segment base, bits 0-15 db 0x00 ; segment base, bits 16-23 db 11111010b ; flags (8 bits) db 11000111b ; flags (4 bits) + segment length, bits 16-19 db 0x00 ; segment base, bits 24-31 ; GDT for data segment. base and length identical to code segment ; some flags changed, again, refer to os-dev.pdf U_data: dw 0xFFFF dw 0x6000 db 0x00 db 11110010b db 11000111b db 0x00 tssSegment: dw tssend - tss dw tss db 0x0 db 0x89 db 0x40 db 0x0 gdt_end: ; GDT descriptor gdt_descriptor: dw gdt_end - gdt_start - 1 ; size (16 bit), always one less of its true size dd gdt_start ; address (32 bit)
TSS配置(UPDATE 1)
tss: dd 0x0 dd 0x4000 ;esp0 dd 0x10 ;ss0 dd 0x0 dd 0x0 dd 0x0 dd 0x0 dd 0x0 dd 0x0 dd 0x0 dd 0x0 dd 0x0 dd 0x0 dd 0x0 dd 0x0 dd 0x0 dd 0x0 dd 0x0 dd 0x0 dd 0x0 dd 0x0 dd 0x0 dd 0x0 dd 0x0 dd 104 ;IOPB dd 0x0 tssend:
错误原因
从QEMU输出的TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy可以看出,任务寄存器(TR)指向空选择子(0x0),CPU没有加载有效的TSS段。
当用户态(CPL=3)触发中断时,CPU需要切换回内核态,此时必须读取TSS中的esp0和ss0来设置内核栈。如果TR未指向有效TSS,CPU会直接抛出"invalid tss type"错误。
另外,原TSS的结构也不符合32位TSS的规范:IOPB偏移量应为16位字段,代码中误用了32位的dd指令,导致TSS大小和结构错误。
修复方案
1. 加载TSS到任务寄存器
在切换到用户态之前,执行ltr指令将TSS的选择子加载到TR中。TSS在GDT中是第5个条目(索引从0开始),所以选择子为0x28(索引5×8=40=0x28,TI=0、RPL=0)。
修改切换用户态的代码:
switchSegments: ; 先加载TSS到TR mov ax, 0x28 ltr ax mov ax, 0x23 mov ds, ax mov es, ax mov fs, ax mov gs, ax mov eax, esp push dword 0x23 push dword eax pushf push dword 0x1B push dword 0x0 iret
2. 修正TSS结构
按照32位TSS的标准结构调整,确保IOPB偏移量为16位,且总大小至少为104字节:
tss: dd 0x0 ; 前一个TSS的选择子 dd 0x4000 ; 内核态栈指针esp0 dd 0x10 ; 内核态栈段选择子ss0 dd 0x0 ; esp1(未使用可设0) dd 0x0 ; ss1(未使用可设0) dd 0x0 ; esp2(未使用可设0) dd 0x0 ; ss2(未使用可设0) dd 0x0 ; cr3(无分页可设0) dd 0x0 ; eip(未使用可设0) dd 0x0 ; eflags(未使用可设0) dd 0x0 ; eax(未使用可设0) dd 0x0 ; ecx(未使用可设0) dd 0x0 ; edx(未使用可设0) dd 0x0 ; ebx(未使用可设0) dd 0x0 ; esp(未使用可设0) dd 0x0 ; ebp(未使用可设0) dd 0x0 ; esi(未使用可设0) dd 0x0 ; edi(未使用可设0) dd 0x0 ; es(未使用可设0) dd 0x0 ; cs(未使用可设0) dd 0x0 ; ss(未使用可设0) dd 0x0 ; ds(未使用可设0) dd 0x0 ; fs(未使用可设0) dd 0x0 ; gs(未使用可设0) dd 0x0 ; LDT选择子(未使用可设0) dw 0x0 ; 陷阱标志(未使用可设0) dw 104 ; IOPB偏移量(必须为16位) tssend:
3. 确认TSS段描述符正确性
原GDT中的tssSegment配置正确:0x89表示这是一个存在的、DPL=0的32位可用TSS,无需修改。
总结
核心问题是未加载有效TSS到任务寄存器,加上TSS结构不符合规范。完成上述修复后,CPU在用户态触发中断时就能正确读取TSS中的内核栈信息,错误即可解决。
内容的提问来源于stack exchange,提问作者Jacob Knuth
相关产品推荐
相关产品推荐

