view miscprog/grokdsn.c @ 277:5c47d916255e

compal/c156-boot.disasm: GPIO comments were wrong
author Mychaela Falconia <falcon@freecalypso.org>
date Mon, 05 Nov 2018 23:18:42 +0000
parents 805e99848aea
children
line wrap: on
line source

/*
 * We have TI's Leonardo reference schematics in the form of 3 different PDF
 * versions and an OrCAD DSN file corresponding to one of them.  The latter
 * appears (based on a cursory strings(1) inspection) to contain more
 * juicy information than is present in the PDF prints.  We need to extract
 * as much of this information as we can in order to replicate the lost
 * Leonardo board as closely as possible.
 *
 * The top level structure of this DSN file appears to be Microsoft CDF.
 * Therefore, I shall begin by parsing this "archive" structure and
 * extracting the individual files contained therein, in an effort to gain
 * more insight as to what goes with what than can be gleaned from strings(1)
 * on the raw DSN file.  This hack-utility is my CDF parser written for
 * this specific purpose.
 */

#include <sys/types.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <stdio.h>
#include <stdint.h>
#include <endian.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>

#define	HEADER_MFAT_ENTRIES	109
struct cdf_header {
	u_char		magic[8];
	u_char		uid[16];
	uint16_t	fmt_minor;
	uint16_t	fmt_major;
	uint16_t	byte_order;
	uint16_t	sector_size;
	uint16_t	subsec_size;
	u_char		rsvd1[10];
	uint32_t	fat_sectors;
	uint32_t	dir_start;
	u_char		rsvd2[4];
	uint32_t	min_large_file;
	uint32_t	subfat_start;
	uint32_t	subfat_sectors;
	uint32_t	mfat_start;
	uint32_t	mfat_sectors;
	uint32_t	fat_secids[HEADER_MFAT_ENTRIES];
};

struct dir_entry {
	uint16_t	uni_name[32];
	uint16_t	name_len;
	u_char		type;
	u_char		color;
	uint32_t	left_child;
	uint32_t	right_child;
	uint32_t	subtree_dirid;
	u_char		uid[16];
	u_char		user_flags[4];
	u_char		time_creat[8];
	u_char		time_lastmod[8];
	uint32_t	content_secid;
	uint32_t	content_bytes;
	u_char		rsvd[4];
};

char *dsnfilename;
u_char *filemapping;
struct cdf_header *cdf_header;
unsigned total_sectors, fat_nsectors;

#define	MAX_DIR_SECTORS	64
unsigned dir_nsectors, dir_secids[MAX_DIR_SECTORS];

open_and_mmap_file()
{
	int fd;
	struct stat st;

	fd = open(dsnfilename, O_RDONLY);
	if (fd < 0) {
		perror(dsnfilename);
		exit(1);
	}
	fstat(fd, &st);
	if (!S_ISREG(st.st_mode)) {
		fprintf(stderr, "error: %s is not a regular file\n",
			dsnfilename);
		exit(1);
	}
	if (st.st_size < 512) {
		fprintf(stderr, "error: %s is shorter than 512 bytes\n",
			dsnfilename);
		exit(1);
	}
	if (st.st_size % 512) {
		fprintf(stderr, "error: %s is not a multiple of 512 bytes\n",
			dsnfilename);
		exit(1);
	}
	filemapping = mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
	if (filemapping == MAP_FAILED) {
		perror("mmap");
		exit(1);
	}
	close(fd);
	cdf_header = (struct cdf_header *) filemapping;
	total_sectors = st.st_size / 512 - 1;
	return(0);
}

dump_cdf_header()
{
	printf("Magic: %02X %02X %02X %02X %02X %02X %02X %02X\n",
		cdf_header->magic[0], cdf_header->magic[1],
		cdf_header->magic[2], cdf_header->magic[3],
		cdf_header->magic[4], cdf_header->magic[5],
		cdf_header->magic[6], cdf_header->magic[7]);
	printf("Format version: %04X.%04X\n", le16toh(cdf_header->fmt_major),
		le16toh(cdf_header->fmt_minor));
	printf("Sector / subsector shift: %u / %u\n",
		le16toh(cdf_header->sector_size),
		le16toh(cdf_header->subsec_size));
	printf("Total FAT sectors: %u\n", le32toh(cdf_header->fat_sectors));
	printf("Directory start sector: %u\n", le32toh(cdf_header->dir_start));
	printf("File size threshold: %u\n",
		le32toh(cdf_header->min_large_file));
	return(0);
}

u_char *
get_sector_ptr(secid)
	unsigned secid;
{
	if (secid > total_sectors) {
		fprintf(stderr,
		"error: request for sector #%u; we only have %u in total\n",
			secid, total_sectors);
		exit(1);
	}
	return filemapping + (secid + 1) * 512;
}

