Skip site navigation (1)Skip section navigation (2)
Date:      Tue, 18 May 2004 12:44:28 -0400
From:      Jung-uk Kim <jkim@niksun.com>
To:        freebsd-current@freebsd.org, freebsd-x11@freebsd.org
Subject:   Test your agp(4) before blaming on DRM/DRI!
Message-ID:  <200405181244.28716.jkim@niksun.com>

next in thread | raw e-mail | index | archive | help

--Boundary-00=_s1jqACQmfCTEW1t
Content-Type: text/plain;
  charset="us-ascii"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

I was trying to make my Intel i815 and VIA K8T800 chipsets working 
with agp(4) and I found that there were number of issues in the 
driver.  These issues were mainly from adding PCI IDs blindly without 
verifying proper documentation.  On top of that, some drivers do not 
care whether the bridge is supported or not and just attach as 
'generic' bridge, e. g., agp_sis.c and agp_via.c.  This gives users 
false hope and I believe this is wrong.

I found the following from DRI website:

http://dri.sourceforge.net/res/testgart.c

and SuSE Linux had little newer package:

http://www.suse.de/us/private/products/suse_linux/prof/packages_professional/testgart.html

I modified SuSE's to work with FreeBSD.  Test your GART before blaming 
on DRM/DRI!  If aperture size is reported differently from BIOS 
setting, that is not supported correctly, e. g., i815 - different 
aperture size encoding and registers.  If writeback test fails, 
that's more serious problem, e. g., K8T800 - doesn't work at all for 
me.

BTW, you have to unload your drm driver (or recompile kernel if you 
compiled it in) before you test agp.

I hope this helps many confused users.

Cheers,

Jung-uk Kim


* PS: Is there anybody working on AMD64 on-CPU GART driver like this?

http://fxr.watson.org/fxr/source/drivers/char/agp/amd64-agp.c?v=linux-2.6.1

Documentation is here (Section 3.6.11-14):

http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/26094.PDF

--Boundary-00=_s1jqACQmfCTEW1t
Content-Type: text/plain;
  charset="us-ascii";
  name="testgart.c"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
	filename="testgart.c"

/*
 * Test program for AGPGART module under Linux & FreeBSD
 * 
 * Copyright (C) 1999 Jeff Hartmann, Precision Insight, Inc., Xi Graphics, Inc.
 * 
 * FreeBSD porting by Jung-uk Kim <jkim@niksun.com>
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/agpio.h>
#if defined (__i386__) || defined (__amd64__)
#include <sys/memrange.h>
#endif
#include <errno.h>

unsigned char  *gart;
int		gartfd;
#if defined (__i386__) || defined (__amd64__)
int		mtrr;
u_int64_t	gart_base;
u_int64_t	gart_range;
#endif

int
usec(void)
{
	struct timeval	tv;
	struct timezone	tz;

	gettimeofday(&tv, &tz);
	return (tv.tv_sec & 2047) * 1000000 + tv.tv_usec;
}

int
MemoryBenchmark(void *buffer, int dwords)
{
	int		i;
	int		start, end;
	int		mb;
	int            *base;

	base = (int *)buffer;
	start = usec();
	for (i = 0; i < dwords; i += 8) {
		base[i] =
			base[i + 1] =
			base[i + 2] =
			base[i + 3] =
			base[i + 4] =
			base[i + 5] =
			base[i + 6] =
			base[i + 7] = 0x15151515;	/* dmapad nops */
	}
	end = usec();
	mb = ((float)dwords / 0x40000) * 1000000 / (end - start);
	printf("start=%d, end=%d\n", start, end);
	printf("MemoryBenchmark: %i mb/s\n", mb);
	return mb;
}

int
insert_gart(int page, int size)
{
	agp_allocate	entry;
	agp_bind	bind;

	entry.type = 0;
	entry.pg_count = size;
#ifdef DEBUG
	printf("Using AGPIOC_ALLOCATE\n");
#endif
	if (ioctl(gartfd, AGPIOC_ALLOCATE, &entry) != 0) {
		perror("ioctl(AGPIOC_ALLOCATE)");
		exit(1);
	}
	bind.key = entry.key;
	bind.pg_start = page;
#ifdef DEBUG
	printf("Using AGPIOC_BIND\n");
#endif
	if (ioctl(gartfd, AGPIOC_BIND, &bind)) {
		perror("ioctl(AGPIOC_BIND)");
		exit(1);
	}
	printf("entry.key : %u\n", entry.key);

	return (entry.key);
}

