268 lines
6.5 KiB
C
268 lines
6.5 KiB
C
/* $NetBSD: testldt.c,v 1.4 1995/04/20 22:42:38 cgd Exp $ */
|
|
|
|
#include <stdio.h>
|
|
#include <signal.h>
|
|
#include <sys/types.h>
|
|
#include <sys/mman.h>
|
|
#include <machine/segments.h>
|
|
|
|
extern int i386_get_ldt(int, union descriptor *, int);
|
|
extern int i386_set_ldt(int, union descriptor *, int);
|
|
|
|
int verbose = 0;
|
|
struct sigaction segv_act;
|
|
|
|
inline void set_fs(unsigned long val)
|
|
{
|
|
__asm__ __volatile__("mov %0,%%fs"::"r" ((unsigned short) val));
|
|
}
|
|
|
|
inline unsigned char get_fs_byte(const char * addr)
|
|
{
|
|
unsigned register char _v;
|
|
|
|
__asm__ ("movb %%fs:%1,%0":"=q" (_v):"m" (*addr));
|
|
return _v;
|
|
}
|
|
|
|
inline unsigned short get_cs(void)
|
|
{
|
|
unsigned register short _v;
|
|
|
|
__asm__ ("movw %%cs,%0"::"r" ((unsigned short) _v));
|
|
return _v;
|
|
}
|
|
|
|
int
|
|
check_desc(unsigned int desc)
|
|
{
|
|
desc = LSEL(desc, SEL_UPL);
|
|
set_fs(desc);
|
|
return(get_fs_byte((char *) 0));
|
|
}
|
|
|
|
void
|
|
gated_call()
|
|
{
|
|
printf("Called from call gate...");
|
|
__asm__ __volatile__("popl %ebp");
|
|
__asm__ __volatile__(".byte 0xcb");
|
|
}
|
|
|
|
struct segment_descriptor *
|
|
make_sd(unsigned base, unsigned limit, int type, int dpl, int seg32, int inpgs)
|
|
{
|
|
static struct segment_descriptor d;
|
|
|
|
d.sd_lolimit = limit & 0x0000ffff;
|
|
d.sd_lobase = base & 0x00ffffff;
|
|
d.sd_type = type & 0x01f;
|
|
d.sd_dpl = dpl & 0x3;
|
|
d.sd_p = 1;
|
|
d.sd_hilimit = (limit & 0x00ff0000) >> 16;
|
|
d.sd_xx = 0;
|
|
d.sd_def32 = seg32?1:0;
|
|
d.sd_gran = inpgs?1:0;
|
|
d.sd_hibase = (base & 0xff000000) >> 24;
|
|
|
|
return (&d);
|
|
}
|
|
|
|
struct gate_descriptor *
|
|
make_gd(unsigned offset, unsigned int sel, unsigned stkcpy, int type, int dpl)
|
|
{
|
|
static struct gate_descriptor d;
|
|
|
|
d.gd_looffset = offset & 0x0000ffff;
|
|
d.gd_selector = sel & 0xffff;
|
|
d.gd_stkcpy = stkcpy & 0x1ff;
|
|
d.gd_type = type & 0x1ff;
|
|
d.gd_dpl = dpl & 0x3;
|
|
d.gd_p = 1;
|
|
d.gd_hioffset = (offset & 0xffff0000) >> 16;
|
|
|
|
return(&d);
|
|
}
|
|
|
|
void
|
|
print_ldt(union descriptor *dp)
|
|
{
|
|
unsigned long base_addr, limit, offset, selector, stack_copy;
|
|
int type, dpl, i;
|
|
unsigned long *lp = (unsigned long *)dp;
|
|
|
|
/* First 32 bits of descriptor */
|
|
selector = base_addr = (*lp >> 16) & 0x0000FFFF;
|
|
offset = limit = *lp & 0x0000FFFF;
|
|
lp++;
|
|
|
|
/* First 32 bits of descriptor */
|
|
base_addr |= (*lp & 0xFF000000) | ((*lp << 16) & 0x00FF0000);
|
|
limit |= (*lp & 0x000F0000);
|
|
type = dp->sd.sd_type;
|
|
dpl = dp->sd.sd_dpl;
|
|
stack_copy = dp->gd.gd_stkcpy;
|
|
offset |= (*lp >> 16) & 0x0000FFFF;
|
|
|
|
if (type == SDT_SYS386CGT || type == SDT_SYS286CGT)
|
|
printf("LDT: Gate Off %08.8x, Sel %05.5x, Stkcpy %d DPL %d, Type %d\n",
|
|
offset, selector, stack_copy, dpl, type);
|
|
else
|
|
printf("LDT: Seg Base %08.8x, Limit %05.5x, DPL %d, Type %d\n",
|
|
base_addr, limit, dpl, type);
|
|
printf(" ");
|
|
if (*lp & 0x100)
|
|
printf("Accessed, ");
|
|
if (*lp & 8000)
|
|
printf("Present, ");
|
|
if (type != SDT_SYS386CGT && type != SDT_SYS286CGT) {
|
|
if (*lp & 0x100000)
|
|
printf("User, ");
|
|
if (*lp & 0x200000)
|
|
printf("X, ");
|
|
if (*lp & 0x400000)
|
|
printf("32, ");
|
|
else
|
|
printf("16, ");
|
|
if (*lp & 0x800000)
|
|
printf("page limit, ");
|
|
else
|
|
printf("byte limit, ");
|
|
}
|
|
printf("\n");
|
|
printf(" %08.8x %08.8x\n", *(lp), *(lp-1));
|
|
}
|
|
|
|
static void busfault(int signal, int code, struct sigcontext *sc)
|
|
{
|
|
fprintf(stderr, "\nbus fault - investigate.\n");
|
|
exit(1);
|
|
}
|
|
|
|
static void usage(int status)
|
|
{
|
|
fprintf(stderr, "Usage: testldt [-v]\n");
|
|
exit(status);
|
|
}
|
|
|
|
#define MAX_USER_LDT 1024
|
|
main(int argc, char *argv[])
|
|
{
|
|
union descriptor ldt[MAX_USER_LDT];
|
|
int num, n, ch;
|
|
unsigned int cs = get_cs();
|
|
char *data;
|
|
struct segment_descriptor *sd;
|
|
struct gate_descriptor *gd;
|
|
|
|
segv_act.sa_handler = (sig_t) busfault;
|
|
if (sigaction(SIGBUS, &segv_act, NULL) < 0) {
|
|
perror("sigaction");
|
|
exit(1);
|
|
}
|
|
|
|
while ((ch = getopt(argc, argv, "v")) != EOF) {
|
|
switch (ch) {
|
|
case 'v':
|
|
verbose++;
|
|
break;
|
|
default:
|
|
usage(1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
printf("Testing i386_get_ldt...\n");
|
|
if ((num = i386_get_ldt(0, ldt, MAX_USER_LDT)) < 0) {
|
|
perror("get_ldt");
|
|
exit(2);
|
|
}
|
|
if (num == 0) {
|
|
fprintf(stderr, "ERROR: i386_get_ldt() return 0 default LDT entries.\n");
|
|
exit(1);
|
|
}
|
|
|
|
if (verbose) {
|
|
printf("Got %d (default) LDTs\n", num);
|
|
for (n=0; n < num; n++) {
|
|
printf("Entry %d: ", n);
|
|
print_ldt(&ldt[n]);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* mmap a data area and assign an LDT to it
|
|
*/
|
|
printf("Testing i386_set_ldt...\n");
|
|
data = (void *) mmap( (char *)0x005f0000, 0x0fff,
|
|
PROT_EXEC | PROT_READ | PROT_WRITE,
|
|
MAP_FIXED | MAP_PRIVATE | MAP_ANON, -1, 0);
|
|
if (data == NULL) {
|
|
perror("mmap");
|
|
exit(1);
|
|
}
|
|
if (verbose) printf("data address: %8.8x\n", data);
|
|
|
|
*data = 0x97;
|
|
|
|
/* Get the next free LDT and set it to the allocated data. */
|
|
sd = make_sd((unsigned)data, 4096, SDT_MEMRW, SEL_UPL, 0, 0);
|
|
if ((num = i386_set_ldt(6, (union descriptor *)sd, 1)) < 0) {
|
|
perror("set_ldt");
|
|
exit(1);
|
|
}
|
|
if (verbose) printf("setldt returned: %d\n", num);
|
|
if ((n = i386_get_ldt(num, ldt, 1)) < 0) {
|
|
perror("get_ldt");
|
|
exit(1);
|
|
}
|
|
if (verbose) {
|
|
printf("Entry %d: ", num);
|
|
print_ldt(&ldt[0]);
|
|
}
|
|
|
|
if (verbose) printf("Checking desc (should be 0x97): 0x%x\n", check_desc(num));
|
|
if (check_desc(num) != 0x97) {
|
|
fprintf(stderr, "ERROR: descriptor check failed: (should be 0x97): 0x%x\n", check_desc(num));
|
|
exit(1);
|
|
}
|
|
|
|
/*
|
|
* Test a Call Gate
|
|
*/
|
|
printf("Testing Call Gate...");
|
|
gd = make_gd((unsigned)gated_call, cs, 0, SDT_SYS386CGT, SEL_UPL);
|
|
if ((num = i386_set_ldt(5, (union descriptor *)gd, 1)) < 0) {
|
|
perror("set_ldt: call gate");
|
|
exit(1);
|
|
}
|
|
if (verbose) printf("setldt returned: %d\n", num);
|
|
if (verbose) printf("Call gate sel = 0x%x\n", LSEL(num, SEL_UPL));
|
|
if ((n = i386_get_ldt(num, ldt, 1)) < 0) {
|
|
perror("get_ldt");
|
|
exit(1);
|
|
}
|
|
if (verbose) printf("Entry %d: ", num);
|
|
if (verbose) print_ldt(&ldt[0]);
|
|
|
|
#if 0
|
|
err = setldt(5,
|
|
gated_call, /* Offset */
|
|
0x0001, /* This selector is for the executable segment descriptor. It
|
|
is the standard linux text descriptor. */
|
|
0x00008c00); /* Descriptor flags (you can't set all, the OS protects some) */
|
|
printf("setldt returned: %d\n", err);
|
|
#endif
|
|
|
|
__asm__ __volatile__(".byte 0x9a"); /* This is a call to a call gate. */
|
|
__asm__ __volatile__(".byte 0x00"); /* Value is ignored in a call gate but can be used. */
|
|
__asm__ __volatile__(".byte 0x00"); /* by the called procedure. */
|
|
__asm__ __volatile__(".byte 0x00");
|
|
__asm__ __volatile__(".byte 0x00");
|
|
__asm__ __volatile__(".byte 0x2f"); /* Selector 0x002f. This is index = 5 (the call gate), */
|
|
__asm__ __volatile__(".byte 0x00"); /* and a requestor priveledge level of 3. */
|
|
|
|
printf("Gated call returned\n");
|
|
exit (0);
|
|
}
|