/*	$OpenBSD: rd.c,v 1.4 2013/03/24 19:20:35 deraadt Exp $	*/

/*
 * Copyright (c) 2010 Miodrag Vallat.
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/param.h>
#include "libsa.h"
#include <machine/cpu.h>
#include <sys/exec_elf.h>

static	off_t rdoffs;

/*
 * INITRD I/O
 */

int
rd_iostrategy(void *f, int rw, daddr32_t dblk, size_t size, void *buf,
    size_t *rsize)
{
	/* never invoked directly */
	return ENXIO;
}

int
rd_ioopen(struct open_file *f, ...)
{
	return 0;
}

int
rd_ioclose(struct open_file *f)
{
	return 0;
}

int
rd_isvalid()
{
	Elf64_Ehdr *elf64 = (Elf64_Ehdr *)INITRD_BASE;

	if (memcmp(elf64->e_ident, ELFMAG, SELFMAG) != 0 ||
	    elf64->e_ident[EI_CLASS] != ELFCLASS64 ||
	    elf64->e_ident[EI_DATA] != ELFDATA2LSB ||
	    elf64->e_type != ET_EXEC || elf64->e_machine != EM_MIPS)
		return 0;

	return 1;
}

void
rd_invalidate()
{
	bzero((void *)INITRD_BASE, sizeof(Elf64_Ehdr));
}

/*
 * INITRD filesystem
 */
int
rdfs_open(char *path, struct open_file *f)
{
	if (f->f_dev->dv_open == rd_ioopen) {
		if (strcmp(path, kernelfile) == 0) {
			rdoffs = 0;
			return 0;
		} else
			return ENOENT;
	}

	return EINVAL;
}

int
rdfs_close(struct open_file *f)
{
	return 0;
}

int
rdfs_read(struct open_file *f, void *buf, size_t size, size_t *resid)
{
	if (size != 0) {
		bcopy((void *)(INITRD_BASE + rdoffs), buf, size);
		rdoffs += size;
	}
	*resid = 0;

	return 0;
}

int
rdfs_write(struct open_file *f, void *buf, size_t size, size_t *resid)
{
	return EROFS;
}

off_t
rdfs_seek(struct open_file *f, off_t offset, int whence)
{
	switch (whence) {
	case 0:	/* SEEK_SET */
		rdoffs = offset;
		break;
	case 1: /* SEEK_CUR */
		rdoffs += offset;
		break;
	default:
		errno = EIO;
		return -1;
	}

	return rdoffs;
}

int
rdfs_stat(struct open_file *f, struct stat *sb)
{
	return EIO;
}

#ifndef NO_READDIR
int
rdfs_readdir(struct open_file *f, char *path)
{
	return EIO;
}
#endif
