blob: d878e1ae416ab279281e70449f3280ada2db259b [file] [log] [blame]
#include <err.h>
#include <stdio.h>
#include <stdint.h>
#include <signal.h>
#include <setjmp.h>
#include <string.h>
#include <xmmintrin.h>
#include <asm/prctl.h>
#include <sys/prctl.h>
extern int arch_prctl(int, unsigned long);
static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
int flags)
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction = handler;
sa.sa_flags = SA_SIGINFO | flags;
sigemptyset(&sa.sa_mask);
if (sigaction(sig, &sa, 0))
err(1, "sigaction");
}
static void clearhandler(int sig)
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = SIG_DFL;
sigemptyset(&sa.sa_mask);
if (sigaction(sig, &sa, 0))
err(1, "sigaction");
}
static jmp_buf sigill_jmp;
static void sigill(int sig, siginfo_t *si, void *ctx_void)
{
siglongjmp(sigill_jmp, 1);
}
/* returns GDT limit */
static uint16_t show_gdt_and_idt(void)
{
struct {
unsigned short limit;
unsigned long base;
} __attribute__((packed)) val;
uint16_t ret;
__asm__ ("sgdt %0" : "=m" (val));
printf("GDT: base = 0x%016lX limit = 0x%04hX\n", val.base, val.limit);
ret = val.limit;
__asm__ ("sidt %0" : "=m" (val));
printf("IDT: base = 0x%016lX limit = 0x%04hX\n", val.base, val.limit);
return ret;
}
static void show_ldt(void)
{
unsigned short ldt;
asm ("sldt %0" : "=rm" (ldt));
printf("LDT: 0x%04hX\n", ldt);
}
static void show_tr(void)
{
unsigned short tr;
asm ("str %0" : "=rm" (tr));
printf("TR: 0x%04X\n", tr);
}
static void show_msw(void)
{
static const struct msw_bit {
int pos;
const char *str;
} bits[] = {
{0, "PE"},
{1, "MP"},
{2, "EM"},
{3, "TS"}, /* this is actually interesting */
{4, "ET"},
{5, "NE"},
};
unsigned short msw, remaining;
asm ("smsww %0" : "=rm" (msw));
printf("MSW: 0x%04X", msw);
remaining = msw;
for (int i = 0; i < sizeof(bits) / sizeof(bits[0]); i++) {
if (msw & (1 << bits[i].pos)) {
printf(" %s", bits[i].str);
remaining &= ~(1 << bits[i].pos);
}
}
if (remaining)
printf(" unknown:0x%x", remaining);
printf("\n");
}
static void show_flags(void)
{
static char const * const flag_bits[] = {
[0] = "CF",
[1] = "FIXED",
[2] = "PF",
[4] = "AF",
[6] = "ZF",
[7] = "SF",
[8] = "TF", /* Detects 'si 100000' in gdb */
[9] = "IF",
[10] = "DF",
[11] = "OF",
[14] = "NT",
[16] = "RF", /* Invisible to pushf. */
[17] = "VM",
[18] = "AC",
[19] = "VIF",
[20] = "VIP",
[21] = "ID",
};
unsigned long flags;
asm ("sub $128, %%sp\n\t"
"xor %%ax,%%ax\n\t" /* Make arithmetic flags be deterministic */
"add $1, %%ax\n\t" /* Clear ZF and PF */
"pushf\n\t"
"pop %0\n\t"
"add $128, %%sp" : "=r" (flags) :
: "flags",
"eax");
printf("FLAGS: 0x%016lX", flags);
for (int i = 0; i < sizeof(flag_bits) / sizeof(flag_bits[0]); i++) {
if (flag_bits[i] && (flags & (1 << i))) {
printf(" %s", flag_bits[i]);
flags &= ~(1 << i);
}
}
printf(" IOPL=%lu", (flags >> 12) & 3);
flags &= ~0x3000;
if (flags)
printf(" unknown:0x%lx", flags);
printf("\n");
}
static void show_rdtscp(void)
{
unsigned int cpu;
sethandler(SIGILL, sigill, 0);
if (sigsetjmp(sigill_jmp, 0)) {
printf("RDTSCP: not supported\n");
} else {
__builtin_ia32_rdtscp(&cpu);
printf("RDTSCP: cpu %d\n", cpu);
}
clearhandler(SIGILL);
}
static const char *user_seg_types[] = {
"RO data",
"RW data",
"RO data, exp-down",
"RW data, exp-down",
"XO code",
"XR code",
"XO code, conforming",
"XR code, confirming",
};
static void show_segment(uint16_t index)
{
uint32_t has_limit = 0, has_ar = 0, limit, ar;
asm ("lsl %[index], %[limit]\n\t"
"jnz 1f\n\t"
"mov $1, %[has_limit]\n\t"
"1:"
: [limit] "=r" (limit), [has_limit] "+rm" (has_limit)
: [index] "r" ((index << 3) + 3));
asm ("larl %[index], %[ar]\n\t"
"jnz 1f\n\t"
"mov $1, %[has_ar]\n\t"
"1:"
: [ar] "=r" (ar), [has_ar] "+rm" (has_ar)
: [index] "r" ((index << 3) + 3));
if (!has_limit && !has_ar)
return;
printf("GDT entry %02hu: ", index);
if (has_limit)
printf(" limit = 0x%08X", limit);
if (has_ar) {
#define ARBITS(low, high) ((ar >> low) & ((1 << (high - low + 1)) - 1))
#define ARSTR(bit, str) (ARBITS(bit,bit) ? " " str : "")
printf(" access rights = 0x%08X\n"
" type=%x (%s%s) DPL=%d%s%s%s%s%s%s",
ar, ARBITS(8,11), user_seg_types[ARBITS(9,11)],
(ARBITS(8,8) ? ", accessed" : ""),
ARBITS(13,14),
ARSTR(12, "S"), ARSTR(15, "P"),
ARSTR(20, "AVL"), /* AVL is reserved for the OS */
ARSTR(21, "L"), ARSTR(22, "D/B"), ARSTR(23, "G"));
#undef ARSTR
#undef ARBITS
}
printf("\n");
}
int main()
{
// Optional: gives some visibility into how TLS works.
arch_prctl(ARCH_SET_GS, 500);
uint16_t gdtlimit = show_gdt_and_idt();
show_ldt();
show_tr();
show_msw();
show_flags();
show_rdtscp();
for (int i = 0; i <= gdtlimit; i++)
show_segment(i);
}