// File: /workspace/github_c_makefile_star2_O2/blasphemy[P]mmc-utils/mmc_cmds.c
// Function: do_ffu
int do_ffu(int nargs, char **argv)
{
#ifndef MMC_IOC_MULTI_CMD
	fprintf(stderr, "mmc-utils has been compiled without MMC_IOC_MULTI_CMD"
			" support, needed by FFU.\n");
	exit(1);
#else
	int dev_fd, img_fd;
	int sect_done = 0, retry = 3, ret = -EINVAL;
	unsigned int sect_size;
	__u8 ext_csd[512];
	__u8 *buf;
	__u32 arg;
	off_t fw_size;
	ssize_t chunk_size;
	char *device;
	struct mmc_ioc_multi_cmd *multi_cmd;

	if (nargs != 3) {
		fprintf(stderr, "Usage: ffu <image name> </path/to/mmcblkX> \n");
		exit(1);
	}

	device = argv[2];
	dev_fd = open(device, O_RDWR);
	if (dev_fd < 0) {
		perror("device open failed");
		exit(1);
	}
	img_fd = open(argv[1], O_RDONLY);
	if (img_fd < 0) {
		perror("image open failed");
		close(dev_fd);
		exit(1);
	}

	buf = malloc(512);
	multi_cmd = calloc(1, sizeof(struct mmc_ioc_multi_cmd) +
				3 * sizeof(struct mmc_ioc_cmd));
	if (!buf || !multi_cmd) {
		perror("failed to allocate memory");
		goto out;
	}

	ret = read_extcsd(dev_fd, ext_csd);
	if (ret) {
		fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
		goto out;
	}

	if (ext_csd[EXT_CSD_REV] < EXT_CSD_REV_V5_0) {
		fprintf(stderr,
			"The FFU feature is only available on devices >= "
			"MMC 5.0, not supported in %s\n", device);
		goto out;
	}

	if (!(ext_csd[EXT_CSD_SUPPORTED_MODES] & EXT_CSD_FFU)) {
		fprintf(stderr, "FFU is not supported in %s\n", device);
		goto out;
	}

	if (ext_csd[EXT_CSD_FW_CONFIG] & EXT_CSD_UPDATE_DISABLE) {
		fprintf(stderr, "Firmware update was disabled in %s\n", device);
		goto out;
	}

	fw_size = lseek(img_fd, 0, SEEK_END);

	if (fw_size == 0) {
		fprintf(stderr, "Firmware image is empty");
		goto out;
	}

	sect_size = (ext_csd[EXT_CSD_DATA_SECTOR_SIZE] == 0) ? 512 : 4096;
	if (fw_size % sect_size) {
		fprintf(stderr, "Firmware data size (%jd) is not aligned!\n", (intmax_t)fw_size);
		goto out;
	}

	/* set CMD ARG */
	arg = ext_csd[EXT_CSD_FFU_ARG_0] |
		ext_csd[EXT_CSD_FFU_ARG_1] << 8 |
		ext_csd[EXT_CSD_FFU_ARG_2] << 16 |
		ext_csd[EXT_CSD_FFU_ARG_3] << 24;

	/* prepare multi_cmd to be sent */
	multi_cmd->num_of_cmds = 3;

	/* put device into ffu mode */
	multi_cmd->cmds[0].opcode = MMC_SWITCH;
	multi_cmd->cmds[0].arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
			(EXT_CSD_MODE_CONFIG << 16) |
			(EXT_CSD_FFU_MODE << 8) |
			EXT_CSD_CMD_SET_NORMAL;
	multi_cmd->cmds[0].flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
	multi_cmd->cmds[0].write_flag = 1;

	/* send image chunk */
	multi_cmd->cmds[1].opcode = MMC_WRITE_BLOCK;
	multi_cmd->cmds[1].blksz = sect_size;
	multi_cmd->cmds[1].blocks = 1;
	multi_cmd->cmds[1].arg = arg;
	multi_cmd->cmds[1].flags = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_ADTC;
	multi_cmd->cmds[1].write_flag = 1;
	mmc_ioc_cmd_set_data(multi_cmd->cmds[1], buf);

	/* return device into normal mode */
	multi_cmd->cmds[2].opcode = MMC_SWITCH;
	multi_cmd->cmds[2].arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
			(EXT_CSD_MODE_CONFIG << 16) |
			(EXT_CSD_NORMAL_MODE << 8) |
			EXT_CSD_CMD_SET_NORMAL;
	multi_cmd->cmds[2].flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
	multi_cmd->cmds[2].write_flag = 1;

do_retry:
	/* read firmware chunk */
	lseek(img_fd, 0, SEEK_SET);
	chunk_size = read(img_fd, buf, 512);

	while (chunk_size > 0) {
		/* send ioctl with multi-cmd */
		ret = ioctl(dev_fd, MMC_IOC_MULTI_CMD, multi_cmd);

		if (ret) {
			perror("Multi-cmd ioctl");
			/* In case multi-cmd ioctl failed before exiting from ffu mode */
			ioctl(dev_fd, MMC_IOC_CMD, &multi_cmd->cmds[2]);
			goto out;
		}

		ret = read_extcsd(dev_fd, ext_csd);
		if (ret) {
			fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
			goto out;
		}

		/* Test if we need to restart the download */
		sect_done = ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_0] |
				ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_1] << 8 |
				ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_2] << 16 |
				ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_3] << 24;
		/* By spec, host should re-start download from the first sector if sect_done is 0 */
		if (sect_done == 0) {
			if (retry > 0) {
				retry--;
				fprintf(stderr, "Programming failed. Retrying... (%d)\n", retry);
				goto do_retry;
			}
			fprintf(stderr, "Programming failed! Aborting...\n");
			goto out;
		} else {
			fprintf(stderr, "Programmed %d/%jd bytes\r", sect_done * sect_size, (intmax_t)fw_size);
		}

		/* read the next firmware chunk (if any) */
		chunk_size = read(img_fd, buf, 512);
	}

	if ((sect_done * sect_size) == fw_size) {
		fprintf(stderr, "Programmed %jd/%jd bytes\n", (intmax_t)fw_size, (intmax_t)fw_size);
		fprintf(stderr, "Programming finished with status %d \n", ret);
	}
	else {
		fprintf(stderr, "FW size and number of sectors written mismatch. Status return %d\n", ret);
		goto out;
	}

	/* check mode operation for ffu install*/
	if (!ext_csd[EXT_CSD_FFU_FEATURES]) {
		fprintf(stderr, "Please reboot to complete firmware installation on %s\n", device);
	} else {
		fprintf(stderr, "Installing firmware on %s...\n", device);
		/* Re-enter ffu mode and install the firmware */
		multi_cmd->num_of_cmds = 2;

		/* set ext_csd to install mode */
		multi_cmd->cmds[1].opcode = MMC_SWITCH;
		multi_cmd->cmds[1].blksz = 0;
		multi_cmd->cmds[1].blocks = 0;
		multi_cmd->cmds[1].arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) |
				(EXT_CSD_MODE_OPERATION_CODES << 16) |
				(EXT_CSD_FFU_INSTALL << 8) |
				EXT_CSD_CMD_SET_NORMAL;
		multi_cmd->cmds[1].flags = MMC_RSP_SPI_R1B | MMC_RSP_R1B | MMC_CMD_AC;
		multi_cmd->cmds[1].write_flag = 1;

		/* send ioctl with multi-cmd */
		ret = ioctl(dev_fd, MMC_IOC_MULTI_CMD, multi_cmd);

		if (ret) {
			perror("Multi-cmd ioctl failed setting install mode");
			/* In case multi-cmd ioctl failed before exiting from ffu mode */
			ioctl(dev_fd, MMC_IOC_CMD, &multi_cmd->cmds[2]);
			goto out;
		}

		ret = read_extcsd(dev_fd, ext_csd);
		if (ret) {
			fprintf(stderr, "Could not read EXT_CSD from %s\n", device);
			goto out;
		}

		/* return status */
		ret = ext_csd[EXT_CSD_FFU_STATUS];
		if (ret) {
			fprintf(stderr, "%s: error %d during FFU install:\n", device, ret);
			goto out;
		} else {
			fprintf(stderr, "FFU finished successfully\n");
		}
	}