int
unbind_gart(int key)
{
	agp_unbind	unbind;

	unbind.key = key;
	unbind.priority = 0;
#ifdef DEBUG
	printf("Using AGPIOC_UNBIND\n");
#endif
	if (ioctl(gartfd, AGPIOC_UNBIND, &unbind) != 0) {
		perror("ioctl(AGPIOC_UNBIND)");
		exit(1);
	}
	return (0);
}

int
bind_gart(int key, int page)
{
	agp_bind	bind;

	bind.key = key;
	bind.pg_start = page;
#ifdef DEBUG
	printf("Using AGPIOC_BIND\n");
#endif
	if (ioctl(gartfd, AGPIOC_BIND, &bind) != 0) {
		perror("ioctl(AGPIOC_BIND)");
		exit(1);
	}
	return (0);
}

int
remove_gart(int key)
{
#ifdef DEBUG
	printf("Using AGPIOC_DEALLOCATE\n");
#endif
	if (ioctl(gartfd, AGPIOC_DEALLOCATE, &key) != 0) {
		perror("ioctl(AGPIOC_DEALLOCATE)");
		exit(1);
	}
	return (0);
}

#if defined (__i386__) || defined (__amd64__)
void
openmtrr(void)
{
	struct mem_range_op	mop;

	if ((mtrr = open("/dev/mem", O_WRONLY, 0)) == -1) {
		perror("open(\"/dev/mem\")");
		return;
	}
	mop.mo_arg[0] = 0;
	if (ioctl(mtrr, MEMRANGE_GET, &mop) == -1) {
		perror("ioctl(MEMRANGE_GET)");
		mtrr = -1;
		return;
	}
}

void
closemtrr(void)
{
	struct mem_range_desc	sentry;
	struct mem_range_op	mop;

	sentry.mr_base = gart_base;
	sentry.mr_len = gart_range;
	sentry.mr_owner[0] = '\0';
	mop.mo_desc = &sentry;
	mop.mo_arg[0] = MEMRANGE_SET_REMOVE;
	mop.mo_arg[1] = 0;

	if (ioctl(mtrr, MEMRANGE_SET, &mop) == -1) {
		perror("ioctl(MEMRANGE_SET)");
		exit(1);
	}
	close(mtrr);
}

void
CoverRangeWithMTRR(u_int64_t base, u_int64_t range, int type)
{
	struct mem_range_desc	sentry;
	struct mem_range_op	mop;

	printf("MTRR: %llx/%llx\n",
	       (unsigned long long)base, (unsigned long long)range);
	/* set it if we aren't just checking the number */
	if (type != -1) {
		sentry.mr_base = base;
		sentry.mr_len = range;
		sentry.mr_flags = type;
		strcpy(sentry.mr_owner, "agpgart");
		mop.mo_desc = &sentry;
		mop.mo_arg[0] = MEMRANGE_SET_UPDATE;
		mop.mo_arg[1] = 0;

		if (ioctl(mtrr, MEMRANGE_SET, &mop) == -1) {
			perror("ioctl(MEMRANGE_SET)");
			exit(1);
		}
	}
}
#endif

int
init_agp(void)
{
	agp_info	info;
	agp_setup	setup;

#ifdef DEBUG
	printf("Using AGPIOC_ACQUIRE\n");
#endif
	if (ioctl(gartfd, AGPIOC_ACQUIRE, 0) != 0) {
		perror("ioctl(AGPIOC_ACQUIRE)");
		exit(1);
	}
#ifdef DEBUG
	printf("Using AGPIOC_INFO\n");
#endif
	if (ioctl(gartfd, AGPIOC_INFO, &info) != 0) {
		perror("ioctl(AGPIOC_INFO)");
		exit(1);
	}
	printf("version: %u.%u\n", info.version.major, info.version.minor);
	printf("bridge id: 0x%lx\n", (unsigned long)info.bridge_id);
	printf("agp_mode: 0x%lx\n", (unsigned long)info.agp_mode);
	printf("aper_base: 0x%lx\n", (unsigned long)info.aper_base);
	printf("aper_size: %u\n", (unsigned int)info.aper_size);
	printf("pg_total: %u\n", (unsigned int)info.pg_total);
	printf("pg_system: %u\n", (unsigned int)info.pg_system);
	printf("pg_used: %u\n", (unsigned int)info.pg_used);

#if defined (__i386__) || defined (__amd64__)
	openmtrr();
	if (mtrr != -1) {
		gart_base = (u_int64_t)info.aper_base;
		gart_range = (u_int64_t)info.aper_size << 20;
		CoverRangeWithMTRR(gart_base, gart_range, MDF_WRITECOMBINE);
	}
#endif

	gart = mmap(NULL, (size_t)info.aper_size << 20,
		    PROT_READ | PROT_WRITE, MAP_SHARED, gartfd, 0);

	if (gart == MAP_FAILED) {
		perror("mmap()");
		close(gartfd);
		exit(1);
	}
	setup.agp_mode = info.agp_mode;
#ifdef DEBUG
	printf("Using AGPIOC_SETUP\n");
#endif
	if (ioctl(gartfd, AGPIOC_SETUP, &setup) != 0) {
		perror("ioctl(AGPIOC_SETUP)");
		exit(1);
	}
	return (0);
}

