/******************************************************************************
 * Copyright (c) 2004, 2008 IBM Corporation
 * All rights reserved.
 * This program and the accompanying materials
 * are made available under the terms of the BSD License
 * which accompanies this distribution, and is available at
 * http://www.opensource.org/licenses/bsd-license.php
 *
 * Contributors:
 *     IBM Corporation - initial implementation
 *****************************************************************************/
#include "macros.h"
#include "romfs.h"

/*******************************************************************
 * Wrapper for romfs_lookup.
 *
 * Input:
 * R3 = address of filename string
 * R4 = address of struct romfs_t
 *		 0: file size (return)
 *		 8: flags     (return)
 *		10: fileaddr  (return and input: tells if first search)
 *		18: nextfile  (return)
 *		20: namep     (return)
 *
 * Find File Procedure
 * - set filename and rombase, on return 0 file properties are stored
 *   in romfs_t else struct not valid
 * 
 * Listing procedure
 * - clear romfs_t (important!)
 * - set filename = NULL and rombase and call returns first file 
 *   with properties in romfs_t including next-file pointer
 * - if nextpointer is non-zero then just the next file is returned
 *
 * Returns:
 * <Success>: 
 *      R3 = 0
 *	romfs_t is updated
 * <FileNotFound>:
 *      R3 = 1
 * 	romfs_t not touched
 *
 * Potentially modifies the following registers:
 *
 
 Example usage from C:

  int list_bootrom()
  {
	struct romfs_t rfs;
	int i;

	printf("Build: "__TIME__" "__DATE__" \n");

	i = 0;
	memset((void*) &rfs, 0, sizeof(struct romfs_t));
	printf("         No. File      Data          Size  Name\n");

	while (romfs_stat(NULL, &rfs) == 0) {
		i++;
		printf("         %02d: %08X  %08X   %7d  %s\n",
				i, rfs.fileaddr, rfs.datap, 
				rfs.size, rfs.namep);
	}
	if (0 == i) {
		printf("Error in reading ROMFS\n");
		return 1;
	}
	return 0;
  }
 *******************************************************************/
#define RFS_T_SIZE	0x00
#define RFS_T_FLAGS	0x08
#define RFS_T_FILEADDR	0x10
#define RFS_T_NEXT	0x18
#define RFS_T_NAME	0x20
#define RFS_T_DATA	0x28

#define RFS_H_NEXT	0x00
#define RFS_H_SIZE	0x08
#define RFS_H_FLAGS	0x10
#define RFS_H_DATA	0x18
#define RFS_H_NAME	0x20

ENTRY(romfs_stat_file)
	/* save link register and romfs_t pointer         */
	mflr	r15
	mr	r16, r4

	/* if filename R3 is 0 then its a listing request */
	/* if not then just continue to lookup name       */
	/* save R4 to R8 which is the address of header   */
	li	r7, 0
	cmpd	r3, r7
	beq	romfs_list
	bl	romfs_lookup
	mfsprg	r8, 1

	/* if file found then go to romfs_fill_properties */
	/* else return 1 to caller                        */
	cmpwi	r3, 0
	beq	romfs_fill_properties
	b	romfs_stat_end

  romfs_list:
	/* check if fileaddr == 0, in this case its       */
	/* the first search on this handle, so return all */
	/* info for file at rombase (R8=R4)               */
	ld	r6, RFS_T_FILEADDR(r4)
	mfsprg	r8, 1
	li	r7, 0
	cmpd	r7, r6
	beq	romfs_fill_properties

	/* check if next file != 0   by looking into      */
	/* romfs_t, if not then return (next = 0) 1       */
	li	r7, 0
	ld	r4, RFS_T_NEXT(r4)
	cmpd	r7, r4
	li	r3, 1
	beq	romfs_stat_end

	/* now next file is available so move R8 to next  */
	/* file address                                   */
	mr	r8, r4

  romfs_fill_properties:
	/* set properties in romfs_t takes R8 as address  */
	/* to file header and R16 as address of romfs_t   */
	mfsprg	r3, 1
	std	r8, RFS_T_FILEADDR(r16)

	ld	r4, RFS_H_NEXT(r8)
	li	r7, 0
	cmpd	r7, r4
	beq	$ + (2 * 4) /* =0 so add no rombase */
	add	r4, r4, r3
	std	r4, RFS_T_NEXT(r16)

	ld	r4, RFS_H_SIZE(r8)
	std	r4, RFS_T_SIZE(r16)
	ld	r4, RFS_H_FLAGS(r8)
	std	r4, RFS_T_FLAGS(r16)

	ld	r4, RFS_H_DATA(r8)
	add	r4, r4, r3
	std	r4, RFS_T_DATA(r16)

	addi	r4, r8, RFS_H_NAME
	std	r4, RFS_T_NAME(r16)

	li	r3, 0

	/* restore romfs_t pointer and link register      */
  romfs_stat_end:
	mr	r5, r16
	mtlr	r15
	blr

/*******************************************************************
 * Copies the data of file referenced by name string to address 
 * requires root address of filesystem.
 * FIXME: ignores flags
 *
 * Input:
 * R3 = address of filename string
 * R4 = ROMBASE
 * R5 = destination address
 *
 * Returns:
 * <Success>: R3 = 0, R6 = size, <FileNotFound>: R3 = 1
 * R5 is kept
 *  
 * Potentially modifies the following registers:
 * ctr, r15, r16, r17, r18
 * 
 * Uses the following calls with subsequent register modification:
 * - romfs_lookup
 *******************************************************************/