init_fat_access()
{
	fat_nsectors = le32toh(cdf_header->fat_sectors);
	if (fat_nsectors > HEADER_MFAT_ENTRIES) {
		fprintf(stderr, "error: large FAT not supported\n");
		exit(1);
	}
	return(0);
}

u_char *
get_fat_sector(fat_sec_no)
	unsigned fat_sec_no;
{
	unsigned secid;

	if (fat_sec_no > fat_nsectors) {
		fprintf(stderr,
		"error: request for FAT sector #%u; we only have %u in total\n",
			fat_sec_no, fat_nsectors);
		exit(1);
	}
	secid = le32toh(cdf_header->fat_secids[fat_sec_no]);
	return get_sector_ptr(secid);
}

int32_t
get_fat_entry(entry_no)
	unsigned entry_no;
{
	unsigned fat_sec_no, entry_in_sec;
	uint32_t *fat_sector;

	fat_sec_no = entry_no / 128;
	entry_in_sec = entry_no % 128;
	fat_sector = (uint32_t *) get_fat_sector(fat_sec_no);
	return le32toh(fat_sector[entry_in_sec]);
}

dump_fat_chain(sarg)
	char *sarg;
{
	int i, n;

	init_fat_access();
	i = atoi(sarg);
	while (i >= 0) {
		n = get_fat_entry(i);
		printf("FAT[%d] = %d\n", i, n);
		i = n;
	}
	return(0);
}

locate_dir_sectors()
{
	int sec, next, num;

	sec = le32toh(cdf_header->dir_start);
	for (num = 0; sec >= 0; sec = next) {
		if (num >= MAX_DIR_SECTORS) {
			fprintf(stderr, "error: MAX_DIR_SECTORS exceeded\n");
			exit(1);
		}
		dir_secids[num++] = sec;
		next = get_fat_entry(sec);
	}
	dir_nsectors = num;
	return(0);
}

u_char *
get_dir_sector(dir_sec_no)
	unsigned dir_sec_no;
{
	unsigned secid;

	if (dir_sec_no > dir_nsectors) {
		fprintf(stderr,
		"error: request for dir sector #%u; we only have %u in total\n",
			dir_sec_no, dir_nsectors);
		exit(1);
	}
	secid = dir_secids[dir_sec_no];
	return get_sector_ptr(secid);
}

struct dir_entry *
get_dir_entry(dirid)
	unsigned dirid;
{
	unsigned dir_sec_no, entry_in_sec;
	struct dir_entry *dir_sector;

	dir_sec_no = dirid / 4;
	entry_in_sec = dirid % 4;
	dir_sector = (struct dir_entry *) get_dir_sector(dir_sec_no);
	return dir_sector + entry_in_sec;
}

dump_dir_entry(rec, indent)
	struct dir_entry *rec;
{
	int i, u;

	for (i = indent; i; i--)
		putchar(' ');
	for (i = 0; i < 32; i++) {
		u = le16toh(rec->uni_name[i]);
		if (u == 0)
			break;
		if (u >= ' ' && u <= '~')
			putchar(u);
		else
			printf("<%04X>", u);
	}
	printf(" (type %02X, length %u)\n", rec->type, rec->content_bytes);
	return(0);
}

dump_dir_level(dirid, indent)
	unsigned dirid;
{
	struct dir_entry *rec;
	int32_t ndir;

	rec = get_dir_entry(dirid);
	ndir = le32toh(rec->left_child);
	if (ndir >= 0)
		dump_dir_level(ndir, indent);
	dump_dir_entry(rec, indent);
	ndir = le32toh(rec->subtree_dirid);
	if (ndir >= 0)
		dump_dir_level(ndir, indent + 2);
	ndir = le32toh(rec->right_child);
	if (ndir >= 0)
		dump_dir_level(ndir, indent);
	return(0);
}

dump_dir_tree()
{
	init_fat_access();
	locate_dir_sectors();
	dump_dir_level(0, 0);
	return(0);
}

main(argc, argv)
	char **argv;
{
	if (sizeof(struct cdf_header) != 512) {
		fprintf(stderr, "error: struct cdf_header is misdefined\n");
		exit(1);
	}
	if (sizeof(struct dir_entry) != 128) {
		fprintf(stderr, "error: struct dir_entry is misdefined\n");
		exit(1);
	}
	if (argc < 3) {
		fprintf(stderr, "usage: %s binfile.dsn <op> [args]\n", argv[0]);
		exit(1);
	}
	dsnfilename = argv[1];
	open_and_mmap_file();
	if (!strcmp(argv[2], "hdr"))
		return dump_cdf_header();
	if (!strcmp(argv[2], "fatchain"))
		return dump_fat_chain(argv[3]);
	if (!strcmp(argv[2], "ls"))
		return dump_dir_tree();
	fprintf(stderr, "error: \"%s\" is not a recognized command\n", argv[2]);
	exit(1);
}