NetBSD-5.0.2/regress/sys/arch/i386/ldt/testldt.c

Compare this file to the similar file:
Show the results in this format:

/*	$NetBSD: testldt.c,v 1.14 2008/04/28 20:23:05 martin Exp $	*/

/*-
 * Copyright (c) 1993 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <err.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/mman.h>
#include <machine/segments.h>
#include <machine/sysarch.h>

static void
set_fs(unsigned int val)
{
	__asm volatile("mov %0,%%fs"::"r" ((unsigned short) val));
}

static unsigned char
get_fs_byte(const char *addr)
{
	unsigned char _v;

	__asm ("movb %%fs:%1,%0":"=q" (_v):"m" (*addr));
	return _v;
}

static unsigned short
get_cs(void)
{
	unsigned short _v;

	__asm ("movw %%cs,%0": "=r" ((unsigned short) _v));
	return _v;
}

static int
check_desc(int desc)
{
	unsigned int sel = LSEL(desc, SEL_UPL);
	set_fs(sel);
	return(get_fs_byte((char *) 0));
}

static void
gated_call(void)
{
	printf("Called from call gate...");
	__asm volatile("movl %ebp,%esp");
	__asm volatile("popl %ebp");
	__asm volatile(".byte 0xcb");
}

static union descriptor *
make_sd(void *basep, unsigned limit, int type, int dpl, int seg32, int inpgs)
{
	static union descriptor d;
	unsigned long base = (unsigned long)basep;

	d.sd.sd_lolimit = limit & 0x0000ffff;
	d.sd.sd_lobase  = base & 0x00ffffff;
	d.sd.sd_type    = type & 0x01f;
	d.sd.sd_dpl     = dpl & 0x3;
	d.sd.sd_p	     = 1;
	d.sd.sd_hilimit = (limit & 0x00ff0000) >> 16;
	d.sd.sd_xx	     = 0;
	d.sd.sd_def32   = seg32?1:0;
	d.sd.sd_gran    = inpgs?1:0;
	d.sd.sd_hibase  = (base & 0xff000000) >> 24;

	return (&d);
}

static union descriptor *
make_gd(void *func, unsigned int sel, unsigned stkcpy, int type, int dpl)
{
	static union descriptor d;
	unsigned long offset = (unsigned long)func;

	d.gd.gd_looffset = offset & 0x0000ffff;
	d.gd.gd_selector = sel & 0xffff;
	d.gd.gd_stkcpy   = stkcpy & 0x1ff;
	d.gd.gd_type     = type & 0x1ff;
	d.gd.gd_dpl      = dpl & 0x3;
	d.gd.gd_p	      = 1;
	d.gd.gd_hioffset = (offset & 0xffff0000) >> 16;

	return(&d);
}

static const char *
seg_type_name[] = {
	"SYSNULL", "SYS286TSS", "SYSLDT", "SYS286BSY",
	"SYS286CGT", "SYSTASKGT", "SYS286IGT", "SYS286TGT",
	"SYSNULL2", "SYS386TSS", "SYSNULL3", "SYS386BSY",
	"SYS386CGT", "SYSNULL4", "SYS386IGT", "SYS386TGT",
	"MEMRO", "MEMROA", "MEMRW", "MEMRWA",
	"MEMROD", "MEMRODA", "MEMRWD", "MEMRWDA",
	"MEME", "MEMEA", "MEMER", "MEMERA",
	"MEMEC", "MEMEAC", "MEMERC", "MEMERAC"
};

static const char *
segtype(unsigned int type)
{
	if (type >= 32)
		return "Out of range";
	
	return seg_type_name[type];
}
	

static void
print_ldt(union descriptor *dp)
{
	unsigned long base_addr, limit, offset, stack_copy;
	unsigned int type, dpl;
	unsigned long lp[2];

	(void) memcpy(lp, dp, sizeof(lp));
    
	base_addr = dp->sd.sd_lobase | (dp->sd.sd_hibase << 24);
	limit = dp->sd.sd_lolimit | (dp->sd.sd_hilimit << 16);
	offset = dp->gd.gd_looffset | (dp->gd.gd_hioffset << 16);
	
	type = dp->sd.sd_type;
	dpl = dp->sd.sd_dpl;
	stack_copy = dp->gd.gd_stkcpy;

	if (type == SDT_SYS386CGT || type == SDT_SYS286CGT)
		printf("LDT: Gate Off %8.8lx, Sel   %5.5x, Stkcpy %lu DPL %d,"
		    " Type %d/%s\n",
		    offset, dp->gd.gd_selector, stack_copy, dpl,
		    type, segtype(type));
	else
		printf("LDT: Seg Base %8.8lx, Limit %5.5lx, DPL %d, "
		    "Type %d/%s\n",
		    base_addr, limit, dpl,
		    type, segtype(type));
	printf("	  ");
	if (type & 0x1)
		printf("Accessed, ");
	if (dp->sd.sd_p)
		printf("Present, ");
	if (type != SDT_SYS386CGT && type != SDT_SYS286CGT) {
		if (type & 0x10)
			printf("User, ");
		if (type & 0x08)
			printf("X, ");
		if (dp->sd.sd_def32)
			printf("32-bit, ");
		else
			printf("16-bit, ");
		if (dp->sd.sd_gran)
			printf("page limit, ");
		else
			printf("byte limit, ");
	}
	printf("\n");
	printf("	  Raw descriptor: %08lx %08lx\n", lp[0], lp[1]);
}

/* ARGSUSED */
static void
busfault(int signo)
{
	errx(1, "%s\n - investigate.", sys_siglist[signo]);
}

