You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 03:47:00