xref: /rk3399_rockchip-uboot/drivers/misc/misc_decompress.c (revision 74eb6027432600de60ed1c8bf892f1f8243c2c8a)
1 // SPDX-License-Identifier:     GPL-2.0+
2 /*
3  * Copyright (C) 2020 Rockchip Electronics Co., Ltd
4  */
5 #include <common.h>
6 #include <dm.h>
7 #include <misc.h>
8 #include <dm/uclass.h>
9 #include <dm/uclass-internal.h>
10 
11 #define HEAD_CRC		2
12 #define EXTRA_FIELD		4
13 #define ORIG_NAME		8
14 #define COMMENT			0x10
15 #define RESERVED		0xe0
16 #define DEFLATED		8
17 
18 static u32 misc_decomp_async, misc_decomp_sync;
19 
20 static void decomp_set_flags(u32 *flags, u8 comp)
21 {
22 	if (comp == IH_COMP_GZIP)
23 		*flags |= DECOM_GZIP;
24 	else if (comp == IH_COMP_LZ4)
25 		*flags |= DECOM_LZ4;
26 }
27 
28 void misc_decompress_async(u8 comp)
29 {
30 	decomp_set_flags(&misc_decomp_async, comp);
31 }
32 
33 void misc_decompress_sync(u8 comp)
34 {
35 	decomp_set_flags(&misc_decomp_sync, comp);
36 }
37 
38 static int misc_gzip_parse_header(const unsigned char *src, unsigned long len)
39 {
40 	int i, flags;
41 
42 	/* skip header */
43 	i = 10;
44 	flags = src[3];
45 	if (src[2] != DEFLATED || (flags & RESERVED) != 0) {
46 		debug("Error: Bad gzipped data\n");
47 		return (-1);
48 	}
49 	if ((flags & EXTRA_FIELD) != 0)
50 		i = 12 + src[10] + (src[11] << 8);
51 	if ((flags & ORIG_NAME) != 0)
52 		while (src[i++] != 0)
53 			;
54 	if ((flags & COMMENT) != 0)
55 		while (src[i++] != 0)
56 			;
57 	if ((flags & HEAD_CRC) != 0)
58 		i += 2;
59 	if (i >= len) {
60 		puts("Error: gunzip out of data in header\n");
61 		return (-1);
62 	}
63 	return i;
64 }
65 
66 static u32 misc_get_data_size(unsigned long src, unsigned long len, u32 cap)
67 {
68 	if (cap == DECOM_GZIP)
69 		return *(u32 *)(src + len - 4);
70 
71 	return 0;
72 }
73 
74 struct udevice *misc_decompress_get_device(u32 capability)
75 {
76 	return misc_get_device_by_capability(capability);
77 }
78 
79 int misc_decompress_start(struct udevice *dev, unsigned long dst,
80 			  unsigned long src, unsigned long src_len)
81 {
82 	struct decom_param param;
83 
84 	param.addr_dst = dst;
85 	param.addr_src = src;
86 	if (misc_gzip_parse_header((unsigned char *)src, 0xffff) > 0) {
87 		param.mode = DECOM_GZIP;
88 	} else {
89 		printf("Unsupported decompression format.\n");
90 		return -EPERM;
91 	}
92 
93 	param.size_src = misc_get_data_size(src, src_len, param.mode);
94 	if (!param.size_src)
95 		return -EINVAL;
96 
97 	return misc_ioctl(dev, IOCTL_REQ_START, &param);
98 }
99 
100 int misc_decompress_stop(struct udevice *dev)
101 {
102 	return misc_ioctl(dev, IOCTL_REQ_STOP, NULL);
103 }
104 
105 bool misc_decompress_is_complete(struct udevice *dev)
106 {
107 	if (misc_ioctl(dev, IOCTL_REQ_POLL, NULL))
108 		return false;
109 	else
110 		return true;
111 }
112 
113 int misc_decompress_data_size(struct udevice *dev, u64 *size, u32 cap)
114 {
115 	struct decom_param param;
116 	int ret;
117 
118 	param.mode = cap;
119 	param.size_dst = 0; /* clear */
120 
121 	ret = misc_ioctl(dev, IOCTL_REQ_DATA_SIZE, &param);
122 	if (!ret)
123 		*size = param.size_dst;
124 
125 	return ret;
126 }
127 
128 static int misc_decompress_finish(struct udevice *dev, u32 cap)
129 {
130 	int timeout = 10000;
131 
132 	while (!misc_decompress_is_complete(dev)) {
133 		if (timeout < 0)
134 			return -EIO;
135 		timeout--;
136 		udelay(10);
137 	}
138 
139 	return misc_decompress_stop(dev);
140 }
141 
142 int misc_decompress_cleanup(void)
143 {
144 	const struct misc_ops *ops;
145 	struct udevice *dev;
146 	struct uclass *uc;
147 	int ret;
148 	u32 cap;
149 
150 	ret = uclass_get(UCLASS_MISC, &uc);
151 	if (ret)
152 		return 0;
153 
154 	/* use "find_" */
155 	for (uclass_find_first_device(UCLASS_MISC, &dev);
156 	     dev;
157 	     uclass_find_next_device(&dev)) {
158 		ops = device_get_ops(dev);
159 		if (!ops || !ops->ioctl)
160 			continue;
161 		else if (ops->ioctl(dev, IOCTL_REQ_CAPABILITY, &cap))
162 			continue;
163 		else if (misc_decomp_async & cap)
164 			continue;
165 
166 		if (misc_decomp_sync & cap) {
167 			ret = ops->ioctl(dev, IOCTL_REQ_STOP, NULL);
168 			if (ret) {
169 				printf("Failed to stop decompress: %s, ret=%d\n",
170 				       dev->name, ret);
171 				return ret;
172 			}
173 		}
174 	}
175 
176 	return 0;
177 }
178 
179 int misc_decompress_process(unsigned long dst, unsigned long src,
180 			    unsigned long src_len, u32 cap, bool sync,
181 			    u64 *size)
182 {
183 	struct udevice *dev;
184 	int ret;
185 
186 	dev = misc_decompress_get_device(cap);
187 	if (!dev)
188 		return -ENODEV;
189 
190 	/* Wait last finish */
191 	ret = misc_decompress_finish(dev, cap);
192 	if (ret)
193 		return ret;
194 
195 	ret = misc_decompress_start(dev, dst, src, src_len);
196 	if (ret)
197 		return ret;
198 
199 	/*
200 	 * Wait this round finish ?
201 	 *
202 	 * If sync, return original data length after decompress done.
203 	 * otherwise return from compressed file information.
204 	 */
205 	if (sync) {
206 		ret = misc_decompress_finish(dev, cap);
207 		if (ret)
208 			return ret;
209 		if (size)
210 			ret = misc_decompress_data_size(dev, size, cap);
211 	} else {
212 		if (size)
213 			*size = misc_get_data_size(src, src_len, cap);
214 	}
215 
216 	return ret;
217 }
218