ASM_ENTRY(romfs_load)
	mflr	r15
	
	/* save R5 twice              */
	/* lookup file, input regs    */
	/* are already set            */
	/* if not found, just return  */
	mr	r16, r5
	mr	r17, r5
	bl	romfs_lookup
	cmpwi	r3, 1
	bne	0f
	mtlr	r15
	blr     /* abort, not found   */

	/* save data size for return  */
	/* found, copy data           */
	/* data size is in R6         */
0:
	//mr	r3, r6
	mtctr	r6
	addi	r16, r16, -1 /* dest  */
	addi	r5, r5, -1   /* source*/

	/* data is expected to be     */
	/* 8 byte aligned             */
	/* copy loop                  */
0:	lbzu	r18, 1(r5)
	stbu	r18, 1(r16)
	bdnz	0b

	/* restore size, keep padding */
	/* restore target address     */
	/* return                     */
	mr	r5, r17
	mtlr	r15
	blr

/*******************************************************************
 * looks up a file based on filename 
 *
 * Input:
 * R3 = address of filename string
 * R4 = ROMBASE
 *
 * Returns:
 * <Success>: 
 *      R3 = 0
 *      R4 = address of file header
 *      R5 = address of data (real address)
 *      R6 = size of data
 *      R7 = flags for file
 * <FileNotFound>:
 *      R3 = 1
 *
 * Potentially modifies the following registers:
 * R3, R4, R5, R6, R7, R8, R9
 * 
 * Uses the following calls with subsequent register modification:
 * - romfs_namematch
 *******************************************************************/
ASM_ENTRY(romfs_lookup)
	mflr	r9
	
  romfs_lookup_next:
  	/* save current file base        */
	mr	r8, r4
  	/* name to look for              */
	mr	r10, r3 
	/* name of file                  */
	mr	r5,  r4
	addi	r5,  r5, (4 /* elems     */ * 8 /* elem-size */)
	mr	r11, r5 /* for namematch */
	/* compare                       */
	bl	romfs_namematch
	cmpwi	r12, 1
	bne	romfs_lookup_match

	/* load next pointer             */
	/* check if next is 0            */
	/* apply root-offset             */
	ld	r5, 0(r4)
	cmpwi	r5, 0
	add	r4, r4, r5
	bne	romfs_lookup_next
	/* last file reached, abort      */
	li	r3, 1
	mtlr	r9
	blr

	/* here the name did match       */
	/* r4 is still usable here and   */
	/* pointing to the initial file  */
	/* load r5 with data ptr         */
	/* load r6 with data size        */
	/* load r7 with flags            */
	/* get abs addr of data          */
  romfs_lookup_match:
	li	r3, 0
	ld	r5, (3 * 8)(r4) /* data  */
	ld	r6, (1 * 8)(r4) /* len   */
	ld	r7, (2 * 8)(r4) /* flags */
	add	r5, r5, r8
	mtlr	r9
	blr

/*******************************************************************
 * compares two strings in memory, 
 * both must be null-terminated and 8-byte aligned
 *
 * Input:
 * R10 = string 1
 * R11 = string 2
 *
 * Returns:
 * <Match>: R12 = 0 <NoMatch>: R12 = 1
 *
 * Potentially modifies the following registers:
 * R10, R11, r12, r13, r14 
 *******************************************************************/
romfs_namematch:
	subi	r10, r10, 8
	subi	r11, r11, 8

	/* 
	 * load chars as 8byte chunk from current pos, name is 
	 * always 8 byte aligned :)
	 */
  romfs_cmp_loop:
	ldu	r13, 8(r10) /* A */
	ldu	r14, 8(r11) /* B */

	cmpd	r13, r14
	li	r12, 1
	beq	1f
	blr

1:	andi.	r14, r14, 0xff
	bne	romfs_cmp_loop

	li	r12, 0
	blr

/*******************************************************************
 * wrapper for romfs_lookup
 * this function saves the registers from r13 - r15 on the stack
 * calls romfs_lookup
 * restores the saved registers
 *
 * the return parameters are copied to (r5) and (r5) has to
 * be 0x20 big
 *******************************************************************/
ENTRY(c_romfs_lookup)
	stdu	r1,-0x50(r1)		# allocate space on stack

	mflr	r0			# save link register
	std	r0,0x30(r1)

	std	r15,0x38(r1)		# save r15
	std	r14,0x40(r1)		# save r14
	std	r13,0x48(r1)		# and r13
	
	mr	r15,r5			# save the pointer for the return value

	bl	romfs_lookup		# do the thing

	ld	r0,0x30(r1)		# restore link register
	mtlr	r0

	std	r4,0x00(r15)		# copy return values
	std	r5,0x08(r15)		# to the return pointer
	std	r6,0x10(r15)
	std	r7,0x18(r15)

	ld	r13,0x48(r1)		# restore registers from stack
	ld	r14,0x40(r1)
	ld	r15,0x38(r1)

	addi	r1,r1,0x50		# cleanup stack

	blr