xref: /freebsd/sys/contrib/xz-embedded/linux/lib/xz/xz_dec_bcj.c (revision cd3a777bca91669fc4711d1eff66c40f3f62a223)
163dab8eeSAdrian Chadd /*
263dab8eeSAdrian Chadd  * Branch/Call/Jump (BCJ) filter decoders
363dab8eeSAdrian Chadd  *
463dab8eeSAdrian Chadd  * Authors: Lasse Collin <lasse.collin@tukaani.org>
5*cd3a777bSXin LI  *          Igor Pavlov <https://7-zip.org/>
663dab8eeSAdrian Chadd  *
763dab8eeSAdrian Chadd  * This file has been put into the public domain.
863dab8eeSAdrian Chadd  * You can do whatever you want with this file.
963dab8eeSAdrian Chadd  */
1063dab8eeSAdrian Chadd 
1163dab8eeSAdrian Chadd #include "xz_private.h"
1263dab8eeSAdrian Chadd 
1363dab8eeSAdrian Chadd /*
1463dab8eeSAdrian Chadd  * The rest of the file is inside this ifdef. It makes things a little more
1563dab8eeSAdrian Chadd  * convenient when building without support for any BCJ filters.
1663dab8eeSAdrian Chadd  */
1763dab8eeSAdrian Chadd #ifdef XZ_DEC_BCJ
1863dab8eeSAdrian Chadd 
1963dab8eeSAdrian Chadd struct xz_dec_bcj {
2063dab8eeSAdrian Chadd 	/* Type of the BCJ filter being used */
2163dab8eeSAdrian Chadd 	enum {
2263dab8eeSAdrian Chadd 		BCJ_X86 = 4,        /* x86 or x86-64 */
2363dab8eeSAdrian Chadd 		BCJ_POWERPC = 5,    /* Big endian only */
2463dab8eeSAdrian Chadd 		BCJ_IA64 = 6,       /* Big or little endian */
2563dab8eeSAdrian Chadd 		BCJ_ARM = 7,        /* Little endian only */
2663dab8eeSAdrian Chadd 		BCJ_ARMTHUMB = 8,   /* Little endian only */
2763dab8eeSAdrian Chadd 		BCJ_SPARC = 9       /* Big or little endian */
2863dab8eeSAdrian Chadd 	} type;
2963dab8eeSAdrian Chadd 
3063dab8eeSAdrian Chadd 	/*
3163dab8eeSAdrian Chadd 	 * Return value of the next filter in the chain. We need to preserve
3263dab8eeSAdrian Chadd 	 * this information across calls, because we must not call the next
3363dab8eeSAdrian Chadd 	 * filter anymore once it has returned XZ_STREAM_END.
3463dab8eeSAdrian Chadd 	 */
3563dab8eeSAdrian Chadd 	enum xz_ret ret;
3663dab8eeSAdrian Chadd 
3763dab8eeSAdrian Chadd 	/* True if we are operating in single-call mode. */
3863dab8eeSAdrian Chadd 	bool single_call;
3963dab8eeSAdrian Chadd 
4063dab8eeSAdrian Chadd 	/*
4163dab8eeSAdrian Chadd 	 * Absolute position relative to the beginning of the uncompressed
4263dab8eeSAdrian Chadd 	 * data (in a single .xz Block). We care only about the lowest 32
4363dab8eeSAdrian Chadd 	 * bits so this doesn't need to be uint64_t even with big files.
4463dab8eeSAdrian Chadd 	 */
4563dab8eeSAdrian Chadd 	uint32_t pos;
4663dab8eeSAdrian Chadd 
4763dab8eeSAdrian Chadd 	/* x86 filter state */
4863dab8eeSAdrian Chadd 	uint32_t x86_prev_mask;
4963dab8eeSAdrian Chadd 
5063dab8eeSAdrian Chadd 	/* Temporary space to hold the variables from struct xz_buf */
5163dab8eeSAdrian Chadd 	uint8_t *out;
5263dab8eeSAdrian Chadd 	size_t out_pos;
5363dab8eeSAdrian Chadd 	size_t out_size;
5463dab8eeSAdrian Chadd 
5563dab8eeSAdrian Chadd 	struct {
5663dab8eeSAdrian Chadd 		/* Amount of already filtered data in the beginning of buf */
5763dab8eeSAdrian Chadd 		size_t filtered;
5863dab8eeSAdrian Chadd 
5963dab8eeSAdrian Chadd 		/* Total amount of data currently stored in buf  */
6063dab8eeSAdrian Chadd 		size_t size;
6163dab8eeSAdrian Chadd 
6263dab8eeSAdrian Chadd 		/*
6363dab8eeSAdrian Chadd 		 * Buffer to hold a mix of filtered and unfiltered data. This
6463dab8eeSAdrian Chadd 		 * needs to be big enough to hold Alignment + 2 * Look-ahead:
6563dab8eeSAdrian Chadd 		 *
6663dab8eeSAdrian Chadd 		 * Type         Alignment   Look-ahead
6763dab8eeSAdrian Chadd 		 * x86              1           4
6863dab8eeSAdrian Chadd 		 * PowerPC          4           0
6963dab8eeSAdrian Chadd 		 * IA-64           16           0
7063dab8eeSAdrian Chadd 		 * ARM              4           0
7163dab8eeSAdrian Chadd 		 * ARM-Thumb        2           2
7263dab8eeSAdrian Chadd 		 * SPARC            4           0
7363dab8eeSAdrian Chadd 		 */
7463dab8eeSAdrian Chadd 		uint8_t buf[16];
7563dab8eeSAdrian Chadd 	} temp;
7663dab8eeSAdrian Chadd };
7763dab8eeSAdrian Chadd 
7863dab8eeSAdrian Chadd #ifdef XZ_DEC_X86
7963dab8eeSAdrian Chadd /*
8063dab8eeSAdrian Chadd  * This is used to test the most significant byte of a memory address
8163dab8eeSAdrian Chadd  * in an x86 instruction.
8263dab8eeSAdrian Chadd  */
bcj_x86_test_msbyte(uint8_t b)8363dab8eeSAdrian Chadd static inline int bcj_x86_test_msbyte(uint8_t b)
8463dab8eeSAdrian Chadd {
8563dab8eeSAdrian Chadd 	return b == 0x00 || b == 0xFF;
8663dab8eeSAdrian Chadd }
8763dab8eeSAdrian Chadd 
bcj_x86(struct xz_dec_bcj * s,uint8_t * buf,size_t size)8863dab8eeSAdrian Chadd static size_t bcj_x86(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
8963dab8eeSAdrian Chadd {
9063dab8eeSAdrian Chadd 	static const bool mask_to_allowed_status[8]
9163dab8eeSAdrian Chadd 		= { true, true, true, false, true, false, false, false };
9263dab8eeSAdrian Chadd 
9363dab8eeSAdrian Chadd 	static const uint8_t mask_to_bit_num[8] = { 0, 1, 2, 2, 3, 3, 3, 3 };
9463dab8eeSAdrian Chadd 
9563dab8eeSAdrian Chadd 	size_t i;
9663dab8eeSAdrian Chadd 	size_t prev_pos = (size_t)-1;
9763dab8eeSAdrian Chadd 	uint32_t prev_mask = s->x86_prev_mask;
9863dab8eeSAdrian Chadd 	uint32_t src;
9963dab8eeSAdrian Chadd 	uint32_t dest;
10063dab8eeSAdrian Chadd 	uint32_t j;
10163dab8eeSAdrian Chadd 	uint8_t b;
10263dab8eeSAdrian Chadd 
10363dab8eeSAdrian Chadd 	if (size <= 4)
10463dab8eeSAdrian Chadd 		return 0;
10563dab8eeSAdrian Chadd 
10663dab8eeSAdrian Chadd 	size -= 4;
10763dab8eeSAdrian Chadd 	for (i = 0; i < size; ++i) {
10863dab8eeSAdrian Chadd 		if ((buf[i] & 0xFE) != 0xE8)
10963dab8eeSAdrian Chadd 			continue;
11063dab8eeSAdrian Chadd 
11163dab8eeSAdrian Chadd 		prev_pos = i - prev_pos;
11263dab8eeSAdrian Chadd 		if (prev_pos > 3) {
11363dab8eeSAdrian Chadd 			prev_mask = 0;
11463dab8eeSAdrian Chadd 		} else {
11563dab8eeSAdrian Chadd 			prev_mask = (prev_mask << (prev_pos - 1)) & 7;
11663dab8eeSAdrian Chadd 			if (prev_mask != 0) {
11763dab8eeSAdrian Chadd 				b = buf[i + 4 - mask_to_bit_num[prev_mask]];
11863dab8eeSAdrian Chadd 				if (!mask_to_allowed_status[prev_mask]
11963dab8eeSAdrian Chadd 						|| bcj_x86_test_msbyte(b)) {
12063dab8eeSAdrian Chadd 					prev_pos = i;
12163dab8eeSAdrian Chadd 					prev_mask = (prev_mask << 1) | 1;
12263dab8eeSAdrian Chadd 					continue;
12363dab8eeSAdrian Chadd 				}
12463dab8eeSAdrian Chadd 			}
12563dab8eeSAdrian Chadd 		}
12663dab8eeSAdrian Chadd 
12763dab8eeSAdrian Chadd 		prev_pos = i;
12863dab8eeSAdrian Chadd 
12963dab8eeSAdrian Chadd 		if (bcj_x86_test_msbyte(buf[i + 4])) {
13063dab8eeSAdrian Chadd 			src = get_unaligned_le32(buf + i + 1);
13163dab8eeSAdrian Chadd 			while (true) {
13263dab8eeSAdrian Chadd 				dest = src - (s->pos + (uint32_t)i + 5);
13363dab8eeSAdrian Chadd 				if (prev_mask == 0)
13463dab8eeSAdrian Chadd 					break;
13563dab8eeSAdrian Chadd 
13663dab8eeSAdrian Chadd 				j = mask_to_bit_num[prev_mask] * 8;
13763dab8eeSAdrian Chadd 				b = (uint8_t)(dest >> (24 - j));
13863dab8eeSAdrian Chadd 				if (!bcj_x86_test_msbyte(b))
13963dab8eeSAdrian Chadd 					break;
14063dab8eeSAdrian Chadd 
14163dab8eeSAdrian Chadd 				src = dest ^ (((uint32_t)1 << (32 - j)) - 1);
14263dab8eeSAdrian Chadd 			}
14363dab8eeSAdrian Chadd 
14463dab8eeSAdrian Chadd 			dest &= 0x01FFFFFF;
14563dab8eeSAdrian Chadd 			dest |= (uint32_t)0 - (dest & 0x01000000);
14663dab8eeSAdrian Chadd 			put_unaligned_le32(dest, buf + i + 1);
14763dab8eeSAdrian Chadd 			i += 4;
14863dab8eeSAdrian Chadd 		} else {
14963dab8eeSAdrian Chadd 			prev_mask = (prev_mask << 1) | 1;
15063dab8eeSAdrian Chadd 		}
15163dab8eeSAdrian Chadd 	}
15263dab8eeSAdrian Chadd 
15363dab8eeSAdrian Chadd 	prev_pos = i - prev_pos;
15463dab8eeSAdrian Chadd 	s->x86_prev_mask = prev_pos > 3 ? 0 : prev_mask << (prev_pos - 1);
15563dab8eeSAdrian Chadd 	return i;
15663dab8eeSAdrian Chadd }
15763dab8eeSAdrian Chadd #endif
15863dab8eeSAdrian Chadd 
15963dab8eeSAdrian Chadd #ifdef XZ_DEC_POWERPC
bcj_powerpc(struct xz_dec_bcj * s,uint8_t * buf,size_t size)16063dab8eeSAdrian Chadd static size_t bcj_powerpc(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
16163dab8eeSAdrian Chadd {
16263dab8eeSAdrian Chadd 	size_t i;
16363dab8eeSAdrian Chadd 	uint32_t instr;
16463dab8eeSAdrian Chadd 
16563dab8eeSAdrian Chadd 	for (i = 0; i + 4 <= size; i += 4) {
16663dab8eeSAdrian Chadd 		instr = get_unaligned_be32(buf + i);
16763dab8eeSAdrian Chadd 		if ((instr & 0xFC000003) == 0x48000001) {
16863dab8eeSAdrian Chadd 			instr &= 0x03FFFFFC;
16963dab8eeSAdrian Chadd 			instr -= s->pos + (uint32_t)i;
17063dab8eeSAdrian Chadd 			instr &= 0x03FFFFFC;
17163dab8eeSAdrian Chadd 			instr |= 0x48000001;
17263dab8eeSAdrian Chadd 			put_unaligned_be32(instr, buf + i);
17363dab8eeSAdrian Chadd 		}
17463dab8eeSAdrian Chadd 	}
17563dab8eeSAdrian Chadd 
17663dab8eeSAdrian Chadd 	return i;
17763dab8eeSAdrian Chadd }
17863dab8eeSAdrian Chadd #endif
17963dab8eeSAdrian Chadd 
18063dab8eeSAdrian Chadd #ifdef XZ_DEC_IA64
bcj_ia64(struct xz_dec_bcj * s,uint8_t * buf,size_t size)18163dab8eeSAdrian Chadd static size_t bcj_ia64(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
18263dab8eeSAdrian Chadd {
18363dab8eeSAdrian Chadd 	static const uint8_t branch_table[32] = {
18463dab8eeSAdrian Chadd 		0, 0, 0, 0, 0, 0, 0, 0,
18563dab8eeSAdrian Chadd 		0, 0, 0, 0, 0, 0, 0, 0,
18663dab8eeSAdrian Chadd 		4, 4, 6, 6, 0, 0, 7, 7,
18763dab8eeSAdrian Chadd 		4, 4, 0, 0, 4, 4, 0, 0
18863dab8eeSAdrian Chadd 	};
18963dab8eeSAdrian Chadd 
19063dab8eeSAdrian Chadd 	/*
19163dab8eeSAdrian Chadd 	 * The local variables take a little bit stack space, but it's less
19263dab8eeSAdrian Chadd 	 * than what LZMA2 decoder takes, so it doesn't make sense to reduce
19363dab8eeSAdrian Chadd 	 * stack usage here without doing that for the LZMA2 decoder too.
19463dab8eeSAdrian Chadd 	 */
19563dab8eeSAdrian Chadd 
19663dab8eeSAdrian Chadd 	/* Loop counters */
19763dab8eeSAdrian Chadd 	size_t i;
19863dab8eeSAdrian Chadd 	size_t j;
19963dab8eeSAdrian Chadd 
20063dab8eeSAdrian Chadd 	/* Instruction slot (0, 1, or 2) in the 128-bit instruction word */
20163dab8eeSAdrian Chadd 	uint32_t slot;
20263dab8eeSAdrian Chadd 
20363dab8eeSAdrian Chadd 	/* Bitwise offset of the instruction indicated by slot */
20463dab8eeSAdrian Chadd 	uint32_t bit_pos;
20563dab8eeSAdrian Chadd 
20663dab8eeSAdrian Chadd 	/* bit_pos split into byte and bit parts */
20763dab8eeSAdrian Chadd 	uint32_t byte_pos;
20863dab8eeSAdrian Chadd 	uint32_t bit_res;
20963dab8eeSAdrian Chadd 
21063dab8eeSAdrian Chadd 	/* Address part of an instruction */
21163dab8eeSAdrian Chadd 	uint32_t addr;
21263dab8eeSAdrian Chadd 
21363dab8eeSAdrian Chadd 	/* Mask used to detect which instructions to convert */
21463dab8eeSAdrian Chadd 	uint32_t mask;
21563dab8eeSAdrian Chadd 
21663dab8eeSAdrian Chadd 	/* 41-bit instruction stored somewhere in the lowest 48 bits */
21763dab8eeSAdrian Chadd 	uint64_t instr;
21863dab8eeSAdrian Chadd 
21963dab8eeSAdrian Chadd 	/* Instruction normalized with bit_res for easier manipulation */
22063dab8eeSAdrian Chadd 	uint64_t norm;
22163dab8eeSAdrian Chadd 
22263dab8eeSAdrian Chadd 	for (i = 0; i + 16 <= size; i += 16) {
22363dab8eeSAdrian Chadd 		mask = branch_table[buf[i] & 0x1F];
22463dab8eeSAdrian Chadd 		for (slot = 0, bit_pos = 5; slot < 3; ++slot, bit_pos += 41) {
22563dab8eeSAdrian Chadd 			if (((mask >> slot) & 1) == 0)
22663dab8eeSAdrian Chadd 				continue;
22763dab8eeSAdrian Chadd 
22863dab8eeSAdrian Chadd 			byte_pos = bit_pos >> 3;
22963dab8eeSAdrian Chadd 			bit_res = bit_pos & 7;
23063dab8eeSAdrian Chadd 			instr = 0;
23163dab8eeSAdrian Chadd 			for (j = 0; j < 6; ++j)
23263dab8eeSAdrian Chadd 				instr |= (uint64_t)(buf[i + j + byte_pos])
23363dab8eeSAdrian Chadd 						<< (8 * j);
23463dab8eeSAdrian Chadd 
23563dab8eeSAdrian Chadd 			norm = instr >> bit_res;
23663dab8eeSAdrian Chadd 
23763dab8eeSAdrian Chadd 			if (((norm >> 37) & 0x0F) == 0x05
23863dab8eeSAdrian Chadd 					&& ((norm >> 9) & 0x07) == 0) {
23963dab8eeSAdrian Chadd 				addr = (norm >> 13) & 0x0FFFFF;
24063dab8eeSAdrian Chadd 				addr |= ((uint32_t)(norm >> 36) & 1) << 20;
24163dab8eeSAdrian Chadd 				addr <<= 4;
24263dab8eeSAdrian Chadd 				addr -= s->pos + (uint32_t)i;
24363dab8eeSAdrian Chadd 				addr >>= 4;
24463dab8eeSAdrian Chadd 
24563dab8eeSAdrian Chadd 				norm &= ~((uint64_t)0x8FFFFF << 13);
24663dab8eeSAdrian Chadd 				norm |= (uint64_t)(addr & 0x0FFFFF) << 13;
24763dab8eeSAdrian Chadd 				norm |= (uint64_t)(addr & 0x100000)
24863dab8eeSAdrian Chadd 						<< (36 - 20);
24963dab8eeSAdrian Chadd 
25063dab8eeSAdrian Chadd 				instr &= (1 << bit_res) - 1;
25163dab8eeSAdrian Chadd 				instr |= norm << bit_res;
25263dab8eeSAdrian Chadd 
25363dab8eeSAdrian Chadd 				for (j = 0; j < 6; j++)
25463dab8eeSAdrian Chadd 					buf[i + j + byte_pos]
25563dab8eeSAdrian Chadd 						= (uint8_t)(instr >> (8 * j));
25663dab8eeSAdrian Chadd 			}
25763dab8eeSAdrian Chadd 		}
25863dab8eeSAdrian Chadd 	}
25963dab8eeSAdrian Chadd 
26063dab8eeSAdrian Chadd 	return i;
26163dab8eeSAdrian Chadd }
26263dab8eeSAdrian Chadd #endif
26363dab8eeSAdrian Chadd 
26463dab8eeSAdrian Chadd #ifdef XZ_DEC_ARM
bcj_arm(struct xz_dec_bcj * s,uint8_t * buf,size_t size)26563dab8eeSAdrian Chadd static size_t bcj_arm(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
26663dab8eeSAdrian Chadd {
26763dab8eeSAdrian Chadd 	size_t i;
26863dab8eeSAdrian Chadd 	uint32_t addr;
26963dab8eeSAdrian Chadd 
27063dab8eeSAdrian Chadd 	for (i = 0; i + 4 <= size; i += 4) {
27163dab8eeSAdrian Chadd 		if (buf[i + 3] == 0xEB) {
27263dab8eeSAdrian Chadd 			addr = (uint32_t)buf[i] | ((uint32_t)buf[i + 1] << 8)
27363dab8eeSAdrian Chadd 					| ((uint32_t)buf[i + 2] << 16);
27463dab8eeSAdrian Chadd 			addr <<= 2;
27563dab8eeSAdrian Chadd 			addr -= s->pos + (uint32_t)i + 8;
27663dab8eeSAdrian Chadd 			addr >>= 2;
27763dab8eeSAdrian Chadd 			buf[i] = (uint8_t)addr;
27863dab8eeSAdrian Chadd 			buf[i + 1] = (uint8_t)(addr >> 8);
27963dab8eeSAdrian Chadd 			buf[i + 2] = (uint8_t)(addr >> 16);
28063dab8eeSAdrian Chadd 		}
28163dab8eeSAdrian Chadd 	}
28263dab8eeSAdrian Chadd 
28363dab8eeSAdrian Chadd 	return i;
28463dab8eeSAdrian Chadd }
28563dab8eeSAdrian Chadd #endif
28663dab8eeSAdrian Chadd 
28763dab8eeSAdrian Chadd #ifdef XZ_DEC_ARMTHUMB
bcj_armthumb(struct xz_dec_bcj * s,uint8_t * buf,size_t size)28863dab8eeSAdrian Chadd static size_t bcj_armthumb(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
28963dab8eeSAdrian Chadd {
29063dab8eeSAdrian Chadd 	size_t i;
29163dab8eeSAdrian Chadd 	uint32_t addr;
29263dab8eeSAdrian Chadd 
29363dab8eeSAdrian Chadd 	for (i = 0; i + 4 <= size; i += 2) {
29463dab8eeSAdrian Chadd 		if ((buf[i + 1] & 0xF8) == 0xF0
29563dab8eeSAdrian Chadd 				&& (buf[i + 3] & 0xF8) == 0xF8) {
29663dab8eeSAdrian Chadd 			addr = (((uint32_t)buf[i + 1] & 0x07) << 19)
29763dab8eeSAdrian Chadd 					| ((uint32_t)buf[i] << 11)
29863dab8eeSAdrian Chadd 					| (((uint32_t)buf[i + 3] & 0x07) << 8)
29963dab8eeSAdrian Chadd 					| (uint32_t)buf[i + 2];
30063dab8eeSAdrian Chadd 			addr <<= 1;
30163dab8eeSAdrian Chadd 			addr -= s->pos + (uint32_t)i + 4;
30263dab8eeSAdrian Chadd 			addr >>= 1;
30363dab8eeSAdrian Chadd 			buf[i + 1] = (uint8_t)(0xF0 | ((addr >> 19) & 0x07));
30463dab8eeSAdrian Chadd 			buf[i] = (uint8_t)(addr >> 11);
30563dab8eeSAdrian Chadd 			buf[i + 3] = (uint8_t)(0xF8 | ((addr >> 8) & 0x07));
30663dab8eeSAdrian Chadd 			buf[i + 2] = (uint8_t)addr;
30763dab8eeSAdrian Chadd 			i += 2;
30863dab8eeSAdrian Chadd 		}
30963dab8eeSAdrian Chadd 	}
31063dab8eeSAdrian Chadd 
31163dab8eeSAdrian Chadd 	return i;
31263dab8eeSAdrian Chadd }
31363dab8eeSAdrian Chadd #endif
31463dab8eeSAdrian Chadd 
31563dab8eeSAdrian Chadd #ifdef XZ_DEC_SPARC
bcj_sparc(struct xz_dec_bcj * s,uint8_t * buf,size_t size)31663dab8eeSAdrian Chadd static size_t bcj_sparc(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
31763dab8eeSAdrian Chadd {
31863dab8eeSAdrian Chadd 	size_t i;
31963dab8eeSAdrian Chadd 	uint32_t instr;
32063dab8eeSAdrian Chadd 
32163dab8eeSAdrian Chadd 	for (i = 0; i + 4 <= size; i += 4) {
32263dab8eeSAdrian Chadd 		instr = get_unaligned_be32(buf + i);
32363dab8eeSAdrian Chadd 		if ((instr >> 22) == 0x100 || (instr >> 22) == 0x1FF) {
32463dab8eeSAdrian Chadd 			instr <<= 2;
32563dab8eeSAdrian Chadd 			instr -= s->pos + (uint32_t)i;
32663dab8eeSAdrian Chadd 			instr >>= 2;
32763dab8eeSAdrian Chadd 			instr = ((uint32_t)0x40000000 - (instr & 0x400000))
32863dab8eeSAdrian Chadd 					| 0x40000000 | (instr & 0x3FFFFF);
32963dab8eeSAdrian Chadd 			put_unaligned_be32(instr, buf + i);
33063dab8eeSAdrian Chadd 		}
33163dab8eeSAdrian Chadd 	}
33263dab8eeSAdrian Chadd 
33363dab8eeSAdrian Chadd 	return i;
33463dab8eeSAdrian Chadd }
33563dab8eeSAdrian Chadd #endif
33663dab8eeSAdrian Chadd 
33763dab8eeSAdrian Chadd /*
33863dab8eeSAdrian Chadd  * Apply the selected BCJ filter. Update *pos and s->pos to match the amount
33963dab8eeSAdrian Chadd  * of data that got filtered.
34063dab8eeSAdrian Chadd  *
34163dab8eeSAdrian Chadd  * NOTE: This is implemented as a switch statement to avoid using function
34263dab8eeSAdrian Chadd  * pointers, which could be problematic in the kernel boot code, which must
34363dab8eeSAdrian Chadd  * avoid pointers to static data (at least on x86).
34463dab8eeSAdrian Chadd  */
bcj_apply(struct xz_dec_bcj * s,uint8_t * buf,size_t * pos,size_t size)34563dab8eeSAdrian Chadd static void bcj_apply(struct xz_dec_bcj *s,
34663dab8eeSAdrian Chadd 		      uint8_t *buf, size_t *pos, size_t size)
34763dab8eeSAdrian Chadd {
34863dab8eeSAdrian Chadd 	size_t filtered;
34963dab8eeSAdrian Chadd 
35063dab8eeSAdrian Chadd 	buf += *pos;
35163dab8eeSAdrian Chadd 	size -= *pos;
35263dab8eeSAdrian Chadd 
35363dab8eeSAdrian Chadd 	switch (s->type) {
35463dab8eeSAdrian Chadd #ifdef XZ_DEC_X86
35563dab8eeSAdrian Chadd 	case BCJ_X86:
35663dab8eeSAdrian Chadd 		filtered = bcj_x86(s, buf, size);
35763dab8eeSAdrian Chadd 		break;
35863dab8eeSAdrian Chadd #endif
35963dab8eeSAdrian Chadd #ifdef XZ_DEC_POWERPC
36063dab8eeSAdrian Chadd 	case BCJ_POWERPC:
36163dab8eeSAdrian Chadd 		filtered = bcj_powerpc(s, buf, size);
36263dab8eeSAdrian Chadd 		break;
36363dab8eeSAdrian Chadd #endif
36463dab8eeSAdrian Chadd #ifdef XZ_DEC_IA64
36563dab8eeSAdrian Chadd 	case BCJ_IA64:
36663dab8eeSAdrian Chadd 		filtered = bcj_ia64(s, buf, size);
36763dab8eeSAdrian Chadd 		break;
36863dab8eeSAdrian Chadd #endif
36963dab8eeSAdrian Chadd #ifdef XZ_DEC_ARM
37063dab8eeSAdrian Chadd 	case BCJ_ARM:
37163dab8eeSAdrian Chadd 		filtered = bcj_arm(s, buf, size);
37263dab8eeSAdrian Chadd 		break;
37363dab8eeSAdrian Chadd #endif
37463dab8eeSAdrian Chadd #ifdef XZ_DEC_ARMTHUMB
37563dab8eeSAdrian Chadd 	case BCJ_ARMTHUMB:
37663dab8eeSAdrian Chadd 		filtered = bcj_armthumb(s, buf, size);
37763dab8eeSAdrian Chadd 		break;
37863dab8eeSAdrian Chadd #endif
37963dab8eeSAdrian Chadd #ifdef XZ_DEC_SPARC
38063dab8eeSAdrian Chadd 	case BCJ_SPARC:
38163dab8eeSAdrian Chadd 		filtered = bcj_sparc(s, buf, size);
38263dab8eeSAdrian Chadd 		break;
38363dab8eeSAdrian Chadd #endif
38463dab8eeSAdrian Chadd 	default:
38563dab8eeSAdrian Chadd 		/* Never reached but silence compiler warnings. */
38663dab8eeSAdrian Chadd 		filtered = 0;
38763dab8eeSAdrian Chadd 		break;
38863dab8eeSAdrian Chadd 	}
38963dab8eeSAdrian Chadd 
39063dab8eeSAdrian Chadd 	*pos += filtered;
39163dab8eeSAdrian Chadd 	s->pos += filtered;
39263dab8eeSAdrian Chadd }
39363dab8eeSAdrian Chadd 
39463dab8eeSAdrian Chadd /*
39563dab8eeSAdrian Chadd  * Flush pending filtered data from temp to the output buffer.
39663dab8eeSAdrian Chadd  * Move the remaining mixture of possibly filtered and unfiltered
39763dab8eeSAdrian Chadd  * data to the beginning of temp.
39863dab8eeSAdrian Chadd  */
bcj_flush(struct xz_dec_bcj * s,struct xz_buf * b)39963dab8eeSAdrian Chadd static void bcj_flush(struct xz_dec_bcj *s, struct xz_buf *b)
40063dab8eeSAdrian Chadd {
40163dab8eeSAdrian Chadd 	size_t copy_size;
40263dab8eeSAdrian Chadd 
40363dab8eeSAdrian Chadd 	copy_size = min_t(size_t, s->temp.filtered, b->out_size - b->out_pos);
40463dab8eeSAdrian Chadd 	memcpy(b->out + b->out_pos, s->temp.buf, copy_size);
40563dab8eeSAdrian Chadd 	b->out_pos += copy_size;
40663dab8eeSAdrian Chadd 
40763dab8eeSAdrian Chadd 	s->temp.filtered -= copy_size;
40863dab8eeSAdrian Chadd 	s->temp.size -= copy_size;
40963dab8eeSAdrian Chadd 	memmove(s->temp.buf, s->temp.buf + copy_size, s->temp.size);
41063dab8eeSAdrian Chadd }
41163dab8eeSAdrian Chadd 
41263dab8eeSAdrian Chadd /*
41363dab8eeSAdrian Chadd  * The BCJ filter functions are primitive in sense that they process the
41463dab8eeSAdrian Chadd  * data in chunks of 1-16 bytes. To hide this issue, this function does
41563dab8eeSAdrian Chadd  * some buffering.
41663dab8eeSAdrian Chadd  */
xz_dec_bcj_run(struct xz_dec_bcj * s,struct xz_dec_lzma2 * lzma2,struct xz_buf * b)41763dab8eeSAdrian Chadd XZ_EXTERN enum xz_ret xz_dec_bcj_run(struct xz_dec_bcj *s,
41863dab8eeSAdrian Chadd 				     struct xz_dec_lzma2 *lzma2,
41963dab8eeSAdrian Chadd 				     struct xz_buf *b)
42063dab8eeSAdrian Chadd {
42163dab8eeSAdrian Chadd 	size_t out_start;
42263dab8eeSAdrian Chadd 
42363dab8eeSAdrian Chadd 	/*
42463dab8eeSAdrian Chadd 	 * Flush pending already filtered data to the output buffer. Return
425*cd3a777bSXin LI 	 * immediately if we couldn't flush everything, or if the next
42663dab8eeSAdrian Chadd 	 * filter in the chain had already returned XZ_STREAM_END.
42763dab8eeSAdrian Chadd 	 */
42863dab8eeSAdrian Chadd 	if (s->temp.filtered > 0) {
42963dab8eeSAdrian Chadd 		bcj_flush(s, b);
43063dab8eeSAdrian Chadd 		if (s->temp.filtered > 0)
43163dab8eeSAdrian Chadd 			return XZ_OK;
43263dab8eeSAdrian Chadd 
43363dab8eeSAdrian Chadd 		if (s->ret == XZ_STREAM_END)
43463dab8eeSAdrian Chadd 			return XZ_STREAM_END;
43563dab8eeSAdrian Chadd 	}
43663dab8eeSAdrian Chadd 
43763dab8eeSAdrian Chadd 	/*
43863dab8eeSAdrian Chadd 	 * If we have more output space than what is currently pending in
43963dab8eeSAdrian Chadd 	 * temp, copy the unfiltered data from temp to the output buffer
44063dab8eeSAdrian Chadd 	 * and try to fill the output buffer by decoding more data from the
44163dab8eeSAdrian Chadd 	 * next filter in the chain. Apply the BCJ filter on the new data
44263dab8eeSAdrian Chadd 	 * in the output buffer. If everything cannot be filtered, copy it
44363dab8eeSAdrian Chadd 	 * to temp and rewind the output buffer position accordingly.
44463dab8eeSAdrian Chadd 	 *
44563dab8eeSAdrian Chadd 	 * This needs to be always run when temp.size == 0 to handle a special
44663dab8eeSAdrian Chadd 	 * case where the output buffer is full and the next filter has no
44763dab8eeSAdrian Chadd 	 * more output coming but hasn't returned XZ_STREAM_END yet.
44863dab8eeSAdrian Chadd 	 */
44963dab8eeSAdrian Chadd 	if (s->temp.size < b->out_size - b->out_pos || s->temp.size == 0) {
45063dab8eeSAdrian Chadd 		out_start = b->out_pos;
45163dab8eeSAdrian Chadd 		memcpy(b->out + b->out_pos, s->temp.buf, s->temp.size);
45263dab8eeSAdrian Chadd 		b->out_pos += s->temp.size;
45363dab8eeSAdrian Chadd 
45463dab8eeSAdrian Chadd 		s->ret = xz_dec_lzma2_run(lzma2, b);
45563dab8eeSAdrian Chadd 		if (s->ret != XZ_STREAM_END
45663dab8eeSAdrian Chadd 				&& (s->ret != XZ_OK || s->single_call))
45763dab8eeSAdrian Chadd 			return s->ret;
45863dab8eeSAdrian Chadd 
45963dab8eeSAdrian Chadd 		bcj_apply(s, b->out, &out_start, b->out_pos);
46063dab8eeSAdrian Chadd 
46163dab8eeSAdrian Chadd 		/*
46263dab8eeSAdrian Chadd 		 * As an exception, if the next filter returned XZ_STREAM_END,
46363dab8eeSAdrian Chadd 		 * we can do that too, since the last few bytes that remain
46463dab8eeSAdrian Chadd 		 * unfiltered are meant to remain unfiltered.
46563dab8eeSAdrian Chadd 		 */
46663dab8eeSAdrian Chadd 		if (s->ret == XZ_STREAM_END)
46763dab8eeSAdrian Chadd 			return XZ_STREAM_END;
46863dab8eeSAdrian Chadd 
46963dab8eeSAdrian Chadd 		s->temp.size = b->out_pos - out_start;
47063dab8eeSAdrian Chadd 		b->out_pos -= s->temp.size;
47163dab8eeSAdrian Chadd 		memcpy(s->temp.buf, b->out + b->out_pos, s->temp.size);
47263dab8eeSAdrian Chadd 
47363dab8eeSAdrian Chadd 		/*
47463dab8eeSAdrian Chadd 		 * If there wasn't enough input to the next filter to fill
47563dab8eeSAdrian Chadd 		 * the output buffer with unfiltered data, there's no point
47663dab8eeSAdrian Chadd 		 * to try decoding more data to temp.
47763dab8eeSAdrian Chadd 		 */
47863dab8eeSAdrian Chadd 		if (b->out_pos + s->temp.size < b->out_size)
47963dab8eeSAdrian Chadd 			return XZ_OK;
48063dab8eeSAdrian Chadd 	}
48163dab8eeSAdrian Chadd 
48263dab8eeSAdrian Chadd 	/*
48363dab8eeSAdrian Chadd 	 * We have unfiltered data in temp. If the output buffer isn't full
48463dab8eeSAdrian Chadd 	 * yet, try to fill the temp buffer by decoding more data from the
48563dab8eeSAdrian Chadd 	 * next filter. Apply the BCJ filter on temp. Then we hopefully can
48663dab8eeSAdrian Chadd 	 * fill the actual output buffer by copying filtered data from temp.
48763dab8eeSAdrian Chadd 	 * A mix of filtered and unfiltered data may be left in temp; it will
48863dab8eeSAdrian Chadd 	 * be taken care on the next call to this function.
48963dab8eeSAdrian Chadd 	 */
49063dab8eeSAdrian Chadd 	if (b->out_pos < b->out_size) {
49163dab8eeSAdrian Chadd 		/* Make b->out{,_pos,_size} temporarily point to s->temp. */
49263dab8eeSAdrian Chadd 		s->out = b->out;
49363dab8eeSAdrian Chadd 		s->out_pos = b->out_pos;
49463dab8eeSAdrian Chadd 		s->out_size = b->out_size;
49563dab8eeSAdrian Chadd 		b->out = s->temp.buf;
49663dab8eeSAdrian Chadd 		b->out_pos = s->temp.size;
49763dab8eeSAdrian Chadd 		b->out_size = sizeof(s->temp.buf);
49863dab8eeSAdrian Chadd 
49963dab8eeSAdrian Chadd 		s->ret = xz_dec_lzma2_run(lzma2, b);
50063dab8eeSAdrian Chadd 
50163dab8eeSAdrian Chadd 		s->temp.size = b->out_pos;
50263dab8eeSAdrian Chadd 		b->out = s->out;
50363dab8eeSAdrian Chadd 		b->out_pos = s->out_pos;
50463dab8eeSAdrian Chadd 		b->out_size = s->out_size;
50563dab8eeSAdrian Chadd 
50663dab8eeSAdrian Chadd 		if (s->ret != XZ_OK && s->ret != XZ_STREAM_END)
50763dab8eeSAdrian Chadd 			return s->ret;
50863dab8eeSAdrian Chadd 
50963dab8eeSAdrian Chadd 		bcj_apply(s, s->temp.buf, &s->temp.filtered, s->temp.size);
51063dab8eeSAdrian Chadd 
51163dab8eeSAdrian Chadd 		/*
51263dab8eeSAdrian Chadd 		 * If the next filter returned XZ_STREAM_END, we mark that
51363dab8eeSAdrian Chadd 		 * everything is filtered, since the last unfiltered bytes
51463dab8eeSAdrian Chadd 		 * of the stream are meant to be left as is.
51563dab8eeSAdrian Chadd 		 */
51663dab8eeSAdrian Chadd 		if (s->ret == XZ_STREAM_END)
51763dab8eeSAdrian Chadd 			s->temp.filtered = s->temp.size;
51863dab8eeSAdrian Chadd 
51963dab8eeSAdrian Chadd 		bcj_flush(s, b);
52063dab8eeSAdrian Chadd 		if (s->temp.filtered > 0)
52163dab8eeSAdrian Chadd 			return XZ_OK;
52263dab8eeSAdrian Chadd 	}
52363dab8eeSAdrian Chadd 
52463dab8eeSAdrian Chadd 	return s->ret;
52563dab8eeSAdrian Chadd }
52663dab8eeSAdrian Chadd 
xz_dec_bcj_create(bool single_call)52763dab8eeSAdrian Chadd XZ_EXTERN struct xz_dec_bcj *xz_dec_bcj_create(bool single_call)
52863dab8eeSAdrian Chadd {
52963dab8eeSAdrian Chadd 	struct xz_dec_bcj *s = kmalloc(sizeof(*s), GFP_KERNEL);
53063dab8eeSAdrian Chadd 	if (s != NULL)
53163dab8eeSAdrian Chadd 		s->single_call = single_call;
53263dab8eeSAdrian Chadd 
53363dab8eeSAdrian Chadd 	return s;
53463dab8eeSAdrian Chadd }
53563dab8eeSAdrian Chadd 
xz_dec_bcj_reset(struct xz_dec_bcj * s,uint8_t id)53663dab8eeSAdrian Chadd XZ_EXTERN enum xz_ret xz_dec_bcj_reset(struct xz_dec_bcj *s, uint8_t id)
53763dab8eeSAdrian Chadd {
53863dab8eeSAdrian Chadd 	switch (id) {
53963dab8eeSAdrian Chadd #ifdef XZ_DEC_X86
54063dab8eeSAdrian Chadd 	case BCJ_X86:
54163dab8eeSAdrian Chadd #endif
54263dab8eeSAdrian Chadd #ifdef XZ_DEC_POWERPC
54363dab8eeSAdrian Chadd 	case BCJ_POWERPC:
54463dab8eeSAdrian Chadd #endif
54563dab8eeSAdrian Chadd #ifdef XZ_DEC_IA64
54663dab8eeSAdrian Chadd 	case BCJ_IA64:
54763dab8eeSAdrian Chadd #endif
54863dab8eeSAdrian Chadd #ifdef XZ_DEC_ARM
54963dab8eeSAdrian Chadd 	case BCJ_ARM:
55063dab8eeSAdrian Chadd #endif
55163dab8eeSAdrian Chadd #ifdef XZ_DEC_ARMTHUMB
55263dab8eeSAdrian Chadd 	case BCJ_ARMTHUMB:
55363dab8eeSAdrian Chadd #endif
55463dab8eeSAdrian Chadd #ifdef XZ_DEC_SPARC
55563dab8eeSAdrian Chadd 	case BCJ_SPARC:
55663dab8eeSAdrian Chadd #endif
55763dab8eeSAdrian Chadd 		break;
55863dab8eeSAdrian Chadd 
55963dab8eeSAdrian Chadd 	default:
56063dab8eeSAdrian Chadd 		/* Unsupported Filter ID */
56163dab8eeSAdrian Chadd 		return XZ_OPTIONS_ERROR;
56263dab8eeSAdrian Chadd 	}
56363dab8eeSAdrian Chadd 
56463dab8eeSAdrian Chadd 	s->type = id;
56563dab8eeSAdrian Chadd 	s->ret = XZ_OK;
56663dab8eeSAdrian Chadd 	s->pos = 0;
56763dab8eeSAdrian Chadd 	s->x86_prev_mask = 0;
56863dab8eeSAdrian Chadd 	s->temp.filtered = 0;
56963dab8eeSAdrian Chadd 	s->temp.size = 0;
57063dab8eeSAdrian Chadd 
57163dab8eeSAdrian Chadd 	return XZ_OK;
57263dab8eeSAdrian Chadd }
57363dab8eeSAdrian Chadd 
57463dab8eeSAdrian Chadd #endif
575