master
c 511 lines 15.5 KB
Raw
1 /*
2 * Kendryte K230 GZIP decompression engine
3 *
4 * Copyright (c) 2026 Tao Ding <dingtao0430@163.com>
5 *
6 * SPDX-License-Identifier: GPL-2.0-or-later
7 *
8 * Decompression accelerator is mainly used to implement hardware
9 * GZIP decompression function. (K230 TRM section 15)
10 */
11
12 #include "qemu/osdep.h"
13 #include "qemu/log.h"
14 #include "qemu/module.h"
15 #include "hw/core/qdev-properties.h"
16 #include "migration/vmstate.h"
17 #include "system/address-spaces.h"
18 #include "hw/core/irq.h"
19 #include "hw/misc/k230_decomp_gzip.h"
20 #include "trace.h"
21
22 #define GZIP_METHOD_DEFLATE 8
23 #define GZIP_METHOD_VENDOR_9 9
24
25 #define DEFLATE_BTYPE_DYNAMIC 2
26
27 #define K230_DECOMP_GZIP_OUTPUT_SLOTS 4
28 #define K230_DECOMP_GZIP_INPUT_SLOTS 2
29
30 static void k230_decomp_gzip_set_output(K230DecompGzipState *s, int line,
31 int level)
32 {
33 qemu_set_irq(s->signal_out[line], level);
34 }
35
36 static bool k230_decomp_gzip_read_mem(K230DecompGzipState *s, hwaddr addr,
37 void *buf, size_t len)
38 {
39 MemTxResult ret = address_space_read(&address_space_memory,
40 s->sram_base + addr,
41 MEMTXATTRS_UNSPECIFIED, buf, len);
42
43 return ret == MEMTX_OK;
44 }
45
46 static bool k230_decomp_gzip_write_mem(K230DecompGzipState *s, hwaddr addr,
47 const void *buf, size_t len)
48 {
49 return address_space_write(&address_space_memory, s->sram_base + addr,
50 MEMTXATTRS_UNSPECIFIED, buf, len) == MEMTX_OK;
51 }
52
53 static hwaddr k230_decomp_gzip_output_addr(K230DecompGzipState *s)
54 {
55 return K230_DECOMP_GZIP_SRAM_OUT_BASE +
56 s->output.slot * K230_DECOMP_GZIP_BLOCK_SIZE +
57 s->output.current_offset;
58 }
59
60 static hwaddr k230_decomp_gzip_input_addr(K230DecompGzipState *s)
61 {
62 return s->input.slot * K230_DECOMP_GZIP_BLOCK_SIZE +
63 K230_DECOMP_GZIP_SRAM_IN_BASE;
64 }
65
66 static uint32_t k230_decomp_gzip_current_input_size(K230DecompGzipState *s)
67 {
68 if (s->total_requested == 0) {
69 return 0;
70 }
71
72 return ((s->total_requested - 1) % K230_DECOMP_GZIP_BLOCK_SIZE) + 1;
73 }
74
75 static void k230_decomp_gzip_update_ctrl_en(K230DecompGzipState *s)
76 {
77 int level = s->active && !!(s->gzip_src_size & K230_DECOMP_GZIP_CTRL_EN);
78
79 k230_decomp_gzip_set_output(s, K230_DECOMP_GZIP_GPIO_DECOMP_CTRL_EN,
80 level);
81 }
82
83 static void k230_decomp_gzip_reset_stream(K230DecompGzipState *s)
84 {
85 if (s->zstream_inited) {
86 inflateEnd(&s->zs);
87 s->zstream_inited = false;
88 }
89 memset(&s->zs, 0, sizeof(s->zs));
90 }
91
92 static void k230_decomp_gzip_finish(K230DecompGzipState *s, bool crc_ok)
93 {
94 s->active = false;
95 k230_decomp_gzip_reset_stream(s);
96 k230_decomp_gzip_update_ctrl_en(s);
97 if (crc_ok) {
98 s->decomp_stat |= K230_DECOMP_GZIP_STAT_CRC_OK;
99 } else {
100 s->decomp_stat &= ~K230_DECOMP_GZIP_STAT_CRC_OK;
101 }
102 }
103
104 static bool k230_decomp_gzip_load_input(K230DecompGzipState *s,
105 uint32_t addr, uint32_t size)
106 {
107 s->input.current_offset = 0;
108
109 return k230_decomp_gzip_read_mem(s, addr, s->input_buf, size);
110 }
111
112 static bool k230_decomp_gzip_request_input(K230DecompGzipState *s)
113 {
114 uint32_t total_requested = s->total_requested;
115
116 k230_decomp_gzip_set_output(s, K230_DECOMP_GZIP_GPIO_DMA_WRITE_REQ, 1);
117 if (s->total_requested == total_requested) {
118 qemu_log_mask(LOG_GUEST_ERROR,
119 "%s: dma write request not acknowledged\n",
120 TYPE_K230_DECOMP_GZIP);
121 return false;
122 }
123 return true;
124 }
125
126 static bool k230_decomp_gzip_request_output(K230DecompGzipState *s)
127 {
128 uint32_t slot = s->output.slot;
129 uint32_t current_offset = s->output.current_offset;
130
131 k230_decomp_gzip_set_output(s, K230_DECOMP_GZIP_GPIO_DMA_READ_REQ, 1);
132 if (s->output.slot == slot &&
133 s->output.current_offset == current_offset) {
134 qemu_log_mask(LOG_GUEST_ERROR,
135 "%s: dma read request not acknowledged\n",
136 TYPE_K230_DECOMP_GZIP);
137 return false;
138 }
139 return true;
140 }
141
142 static bool k230_decomp_gzip_init_stream(K230DecompGzipState *s)
143 {
144 uint8_t btype = (s->input_buf[10] >> 1) & 0x3;
145 if (btype != DEFLATE_BTYPE_DYNAMIC) {
146 qemu_log_mask(LOG_GUEST_ERROR,
147 "%s: unsupported DEFLATE BTYPE %u\n",
148 TYPE_K230_DECOMP_GZIP, btype);
149 return false;
150 }
151
152 /* The compression script for K230 will modify the third byte to 0x9 */
153 if (s->input_buf[2] == GZIP_METHOD_VENDOR_9) {
154 s->input_buf[2] = GZIP_METHOD_DEFLATE;
155 }
156
157 if (inflateInit2(&s->zs, 15 + 16) != Z_OK) {
158 return false;
159 }
160
161 s->zstream_inited = true;
162 return true;
163 }
164
165 static bool k230_decomp_gzip_run_inflate(K230DecompGzipState *s)
166 {
167 uint32_t output_size = s->gzip_out_size;
168 uint32_t current_input_size = k230_decomp_gzip_current_input_size(s);
169 uint32_t avail_in;
170 uint32_t avail_out;
171 uint32_t consumed;
172 uint32_t produced;
173 int ret;
174
175 avail_in = current_input_size - s->input.current_offset;
176 avail_out = K230_DECOMP_GZIP_BLOCK_SIZE - s->output.current_offset;
177
178 s->zs.next_in = s->input_buf + s->input.current_offset;
179 s->zs.avail_in = avail_in;
180 s->zs.next_out = s->output_buf;
181 s->zs.avail_out = avail_out;
182
183 ret = inflate(&s->zs, Z_NO_FLUSH);
184 if (ret != Z_OK && ret != Z_STREAM_END && ret != Z_BUF_ERROR) {
185 return false;
186 }
187
188 consumed = avail_in - s->zs.avail_in;
189 produced = avail_out - s->zs.avail_out;
190
191 if (produced) {
192 if (s->total_produced + produced > output_size ||
193 !k230_decomp_gzip_write_mem(s, k230_decomp_gzip_output_addr(s),
194 s->output_buf, produced)) {
195 return false;
196 }
197 s->output.current_offset += produced;
198 s->total_produced += produced;
199 }
200
201 if (consumed) {
202 s->input.current_offset += consumed;
203 }
204
205 if (ret == Z_STREAM_END) {
206 s->stream_end = true;
207 if (s->total_produced != output_size) {
208 return false;
209 }
210 }
211
212 return true;
213 }
214
215 static void k230_decomp_gzip_kick(K230DecompGzipState *s)
216 {
217 if (!s->active || s->in_kick) {
218 return;
219 }
220
221 uint32_t input_size = s->gzip_src_size & K230_DECOMP_GZIP_DMA_IN_MASK;
222 s->in_kick = true;
223 while (s->active) {
224 uint32_t current_input_size = k230_decomp_gzip_current_input_size(s);
225 /* Output is full or last block */
226 if (s->output.current_offset == K230_DECOMP_GZIP_BLOCK_SIZE ||
227 (s->stream_end && s->output.current_offset != 0)) {
228 if (!k230_decomp_gzip_request_output(s)) {
229 k230_decomp_gzip_finish(s, false);
230 break;
231 }
232 continue;
233 }
234
235 if (!s->zstream_inited) {
236 if (!k230_decomp_gzip_request_input(s) ||
237 !k230_decomp_gzip_init_stream(s)) {
238 k230_decomp_gzip_finish(s, false);
239 break;
240 }
241 continue;
242 }
243
244 /* Transfer finish */
245 if (s->stream_end) {
246 k230_decomp_gzip_finish(s,
247 s->total_produced == s->gzip_out_size);
248 break;
249 }
250
251 /* Input buf has has been fully decompreed, need request more */
252 if (s->input.current_offset == current_input_size) {
253 if (s->total_requested < input_size) {
254 if (!k230_decomp_gzip_request_input(s)) {
255 k230_decomp_gzip_finish(s, false);
256 break;
257 }
258 continue;
259 }
260 k230_decomp_gzip_finish(s, false);
261 break;
262 }
263
264 /* Decompress the data, input buf or output buf are consumed in one block */
265 if (!k230_decomp_gzip_run_inflate(s)) {
266 k230_decomp_gzip_finish(s, false);
267 break;
268 }
269 }
270 s->in_kick = false;
271 }
272
273 static void k230_decomp_gzip_handle_ack(void *opaque, int n, int level)
274 {
275 K230DecompGzipState *s = opaque;
276
277 if (!level || !s->active) {
278 return;
279 }
280
281 switch (n) {
282 case K230_DECOMP_GZIP_GPIO_DMA_WRITE_ACK:
283 {
284 uint32_t input_size = s->gzip_src_size & K230_DECOMP_GZIP_DMA_IN_MASK;
285 uint32_t chunk = MIN(K230_DECOMP_GZIP_BLOCK_SIZE,
286 input_size - s->total_requested);
287
288 if (!k230_decomp_gzip_load_input(s, k230_decomp_gzip_input_addr(s),
289 chunk)) {
290 k230_decomp_gzip_finish(s, false);
291 return;
292 }
293 s->total_requested += chunk;
294 s->input.slot = (s->input.slot + 1) % K230_DECOMP_GZIP_INPUT_SLOTS;
295 break;
296 }
297 case K230_DECOMP_GZIP_GPIO_DMA_READ_ACK:
298 if (s->output.current_offset == 0) {
299 k230_decomp_gzip_finish(s, false);
300 return;
301 }
302 s->output.slot = (s->output.slot + 1) % K230_DECOMP_GZIP_OUTPUT_SLOTS;
303 s->output.current_offset = 0;
304 break;
305 default:
306 qemu_log_mask(LOG_GUEST_ERROR,
307 "%s: invalid DMA acknowledgment signal %d\n",
308 TYPE_K230_DECOMP_GZIP, n);
309 return;
310 }
311
312 k230_decomp_gzip_kick(s);
313 }
314
315 static void k230_decomp_gzip_start(K230DecompGzipState *s)
316 {
317 uint32_t input_size = s->gzip_src_size & K230_DECOMP_GZIP_DMA_IN_MASK;
318
319 k230_decomp_gzip_reset_stream(s);
320 s->decomp_stat &= ~K230_DECOMP_GZIP_STAT_CRC_OK;
321 s->total_requested = 0;
322 s->total_produced = 0;
323 s->stream_end = false;
324 memset(&s->input, 0, sizeof(s->input));
325 memset(&s->output, 0, sizeof(s->output));
326
327 if (!(s->gzip_src_size & K230_DECOMP_GZIP_CTRL_EN) ||
328 input_size == 0 || s->gzip_out_size == 0) {
329 k230_decomp_gzip_finish(s, false);
330 return;
331 }
332
333 s->active = true;
334 k230_decomp_gzip_update_ctrl_en(s);
335 k230_decomp_gzip_kick(s);
336 }
337
338 static uint64_t k230_decomp_gzip_read(void *opaque, hwaddr offset,
339 unsigned size)
340 {
341 K230DecompGzipState *s = opaque;
342 uint64_t value = 0;
343
344 switch (offset) {
345 case K230_DECOMP_GZIP_DECOMP_START:
346 value = s->decomp_start & ~K230_DECOMP_GZIP_START; /* start bit is write only */
347 break;
348 case K230_DECOMP_GZIP_GZIP_SRC_SIZE:
349 value = s->gzip_src_size;
350 break;
351 case K230_DECOMP_GZIP_GZIP_OUT_SIZE:
352 value = s->gzip_out_size;
353 break;
354 case K230_DECOMP_GZIP_DECOMP_STAT:
355 value = s->decomp_stat;
356 break;
357 default:
358 qemu_log_mask(LOG_GUEST_ERROR,
359 "%s: bad read offset 0x%" HWADDR_PRIx "\n",
360 TYPE_K230_DECOMP_GZIP, offset);
361 break;
362 }
363
364 trace_k230_decomp_gzip_read(offset, size, value);
365 return value;
366 }
367
368 static void k230_decomp_gzip_write(void *opaque, hwaddr offset,
369 uint64_t value, unsigned size)
370 {
371 K230DecompGzipState *s = opaque;
372
373 trace_k230_decomp_gzip_write(offset, size, value);
374
375 switch (offset) {
376 case K230_DECOMP_GZIP_DECOMP_START:
377 s->decomp_start = value;
378 if (value & K230_DECOMP_GZIP_START) {
379 k230_decomp_gzip_start(s);
380 }
381 break;
382 case K230_DECOMP_GZIP_GZIP_SRC_SIZE:
383 s->gzip_src_size = value;
384 k230_decomp_gzip_update_ctrl_en(s);
385 break;
386 case K230_DECOMP_GZIP_GZIP_OUT_SIZE:
387 s->gzip_out_size = value;
388 break;
389 case K230_DECOMP_GZIP_DECOMP_STAT:
390 break;
391 default:
392 qemu_log_mask(LOG_GUEST_ERROR,
393 "%s: bad write offset 0x%" HWADDR_PRIx "\n",
394 TYPE_K230_DECOMP_GZIP, offset);
395 break;
396 }
397 }
398
399 static const MemoryRegionOps k230_decomp_gzip_ops = {
400 .read = k230_decomp_gzip_read,
401 .write = k230_decomp_gzip_write,
402 .endianness = DEVICE_LITTLE_ENDIAN,
403 .valid.min_access_size = 4,
404 .valid.max_access_size = 4,
405 .impl.min_access_size = 4,
406 .impl.max_access_size = 4,
407 };
408
409 static const VMStateDescription vmstate_k230_decomp_gzip_slot = {
410 .name = "k230.decomp-gzip.slot",
411 .version_id = 1,
412 .minimum_version_id = 1,
413 .fields = (const VMStateField[]) {
414 VMSTATE_UINT32(slot, K230DecompGzipSlotState),
415 VMSTATE_UINT32(current_offset, K230DecompGzipSlotState),
416 VMSTATE_END_OF_LIST()
417 }
418 };
419
420 static const VMStateDescription vmstate_k230_decomp_gzip = {
421 .name = "k230.decomp-gzip",
422 .version_id = 1,
423 .minimum_version_id = 1,
424 .fields = (const VMStateField[]) {
425 VMSTATE_UINT32(decomp_start, K230DecompGzipState),
426 VMSTATE_UINT32(gzip_src_size, K230DecompGzipState),
427 VMSTATE_UINT32(gzip_out_size, K230DecompGzipState),
428 VMSTATE_UINT32(decomp_stat, K230DecompGzipState),
429 VMSTATE_STRUCT(input, K230DecompGzipState, 1,
430 vmstate_k230_decomp_gzip_slot,
431 K230DecompGzipSlotState),
432 VMSTATE_STRUCT(output, K230DecompGzipState, 1,
433 vmstate_k230_decomp_gzip_slot,
434 K230DecompGzipSlotState),
435 VMSTATE_UINT32(total_requested, K230DecompGzipState),
436 VMSTATE_UINT32(total_produced, K230DecompGzipState),
437 VMSTATE_BOOL(active, K230DecompGzipState),
438 VMSTATE_BOOL(in_kick, K230DecompGzipState),
439 VMSTATE_BOOL(zstream_inited, K230DecompGzipState),
440 VMSTATE_BOOL(stream_end, K230DecompGzipState),
441 VMSTATE_UINT8_ARRAY(input_buf, K230DecompGzipState,
442 K230_DECOMP_GZIP_BLOCK_SIZE),
443 VMSTATE_UINT8_ARRAY(output_buf, K230DecompGzipState,
444 K230_DECOMP_GZIP_BLOCK_SIZE),
445 VMSTATE_END_OF_LIST()
446 }
447 };
448
449 static void k230_decomp_gzip_reset_hold(Object *obj, ResetType type)
450 {
451 K230DecompGzipState *s = K230_DECOMP_GZIP(obj);
452
453 k230_decomp_gzip_reset_stream(s);
454 memset(&s->zs, 0, sizeof(s->zs));
455 s->decomp_start = 0;
456 s->gzip_src_size = 0;
457 s->gzip_out_size = 0;
458 s->decomp_stat = 0;
459 memset(&s->input, 0, sizeof(s->input));
460 memset(&s->output, 0, sizeof(s->output));
461 s->total_requested = 0;
462 s->total_produced = 0;
463 s->active = false;
464 s->in_kick = false;
465 s->stream_end = false;
466 }
467
468 static void k230_decomp_gzip_realize(DeviceState *dev, Error **errp)
469 {
470 K230DecompGzipState *s = K230_DECOMP_GZIP(dev);
471 SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
472
473 memory_region_init_io(&s->iomem, OBJECT(dev), &k230_decomp_gzip_ops, s,
474 TYPE_K230_DECOMP_GZIP,
475 K230_DECOMP_GZIP_MMIO_SIZE);
476 sysbus_init_mmio(sbd, &s->iomem);
477 qdev_init_gpio_in(dev, k230_decomp_gzip_handle_ack,
478 K230_DECOMP_GZIP_NUM_GPIOS_IN);
479 qdev_init_gpio_out(dev, s->signal_out,
480 K230_DECOMP_GZIP_NUM_GPIOS_OUT);
481 }
482
483 static const Property k230_decomp_gzip_properties[] = {
484 DEFINE_PROP_UINT64("sram-base", K230DecompGzipState, sram_base, 0),
485 };
486
487 static void k230_decomp_gzip_class_init(ObjectClass *oc, const void *data)
488 {
489 DeviceClass *dc = DEVICE_CLASS(oc);
490 ResettableClass *rc = RESETTABLE_CLASS(oc);
491
492 dc->realize = k230_decomp_gzip_realize;
493 rc->phases.hold = k230_decomp_gzip_reset_hold;
494 device_class_set_props(dc, k230_decomp_gzip_properties);
495 dc->vmsd = &vmstate_k230_decomp_gzip;
496 dc->desc = "Kendryte K230 GZIP decompression engine";
497 }
498
499 static const TypeInfo k230_decomp_gzip_info = {
500 .name = TYPE_K230_DECOMP_GZIP,
501 .parent = TYPE_SYS_BUS_DEVICE,
502 .instance_size = sizeof(K230DecompGzipState),
503 .class_init = k230_decomp_gzip_class_init,
504 };
505
506 static void k230_decomp_gzip_register_types(void)
507 {
508 type_register_static(&k230_decomp_gzip_info);
509 }
510
511 type_init(k230_decomp_gzip_register_types)