if ( *reps != 1 )
return X86EMUL_UNHANDLEABLE;
- /* This is a singleton operation: fail it with an exception. */
- x86_emul_hw_exception((seg == x86_seg_ss)
- ? TRAP_stack_error
- : TRAP_gp_fault, 0, &hvmemul_ctxt->ctxt);
+ /*
+ * Leave exception injection to the caller for non-user segments: We
+ * neither know the exact error code to be used, nor can we easily
+ * determine the kind of exception (#GP or #TS) in that case.
+ */
+ if ( is_x86_user_segment(seg) )
+ x86_emul_hw_exception((seg == x86_seg_ss)
+ ? TRAP_stack_error
+ : TRAP_gp_fault, 0, &hvmemul_ctxt->ctxt);
+
return X86EMUL_EXCEPTION;
}
if ( !reg->attr.fields.p )
goto out;
- switch ( access_type )
+ /* Read/write restrictions only exist for user segments. */
+ if ( reg->attr.fields.s )
{
- case hvm_access_read:
- if ( (reg->attr.fields.type & 0xa) == 0x8 )
- goto out; /* execute-only code segment */
- break;
- case hvm_access_write:
- if ( (reg->attr.fields.type & 0xa) != 0x2 )
- goto out; /* not a writable data segment */
- break;
- default:
- break;
+ switch ( access_type )
+ {
+ case hvm_access_read:
+ if ( (reg->attr.fields.type & 0xa) == 0x8 )
+ goto out; /* execute-only code segment */
+ break;
+ case hvm_access_write:
+ if ( (reg->attr.fields.type & 0xa) != 0x2 )
+ goto out; /* not a writable data segment */
+ break;
+ default:
+ break;
+ }
}
last_byte = (uint32_t)offset + bytes - !!bytes;
/* Is this a grows-down data segment? Special limit check if so. */
- if ( (reg->attr.fields.type & 0xc) == 0x4 )
+ if ( reg->attr.fields.s && (reg->attr.fields.type & 0xc) == 0x4 )
{
/* Is upper limit 0xFFFF or 0xFFFFFFFF? */
if ( !reg->attr.fields.db )
else
{
/*
- * LONG MODE: FS and GS add segment base. Addresses must be canonical.
+ * User segments are always treated as present. System segment may
+ * not be, and also incur limit checks.
*/
+ if ( is_x86_system_segment(seg) &&
+ (!reg->attr.fields.p || (offset + bytes - !!bytes) > reg->limit) )
+ goto out;
- if ( (seg == x86_seg_fs) || (seg == x86_seg_gs) )
+ /*
+ * LONG MODE: FS, GS and system segments: add segment base. All
+ * addresses must be canonical.
+ */
+ if ( seg >= x86_seg_fs )
addr += reg->base;
last_byte = addr + bytes - !!bytes;
if ( !okay )
{
- x86_emul_hw_exception(
- (seg == x86_seg_ss) ? TRAP_stack_error : TRAP_gp_fault,
- 0, &sh_ctxt->ctxt);
+ /*
+ * Leave exception injection to the caller for non-user segments: We
+ * neither know the exact error code to be used, nor can we easily
+ * determine the kind of exception (#GP or #TS) in that case.
+ */
+ if ( is_x86_user_segment(seg) )
+ x86_emul_hw_exception(
+ (seg == x86_seg_ss) ? TRAP_stack_error : TRAP_gp_fault,
+ 0, &sh_ctxt->ctxt);
return X86EMUL_EXCEPTION;
}
struct x86_emulate_ctxt;
-/* Comprehensive enumeration of x86 segment registers. */
+/*
+ * Comprehensive enumeration of x86 segment registers. Various bits of code
+ * rely on this order (general purpose before system, tr at the beginning of
+ * system).
+ */
enum x86_segment {
/* General purpose. Matches the SReg3 encoding in opcode/ModRM bytes. */
x86_seg_es,
x86_seg_ds,
x86_seg_fs,
x86_seg_gs,
- /* System. */
+ /* System: Valid to use for implicit table references. */
x86_seg_tr,
x86_seg_ldtr,
x86_seg_gdtr,
x86_seg_idtr,
- /*
- * Dummy: used to emulate direct processor accesses to management
- * structures (TSS, GDT, LDT, IDT, etc.) which use linear addressing
- * (no segment component) and bypass usual segment- and page-level
- * protection checks.
- */
+ /* No Segment: For accesses which are already linear. */
x86_seg_none
};
-#define is_x86_user_segment(seg) ((unsigned)(seg) <= x86_seg_gs)
+static inline bool is_x86_user_segment(enum x86_segment seg)
+{
+ unsigned int idx = seg;
+
+ return idx <= x86_seg_gs;
+}
+static inline bool is_x86_system_segment(enum x86_segment seg)
+{
+ return seg >= x86_seg_tr && seg < x86_seg_none;
+}
/* Classification of the types of software generated interrupts/exceptions. */
enum x86_swint_type {