int		xchangeDummy;

#if defined (__i386__) || defined (__amd64__)
void
FlushWriteCombining(void)
{
	__asm__ volatile (
		" push %%eax ; xchg %%eax, %0 ; pop %%eax"
		: /* no outputs */ : "m" (xchangeDummy)
	);
	__asm__ volatile (
		" push %%eax ; push %%ebx ; push %%ecx ; push %%edx ;"
		" movl $0,%%eax ; cpuid ;"
		" pop %%edx ; pop %%ecx ; pop %%ebx ; pop %%eax"
		: /* no outputs */ : /* no inputs */
	);
}
#endif

void
BenchMark(int key, int key2)
{
	int		i, worked = 1;

	i = MemoryBenchmark(gart, (1024 * 1024 * 4) / 4) +
		MemoryBenchmark(gart, (1024 * 1024 * 4) / 4) +
		MemoryBenchmark(gart, (1024 * 1024 * 4) / 4);

	printf("Average speed: %i mb/s\n", i / 3);

	printf("Testing data integrity (1st pass): ");
	fflush(stdout);

#if defined (__i386__) || defined (__amd64__)
	FlushWriteCombining();
#endif

	for (i = 0; i < 0x800000; i++) {
		gart[i] = i % 256;
	}

#if defined (__i386__) || defined (__amd64__)
	FlushWriteCombining();
#endif

	for (i = 0; i < 0x800000; i++) {
		if (!(gart[i] == i % 256)) {
#ifdef DEBUG
			printf("failed on %i, gart[i] = %i\n", i, gart[i]);
#endif
			worked = 0;
		}
	}

	if (!worked)
		printf("failed on first pass!\n");
	else
		printf("passed on first pass.\n");

	unbind_gart(key);
	unbind_gart(key2);
	bind_gart(key, 0);
	bind_gart(key2, 1024);

	worked = 1;
	printf("Testing data integrity (2nd pass): ");
	fflush(stdout);

	for (i = 0; i < 0x800000; i++) {
		if (!(gart[i] == i % 256)) {
#ifdef DEBUG
			printf("failed on %i, gart[i] = %i\n", i, gart[i]);
#endif
			worked = 0;
		}
	}

	if (!worked)
		printf("failed on second pass!\n");
	else
		printf("passed on second pass.\n");
}

int
main()
{
	int		key;
	int		key2;
#ifdef DEBUG
	agp_info	info;
#endif

	gartfd = open("/dev/agpgart", O_RDWR);
	if (gartfd == -1) {
		perror("open(\"/dev/agpgart\")");
		exit(1);
	}
	init_agp();

	key = insert_gart(0, 1024);
	key2 = insert_gart(1024, 1024);

#ifdef DEBUG
	printf("Using AGPIOC_INFO\n");
	if (ioctl(gartfd, AGPIOC_INFO, &info) != 0) {
		perror("ioctl(AGPIOC_INFO)");
		exit(1);
	}
	printf("version: %u.%u\n", info.version.major, info.version.minor);
	printf("bridge id: 0x%lx\n", (unsigned long)info.bridge_id);
	printf("agp_mode: 0x%lx\n", (unsigned long)info.agp_mode);
	printf("aper_base: 0x%lx\n", (unsigned long)info.aper_base);
	printf("aper_size: %u\n", (unsigned int)info.aper_size);
	printf("pg_total: %u\n", (unsigned int)info.pg_total);
	printf("pg_system: %u\n", (unsigned int)info.pg_system);
	printf("pg_used: %u\n", (unsigned int)info.pg_used);
#endif

	printf("Allocated 8 megs of GART memory\n");

	BenchMark(key, key2);

	remove_gart(key);
	remove_gart(key2);

#ifdef DEBUG
	printf("Using AGPIOC_RELEASE\n");
#endif
	if (ioctl(gartfd, AGPIOC_RELEASE, 0) != 0) {
		perror("ioctl(AGPIOC_RELEASE)");
		exit(1);
	}

	close(gartfd);
#if defined (__i386__) || defined (__amd64__)
	closemtrr();
#endif

	return 0;
}

--Boundary-00=_s1jqACQmfCTEW1t--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200405181244.28716.jkim>