static void
usage(int status)
{
	errx(status, "Usage: testldt [-v]");
}

#define MAX_USER_LDT 1024

int
main(int argc, char *argv[])
{
	union descriptor ldt[MAX_USER_LDT];
	int n, ch;
	int num;
	unsigned int cs = get_cs();
	unsigned char *data;
	union descriptor *sd, *gd;
	unsigned char one = 1, two = 2, val;
	struct sigaction segv_act;
	int verbose = 0;

	segv_act.sa_handler = busfault;
	if (sigaction(SIGBUS, &segv_act, NULL) < 0)
		err(1, "sigaction");

	while ((ch = getopt(argc, argv, "v")) != -1) {
		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)
		err(2, "get_ldt");

	if (num == 0)
		errx(1, "i386_get_ldt() returned empty initial LDT");

	if (verbose) {
		printf("Got %d (initial) LDT entries\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, (off_t)0);
	if (data == NULL)
		err(1, "mmap");

	if (verbose)
		printf("data address: %p\n", data);

	*data = 0x97;

	/* Get the next free LDT and set it to the allocated data. */
	sd = make_sd(data, 2048, SDT_MEMRW, SEL_UPL, 0, 0);
	if ((num = i386_set_ldt(1024, sd, 1)) < 0)
		err(1, "set_ldt");

	if (verbose) printf("setldt returned: %d\n", num);
	if ((n = i386_get_ldt(num, ldt, 1)) < 0)
		err(1, "get_ldt");

	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)
		errx(1, "ERROR: descriptor check failed; "
		    "expected 0x97, got 0x%x", check_desc(num));
	
	/*
	 * Test a Call Gate
	 */
	printf("Making Call Gate\n");
	fflush(stdout);
	
	gd = make_gd((void *)gated_call, cs, 0, SDT_SYS386CGT, SEL_UPL);
	if ((num = i386_set_ldt(4095, gd, 1)) < 0)
		err(1, "set_ldt: call gate");

	if (verbose) {
		printf("setldt returned: %d\n", num);
		printf("Call gate sel = 0x%x\n", LSEL(num, SEL_UPL));
	}

	if ((n = i386_get_ldt(num, ldt, 1)) < 0)
		err(1, "get_ldt");

	if (verbose) {
		printf("Entry %d: ", num);
		print_ldt(&ldt[0]);
	}

	printf("Testing call gate...");
	fflush(stdout);
	/*
	 * Long call to call gate.
	 * Only the selector matters; offset is irrelevant.
	 */
	__asm volatile("lcall $0x7fff,$0x0");

	printf("Gated call returned\n");

	/*
	 * Test multiple sets.
	 */

	printf("Testing multiple descriptors at once.\n");

	sd = (union descriptor *)malloc (sizeof(*sd) * 2);
	if (sd == NULL)
		err(1, "can't malloc");

	sd[0] = *make_sd(&one, 1, SDT_MEMRO, SEL_UPL, 1, 0);
	sd[1] = *make_sd(&two, 1, SDT_MEMRO, SEL_UPL, 1, 0);

	if ((num = i386_set_ldt(8000, (union descriptor *)sd, 2)) < 0)
		err(1, "set_ldt");

	if (verbose) printf("setldt returned: %d\n", num);
	if ((n = i386_get_ldt(num, ldt, 2)) < 0)
		err(1, "get_ldt");

	if (verbose) {
		printf("Entry %d: ", num);
		print_ldt(&ldt[0]);
		printf("Entry %d: ", num+1);
		print_ldt(&ldt[1]);
	}
	val = check_desc(num);
	printf("contents of segment ONE: %x\n", val);
	if (val != 1) {
		errx(1, "ONE has unexpected value %x", val);
	}
	val = check_desc(num+1);	
	printf("contents of segment TWO: %x\n", val);
	if (val != 2)
		errx(1, "TWO has unexpected value %x", val);

	if ((n = i386_get_ldt(num, ldt, 2)) < 0)
		err(1, "get_ldt");		

	if (verbose) {
		printf("Entry %d: ", num);
		print_ldt(&ldt[0]);
		printf("Entry %d: ", num+1);
		print_ldt(&ldt[1]);
	}

	return 0;
}