arch-x86: Use the seg unusable bit and not a null selector in the TLB.

When dealing with segmentation in x86, it is *usually* illegal to
attempt to access a segment which has a null selector when in protected
mode and not in 64 bit mode. While this is *almost* true, it is not
actually technically true.

What actually *is* true is that if you *set up* a segment using a null
selector in those circumstances, that segment becomes unusable, and then
tryint to use it causes a fault.

When in real mode, it is perfectly legal to use a null selector to
access memory, since that is just a selector with numerical value 0.
When you then transition into protected mode, the selector would still
be 0 (a null selector), but the segment itself would still be set up
properly and usuable using the base value, limit, and other attributes
it carried over from real mode.

Rather than check if a segment has a null selector while handling
segmentation, it's more correct for us to keep track of whether the
segment is currently usable and check that in the TLB.

Change-Id: Ic2c09e1cfa05afcb03900213b72733545c8f0f4c
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/55245
Maintainer: Gabe Black <gabe.black@gmail.com>
Tested-by: kokoro <noreply+kokoro@google.com>
Reviewed-by: Bobby Bruce <bbruce@ucdavis.edu>
This commit is contained in:
Gabe Black
2022-01-08 23:52:13 -08:00
parent 0ad4a0b774
commit a9ef634fa8
4 changed files with 19 additions and 9 deletions

View File

@@ -255,7 +255,8 @@ InitInterrupt::invoke(ThreadContext *tc, const StaticInstPtr &inst)
tc->setMiscReg(MISCREG_IDTR_LIMIT, 0xffff);
SegAttr tslAttr = 0;
tslAttr.present = 1;
tslAttr.unusable = 1;
tslAttr.present = 0;
tslAttr.type = 2; // LDT
tc->setMiscReg(MISCREG_TSL, 0);
tc->setMiscReg(MISCREG_TSL_BASE, 0);
@@ -263,7 +264,8 @@ InitInterrupt::invoke(ThreadContext *tc, const StaticInstPtr &inst)
tc->setMiscReg(MISCREG_TSL_ATTR, tslAttr);
SegAttr trAttr = 0;
trAttr.present = 1;
trAttr.unusable = 1;
trAttr.present = 0;
trAttr.type = 3; // Busy 16-bit TSS
tc->setMiscReg(MISCREG_TR, 0);
tc->setMiscReg(MISCREG_TR_BASE, 0);

View File

@@ -1669,9 +1669,15 @@ let {{
SegLimitDest = desc.limit;
SegAttrDest = attr;
} else {
HandyM5Reg m5reg = M5Reg;
SegBaseDest = SegBaseDest;
SegLimitDest = SegLimitDest;
SegAttrDest = SegAttrDest;
SegAttr attr = SegAttrDest;
if (m5reg != LongMode)
attr.unusable = 1;
SegAttrDest = attr;
}
break;
}

View File

@@ -322,7 +322,8 @@ X86_64Process::initState()
// LDT
tc->setMiscReg(MISCREG_TSL, 0);
SegAttr tslAttr = 0;
tslAttr.present = 1;
tslAttr.unusable = 1;
tslAttr.present = 0;
tslAttr.type = 2;
tc->setMiscReg(MISCREG_TSL_ATTR, tslAttr);
@@ -684,6 +685,9 @@ I386Process::initState()
// Set the LDT selector to 0 to deactivate it.
tc->setMiscRegNoEffect(MISCREG_TSL, 0);
SegAttr attr = 0;
attr.unusable = 1;
tc->setMiscRegNoEffect(MISCREG_TSL_ATTR, attr);
Efer efer = 0;
efer.sce = 1; // Enable system call extensions.

View File

@@ -334,13 +334,11 @@ TLB::translate(const RequestPtr &req,
// If we're not in 64-bit mode, do protection/limit checks
if (m5Reg.mode != LongMode) {
DPRINTF(TLB, "Not in long mode. Checking segment protection.\n");
// Check for a NULL segment selector.
if (!(seg == SEGMENT_REG_TSG || seg == SYS_SEGMENT_REG_IDTR ||
seg == SEGMENT_REG_HS || seg == SEGMENT_REG_LS)
&& !tc->readMiscRegNoEffect(MISCREG_SEG_SEL(seg)))
SegAttr attr = tc->readMiscRegNoEffect(MISCREG_SEG_ATTR(seg));
// Check for an unusable segment.
if (attr.unusable)
return std::make_shared<GeneralProtection>(0);
bool expandDown = false;
SegAttr attr = tc->readMiscRegNoEffect(MISCREG_SEG_ATTR(seg));
if (seg >= SEGMENT_REG_ES && seg <= SEGMENT_REG_HS) {
if (!attr.writable && (mode == BaseMMU::Write || storeCheck))
return std::make_shared<GeneralProtection>(0);