#include <stdio.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>

#include "loki.h"

#define BOOT_PATTERN1 "\x4f\xf4\x70\x40\xb3\x49\x2d\xe9"	/* Samsung GS4 */
#define BOOT_PATTERN2 "\x2d\xe9\xf0\x4f\xad\xf5\x82\x5d"	/* LG */
#define BOOT_PATTERN3 "\x2d\xe9\xf0\x4f\x4f\xf4\x70\x40"	/* LG */
#define BOOT_PATTERN4 "\x2d\xe9\xf0\x4f\xad\xf5\x80\x5d"	/* LG G2 */

int loki_find(const char* aboot_image)
{
	int aboot_fd;
	struct stat st;
	void *aboot, *ptr;
	unsigned long aboot_base, check_sigs, boot_mmc;

	aboot_fd = open(aboot_image, O_RDONLY);
	if (aboot_fd < 0) {
		printf("[-] Failed to open %s for reading.\n", aboot_image);
		return 1;
	}

	if (fstat(aboot_fd, &st)) {
		printf("[-] fstat() failed.\n");
		return 1;
	}

	aboot = mmap(0, (st.st_size + 0xfff) & ~0xfff, PROT_READ, MAP_PRIVATE, aboot_fd, 0);
	if (aboot == MAP_FAILED) {
		printf("[-] Failed to mmap aboot.\n");
		return 1;
	}

	check_sigs = 0;
	aboot_base = *(unsigned int *)(aboot + 12) - 0x28;

	/* Do a pass to find signature checking function */
	for (ptr = aboot; ptr < aboot + st.st_size - 0x1000; ptr++) {
		if (!memcmp(ptr, PATTERN1, 8) ||
			!memcmp(ptr, PATTERN2, 8) ||
			!memcmp(ptr, PATTERN3, 8) ||
			!memcmp(ptr, PATTERN4, 8) ||
			!memcmp(ptr, PATTERN5, 8)) {

			check_sigs = (unsigned long)ptr - (unsigned long)aboot + aboot_base;
			break;
		}

		if (!memcmp(ptr, PATTERN6, 8)) {

			check_sigs = (unsigned long)ptr - (unsigned long)aboot + aboot_base;

			/* Don't break, because the other LG patterns override this one */
			continue;
		}
	}

	if (!check_sigs) {
		printf("[-] Could not find signature checking function.\n");
		return 1;
	}

	printf("[+] Signature check function: %.08lx\n", check_sigs);

	boot_mmc = 0;

	/* Do a second pass for the boot_linux_from_emmc function */
	for (ptr = aboot; ptr < aboot + st.st_size - 0x1000; ptr++) {
		if (!memcmp(ptr, BOOT_PATTERN1, 8) ||
			!memcmp(ptr, BOOT_PATTERN2, 8) ||
			!memcmp(ptr, BOOT_PATTERN3, 8) ||
			!memcmp(ptr, BOOT_PATTERN4, 8)) {

			boot_mmc = (unsigned long)ptr - (unsigned long)aboot + aboot_base;
			break;
		}
    }

	if (!boot_mmc) {
		printf("[-] Could not find boot_linux_from_mmc.\n");
		return 1;
	}

	printf("[+] boot_linux_from_mmc: %.08lx\n", boot_mmc);

	return 0;
}
