/*
 *  p6uc - Update Intel Microcode on P6 processors.
 * 
 *  Copyright (C) 2000, Tigran Aivazian (GPL v2, as usual)
 *
 *  Reference: Section 8.10 of Volume III, 
 *  Intel Pentium III Manual, Order Number 243192.
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <stdarg.h>

#include <asm/mtrr.h>

struct mtrr_p6update ioc;
void * ucode;
static char * myname;

static void die(const char * fmt, ...);

int main(int argc, char *argv[])
{
	int fd;
	struct stat st;

	myname = argv[0];

	if (argc != 2) {
		fprintf(stderr, "usage: %s [mcode_file]\n", argv[0]);
		exit(1);
	}

	fd = open(argv[1], O_RDONLY);
	if (fd == -1) 
		die("open(%s)", argv[1]);
	if (fstat(fd, &st) == -1)
		die("fstat(fd=%d)", fd);
	if (st.st_size == 0 || (st.st_size % sizeof(struct p6ucode) != 0)) {
		fprintf(stderr, "%s: corrupted microcode in '%s' or 'struct p6ucode' "
				"in <asm/mtrr.h> needs updating\n", argv[0], argv[1]);
		exit(1);
	}
	ucode = malloc(st.st_size);
	if (!ucode)
		die("Can't malloc(%ld) to hold microcode\n", st.st_size);
	if (read(fd, ucode, st.st_size) != st.st_size)
		die("read(fd, ucode, %ld)", st.st_size);
	if (close(fd) == -1)
		die("close(fd=%d)", fd);
	fd = open("/proc/mtrr", O_RDONLY);
	if (fd == -1)
		die("open(/proc/mtrr)");
	ioc.num = st.st_size/sizeof(struct p6ucode);
	ioc.uaddr = ucode;
	if (ioctl(fd, MTRRIOC_P6UPDATE, &ioc) == -1)
		die("ioctl(MTRRIOC_P6UPDATE, num=%d)", ioc.num);
	if (close(fd) == -1) 
		die("close(fd=%d)", fd);
	return 0;
}

static void die(const char * fmt, ...)
{
	va_list args;
	static char buf[4096];

	va_start(args, fmt);
	vsprintf(buf, fmt, args);
	va_end(args);

	fprintf(stderr, "%s: %s, errno=%d (%s)\n", 
		myname, buf, errno, strerror(errno));
	exit(1);
}