out:
	free(buf);
	free(multi_cmd);
	close(img_fd);
	close(dev_fd);
	return ret;
#endif
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x228, %rsp            # imm = 0x228
cmpl $0x3, %edi
jne 0x7d34
movq %rsi, %r14
movq 0x10(%rsi), %r12
pushq $0x2
popq %rsi
movq %r12, %rdi
xorl %eax, %eax
callq 0x31e0
testl %eax, %eax
js 0x7d58
movl %eax, %ebx
movq 0x8(%r14), %rdi
xorl %esi, %esi
xorl %eax, %eax
callq 0x31e0
testl %eax, %eax
js 0x7d66
movl %eax, %ebp
movl $0x200, %edi            # imm = 0x200
callq 0x31a0
movq %rax, %r14
pushq $0x1
popq %rdi
movl $0xe0, %esi
callq 0x3140
movq %rax, %r15
testq %r14, %r14
je 0x79d4
testq %r15, %r15
je 0x79d4
leaq 0x20(%rsp), %rsi
movl %ebx, %edi
callq 0x39b8
testl %eax, %eax
je 0x7936
movl %eax, %r13d
movq 0x106c9(%rip), %rax     # 0x17ff0
movq (%rax), %rdi
leaq 0x6dd4(%rip), %rdx      # 0xe705
jmp 0x79fa
cmpb $0x6, 0xe0(%rsp)
ja 0x7956
movq 0x106a9(%rip), %rax     # 0x17ff0
movq (%rax), %rdi
leaq 0x9b3d(%rip), %rdx      # 0x1148e
jmp 0x79f7
testb $0x1, 0x20d(%rsp)
jne 0x7976
movq 0x10689(%rip), %rax     # 0x17ff0
movq (%rax), %rdi
leaq 0x9b6b(%rip), %rdx      # 0x114dc
jmp 0x79f7
testb $0x1, 0xc9(%rsp)
jne 0x79e6
xorl %r13d, %r13d
pushq $0x2
popq %rdx
movl %ebp, %edi
xorl %esi, %esi
callq 0x3240
testq %rax, %rax
je 0x7a3a
movb 0x5d(%rsp), %cl
testb %cl, %cl
movl $0x200, %edx            # imm = 0x200
movl $0x1000, %edi           # imm = 0x1000
cmovel %edx, %edi
leaq -0x1(%rdi), %rdx
testq %rax, %rdx
je 0x7a57
movq 0x10631(%rip), %rcx     # 0x17ff0
movq (%rcx), %rdi
leaq 0x9b6b(%rip), %rdx      # 0x11534
xorl %r13d, %r13d
pushq $0x1
popq %rsi
movq %rax, %rcx
jmp 0x7a00
leaq 0x9a99(%rip), %rdi      # 0x11474
callq 0x31f0
pushq $-0x16
popq %r13
jmp 0x7a07
movq 0x10603(%rip), %rax     # 0x17ff0
movq (%rax), %rdi
leaq 0x9b01(%rip), %rdx      # 0x114f8
xorl %r13d, %r13d
pushq $0x1
popq %rsi
movq %r12, %rcx
xorl %eax, %eax
callq 0x3250
movq %r14, %rdi
callq 0x3030
movq %r15, %rdi
callq 0x3030
movl %ebp, %edi
callq 0x3100
movl %ebx, %edi
callq 0x3100
movl %r13d, %eax
addq $0x228, %rsp            # imm = 0x228
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movq 0x105af(%rip), %rax     # 0x17ff0
movq (%rax), %rdi
leaq 0x9ad1(%rip), %rdx      # 0x1151c
pushq $0x1
popq %rsi
xorl %eax, %eax
callq 0x3250
jmp 0x7a07
movq %rax, 0x18(%rsp)
xorl %edx, %edx
testb %cl, %cl
setne %dl
leaq 0x20(%rsp), %rax
movl 0x1e7(%rax), %ecx
movq $0x3, (%r15)
movabsq $0x31e010100000006, %rsi # imm = 0x31E010100000006
movq %rsi, 0x10(%r15)
movl $0x49d, %esi            # imm = 0x49D
movl %esi, 0x28(%r15)
pushq $0x1
popq %r8
movl %r8d, 0x8(%r15)
movl $0x18, 0x58(%r15)
movl %edi, 0x74(%r15)
movl %r8d, 0x78(%r15)
movl %ecx, 0x5c(%r15)
movl $0xb5, 0x70(%r15)
movl %r8d, 0x50(%r15)
movq %r14, 0x90(%r15)
movq %r15, %rax
addq $0x98, %rax
movq %rax, 0x10(%rsp)
movabsq $0x31e000100000006, %rax # imm = 0x31E000100000006
movq %rax, 0xa0(%r15)
movl %esi, 0xb8(%r15)
movl %r8d, 0x98(%r15)
leal 0x9(%rdx,%rdx,2), %eax
movl %eax, 0xc(%rsp)
pushq $0x3
popq %rax
movl %eax, 0x8(%rsp)
movl %ebp, %edi
xorl %esi, %esi
xorl %edx, %edx
callq 0x3240
xorl %r13d, %r13d
movl $0x200, %edx            # imm = 0x200
movl %ebp, %edi
movq %r14, %rsi
callq 0x3110
testq %rax, %rax
jle 0x7bae
movl $0xc008b301, %esi       # imm = 0xC008B301
movl %ebx, %edi
movq %r15, %rdx
xorl %eax, %eax
callq 0x30f0
testl %eax, %eax
jne 0x7cdc
movl %ebx, %edi
leaq 0x20(%rsp), %rsi
callq 0x39b8
testl %eax, %eax
jne 0x791d
movl 0x14e(%rsp), %r13d
testl %r13d, %r13d
movq 0x10494(%rip), %rax     # 0x17ff0
je 0x7b84
movq (%rax), %rdi
movl %r13d, %eax
movl 0xc(%rsp), %ecx
shll %cl, %eax
pushq $0x1
popq %rsi
leaq 0x9a41(%rip), %rdx      # 0x115b5
movl %eax, %ecx
movq 0x18(%rsp), %r8
xorl %eax, %eax
callq 0x3250
jmp 0x7b05
movl 0x8(%rsp), %ecx
subl $0x1, %ecx
jb 0x7cf4
movq (%rax), %rdi
pushq $0x1
popq %rsi
leaq 0x99d0(%rip), %rdx      # 0x1156e
movl %ecx, 0x8(%rsp)
xorl %eax, %eax
callq 0x3250
jmp 0x7af7
movl 0xc(%rsp), %ecx
shll %cl, %r13d
movq 0x10434(%rip), %rax     # 0x17ff0
movq (%rax), %rdi
movq 0x18(%rsp), %rcx
cmpq %r13, %rcx
jne 0x7cb5
leaq 0x99fa(%rip), %rdx      # 0x115ce
xorl %r13d, %r13d
pushq $0x1
popq %rsi
movq %rcx, %r8
xorl %eax, %eax
callq 0x3250
movq 0x10405(%rip), %rax     # 0x17ff0
movq (%rax), %rdi
leaq 0x99f3(%rip), %rdx      # 0x115e8
pushq $0x1
popq %rsi
xorl %ecx, %ecx
xorl %eax, %eax
callq 0x3250
cmpb $0x0, 0x20c(%rsp)
movq 0x103e0(%rip), %rax     # 0x17ff0
movq (%rax), %rdi
je 0x7cd0
leaq 0x9a67(%rip), %rdx      # 0x11687
pushq $0x1
popq %rsi
movq %r12, %rcx
xorl %eax, %eax
callq 0x3250
movq $0x2, (%r15)
movabsq $0x31d010100000006, %rax # imm = 0x31D010100000006
movq %rax, 0x58(%r15)
andl $0x0, 0x78(%r15)
movq $0x49d, 0x70(%r15)      # imm = 0x49D
movl $0x1, 0x50(%r15)
movl $0xc008b301, %esi       # imm = 0xC008B301
movl %ebx, %edi
movq %r15, %rdx
xorl %eax, %eax
callq 0x30f0
testl %eax, %eax
jne 0x7d06
leaq 0x20(%rsp), %rsi
movl %ebx, %edi
callq 0x39b8
testl %eax, %eax
jne 0x791d
movzbl 0x3a(%rsp), %r13d
testl %r13d, %r13d
movq 0x1035c(%rip), %rax     # 0x17ff0
je 0x7ce8
movq (%rax), %rdi
leaq 0x9a31(%rip), %rdx      # 0x116d1
pushq $0x1
popq %rsi
movq %r12, %rcx
movl %r13d, %r8d
xorl %eax, %eax
callq 0x3250
jmp 0x7a07
leaq 0x9952(%rip), %rdx      # 0x1160e
xorl %r13d, %r13d
pushq $0x1
popq %rsi
xorl %ecx, %ecx
xorl %eax, %eax
callq 0x3250
jmp 0x7a07
leaq 0x9979(%rip), %rdx      # 0x11650
jmp 0x79fa
movl %eax, %r13d
leaq 0x9878(%rip), %rdi      # 0x1155e
jmp 0x7d10
movq (%rax), %rdi
leaq 0x9a01(%rip), %rdx      # 0x116f3
jmp 0x7cfe
movq (%rax), %rdi
leaq 0x9896(%rip), %rdx      # 0x11594
xorl %r13d, %r13d
jmp 0x7a4b
movl %eax, %r13d
leaq 0x9995(%rip), %rdi      # 0x116a5
callq 0x31f0
movl $0xc008b301, %esi       # imm = 0xC008B301
addq $0x3fffff, %rsi         # imm = 0x3FFFFF
movl %ebx, %edi
movq 0x10(%rsp), %rdx
xorl %eax, %eax
callq 0x30f0
jmp 0x7a07
movq 0x102b5(%rip), %rax     # 0x17ff0
movq (%rax), %rdi
leaq 0x96dd(%rip), %rdx      # 0x11422
pushq $0x1
popq %rbx
movl %ebx, %esi
xorl %eax, %eax
callq 0x3250
movl %ebx, %edi
callq 0x3230
leaq 0x96f0(%rip), %rdi      # 0x1144f
callq 0x31f0
jmp 0x7d79
leaq 0x96f5(%rip), %rdi      # 0x11462
callq 0x31f0
movl %ebx, %edi
callq 0x3100
pushq $0x1
popq %rdi
callq 0x3230