Skip to content

fullhan: FH885xV200, FH865x, FH885x V100, FH8626V100 and FH8632 machines - #207

Merged
dimerr merged 4 commits into
OpenIPC:masterfrom
dimerr:fullhan
Oct 25, 2026
Merged

dimerr merged 4 commits into
OpenIPC:masterfrom
dimerr:fullhan

Conversation

@dimerr

@dimerr dimerr commented Oct 25, 2026

Copy link
Copy Markdown
Collaborator

Adds Fullhan's camera SoCs: ten machines in hw/arm/fullhan.c over one memory map of mostly DesignWare blocks, Fullhan's own devices as hw/*/fh-*.c. Details in docs/fullhan.md.

Machines: fh8852v200, fh8856v200, fh8858v200, fh8652, fh8656, fh8658, fh8852v100, fh8856v100, fh8626v100, fh8632.

Devices

  • ICTL, APB timers (clocked from the PMU), UART, GPIO, SSI with the SPIC data window, AXI and AHB DMACs, I2C, watchdog, RTC (v2 and FH8632's v1), PMU, eFuse, PTS.
  • AES/DES engine (ECB/CBC/CTR/CFB/OFB) and SADC (three layouts; inputs via qom-set /machine/sadc chN).
  • GMAC on xm-dwmac (new phy-id / phy-addr properties), MMC on hisi-himci, USB on dwc2-usb, the FH865x GIC-400.
  • sensor= on I2C0: GalaxyCore GC4653 / GC2053 / GC2063 / GC2093 / GC1054, MIS2006, JX-F37.
  • A mask ROM stand-in that boots the SPI NOR (flash-file=, written back), past FH865x's DDR-sizing ramboot.

QEMU patches

  • 0012: CCSIDR of a missing cache reads the L1 D-cache's (FH865x U-Boot).
  • 0013: m25p80 clears WEL after program/erase (3.0.8 kernels' m25p80).
  • 0014: dwc2 GHWCFG properties + host descriptor DMA (3.0.8 kernels' fh_otg).

Tested

  • Camera flash dumps (FH8852V200, FH8856V200, FH8852 V100, FH8626V100, FH8652) boot through the vendor U-Boot to their applications; FH8632 from its SDK images.
  • OpenIPC fh8852v200 / fh8852v100 boot to a login with a DHCP lease and a JFFS2 overlay: qemu-boot/test-fullhan-boot.sh, added to CI.
  • qemu/tests/test-fullhan.py (qtest, all generations; AES against FIPS-197 / SP 800-38A vectors), added to CI.
  • USB: hub + storage on both kernel generations; usb-net pings over fh_otg.

Not modelled: the ARC625 coprocessor and the ISP / VPU / encoder / JPEG blocks (only binaries in the SDKs), hence no video or audio; PWM, IR receiver.

Ten machines in hw/arm/fullhan.c over one memory map of mostly
DesignWare blocks, with Fullhan's own devices as hw/*/fh-*.c:

* fh-intc (DW APB ICTL; byte reads of FINALSTATUS for find_first_bit),
  fh-timer (DW APB timers clocked from the PMU), fh-uart (16550 + USR),
  fh-gpio, fh-spi (DW SSI with the SPIC data window, swap and 32-bit
  frames; fast-read dummies clocked as 8), fh-axi-dmac / fh-ahb-dmac,
  fh-i2c, fh-wdt, fh-rtc (v2 command interface, FH8632's v1), fh-pmu
  (chip IDs, boot strap, resets, the timer clock as a QEMU Clock, PTS),
  fh-efuse, fh-pts, fh-aes (AES/DES/3DES in ECB/CBC/CTR/CFB/OFB through
  QEMU's ciphers) and fh-sadc (the three register layouts);
* the GMAC on xm-dwmac (new phy-id / phy-addr properties), MMC on
  hisi-himci, USB on dwc2-usb, the FH865x GIC-400 on a15mpcore_priv;
* a mask ROM stand-in that loads the boot header's image, past FH865x's
  DDR-sizing ramboot; the SPI NOR from flash-file= (written back);
* sensor=: a GalaxyCore model (GC4653, GC2053, GC2063, GC2093, GC1054)
  or the MIS2006 / JX-F37 ones on I2C0, for the vendor apps and ipcinfo.

QEMU patches: 0012 (CCSIDR of a missing cache reads the L1 D-cache's,
for FH865x's U-Boot), 0013 (m25p80 clears WEL after program / erase, for
the 3.0.8 kernels' m25p80), 0014 (dwc2 GHWCFG properties and host
descriptor DMA, for the 3.0.8 kernels' fh_otg).

Camera dumps boot through the vendor U-Boot to their applications; the
OpenIPC fh8852v200 / fh8852v100 releases boot to a login with a DHCP
lease and a JFFS2 overlay (qemu-boot/test-fullhan-boot.sh, in CI).
The ARC coprocessor and the media blocks are not modelled.
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Add ten Fullhan camera SoC machines and firmware boot support

✨ Enhancement 🐞 Bug fix 🧪 Tests 📝 Documentation ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Add ten Fullhan machines with generation-specific peripherals, interrupts, clocks, sensors, and
 SPI NOR booting.
• Extend shared QEMU devices to support Fullhan Ethernet, legacy USB, cache, and flash behavior.
• Document supported firmware and validate devices and OpenIPC boots in CI.
Diagram

graph TD
  Machine["Fullhan machines"] --> Boot["ROM stand-in"] --> NOR[("SPI NOR")]
  Machine --> SPI["SPI controller"] --> NOR
  SPI --> DMA["AXI/AHB DMA"]
  Machine --> PMU["PMU and timers"] --> IRQ["CPU interrupts"]
  Machine --> I2C["I2C sensors"]
  DMA --> IRQ
  I2C --> IRQ
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Extend reusable DesignWare cores instead of adding Fullhan-specific models
  • ➕ Could reduce duplicate controller logic across future machine families.
  • ➕ Could consolidate maintenance and hardware-level tests.
  • ➖ Existing models may not expose the register layouts and vendor behavior needed by these firmware images.
  • ➖ Broad shared-device changes would increase regression risk for other machines.

Recommendation: The chosen boundary is reasonable: reuse existing GMAC, MMC, USB, and flash devices where compatible, and keep firmware-specific controller behavior in Fullhan models. Consider extracting common DesignWare logic only when another machine demonstrates a compatible need.

Files changed (30) +7719 / -7

Enhancement (21) +6288 / -5
fh-openipc-flash.shAssemble an OpenIPC NOR image around vendor U-Boot +71/-0

Assemble an OpenIPC NOR image around vendor U-Boot

• Retains the vendor dump’s boot region, places an OpenIPC kernel and root filesystem in their NOR partitions, and writes matching U-Boot environment settings.

qemu-boot/fh-openipc-flash.sh

run-fullhan.shProvide a launcher for Fullhan NOR images +37/-0

Provide a launcher for Fullhan NOR images

• Starts a selected Fullhan machine with a writable flash image and chooses TAP or user-mode networking according to availability.

qemu-boot/run-fullhan.sh

fullhan.cDefine ten table-driven Fullhan SoC machines +950/-0

Define ten table-driven Fullhan SoC machines

• Selects each die’s CPU, interrupt controller, DMA engine, devices, and sensor on a shared memory map. Adds persistent SPI NOR, a boot-header ROM stand-in, and direct kernel boot support.

qemu/hw/arm/fullhan.c

fh-uart.cModel Fullhan’s DesignWare UART status extensions +176/-0

Model Fullhan’s DesignWare UART status extensions

• Wraps the existing 16550-style serial device with Fullhan register spacing and the FIFO status registers polled by vendor drivers.

qemu/hw/char/fh-uart.c

fh-ahb-dmac.cModel older Fullhan AHB descriptor DMA +493/-0

Model older Fullhan AHB descriptor DMA

• Implements channel registers, linked descriptors, write-back, handshake-gated transfers, and completion interrupts for V100-generation machines.

qemu/hw/dma/fh-ahb-dmac.c

fh-axi-dmac.cModel Fullhan AXI linked-list DMA +528/-0

Model Fullhan AXI linked-list DMA

• Implements AXI channel configuration, descriptor-driven transfers, peripheral handshakes, and interrupt signaling used by newer machines and SPI flash reads.

qemu/hw/dma/fh-axi-dmac.c

fh-gpio.cAdd GPIO ports and flash-select wiring support +236/-0

Add GPIO ports and flash-select wiring support

• Models port direction, pin levels, and port-A interrupts. Its output lines allow the machine to select SPI flash through the GPIO used by firmware.

qemu/hw/gpio/fh-gpio.c

fh-i2c.cAdd the Fullhan DesignWare-style I2C master +380/-0

Add the Fullhan DesignWare-style I2C master

• Models command-driven I2C transfers, receive FIFO, interrupt status, and target-address aborts needed for sensor probing.

qemu/hw/i2c/fh-i2c.c

galaxycore.cProvide configurable GalaxyCore sensor identification +141/-0

Provide configurable GalaxyCore sensor identification

• Adds an I2C register-file model supporting 8- or 16-bit addresses and configurable chip IDs, allowing sensor detection and initialization writes without pixel output.

qemu/hw/i2c/galaxycore.c

fh-intc.cAdd the 64-source Fullhan interrupt controller +221/-0

Add the 64-source Fullhan interrupt controller

• Models enable, mask, force, and status registers, including byte reads of FINALSTATUS required by the vendor kernel’s interrupt handler.

qemu/hw/intc/fh-intc.c

fh-aes.cAdd DMA-backed AES, DES, and 3DES operations +315/-0

Add DMA-backed AES, DES, and 3DES operations

• Uses QEMU ciphers for the engine’s ECB, CBC, CTR, CFB, and OFB modes, retaining chaining state across transfers and signaling completion.

qemu/hw/misc/fh-aes.c

fh-efuse.cModel blank Fullhan eFuses and command completion +161/-0

Model blank Fullhan eFuses and command completion

• Implements the eFuse command and status interface, including reads, one-way programming, and completed-operation reporting.

qemu/hw/misc/fh-efuse.c

fh-pmu.cProvide per-die identity, resets, and timer clocks +333/-0

Provide per-die identity, resets, and timer clocks

• Models PMU chip IDs, NOR boot strap, reset controls, watchdog pause, and PTS latch. PLL and divider settings drive a QEMU Clock consumed by the timers.

qemu/hw/misc/fh-pmu.c

fh-sadc.cAdd three Fullhan ADC register layouts +260/-0

Add three Fullhan ADC register layouts

• Models timed conversions and completion interrupts across three SoC layouts, with eight input levels exposed as runtime-settable properties.

qemu/hw/misc/fh-sadc.c

xm-dwmac.cMake the shared GMAC PHY identity and address configurable +12/-5

Make the shared GMAC PHY identity and address configurable

• Adds PHY ID and MDIO-address properties so Fullhan can present its expected IP101G at address zero while retaining existing defaults for other machines.

qemu/hw/net/xm-dwmac.c

fh-rtc.cAdd Fullhan command-based and FH8632 RTC layouts +304/-0

Add Fullhan command-based and FH8632 RTC layouts

• Models the newer command-accessed RTC and FH8632’s direct-register variant, with packed time derived from the configured RTC base.

qemu/hw/rtc/fh-rtc.c

fh-spi.cAdd the Fullhan SSI and SPIC flash data path +628/-0

Add the Fullhan SSI and SPIC flash data path

• Implements SSI transfers, Fullhan frame swapping and data window behavior, GPIO chip select, fast-read dummy clocks, and FIFO-driven DMA requests.

qemu/hw/ssi/fh-spi.c

fh-pts.cAdd FH8632’s free-running PTS clocksource +75/-0

Add FH8632’s free-running PTS clocksource

• Exposes the microsecond counter read by the FH8632 kernel as its clocksource.

qemu/hw/timer/fh-pts.c

fh-timer.cAdd PMU-clocked Fullhan APB timers +366/-0

Add PMU-clocked Fullhan APB timers

• Models periodic and free-running channels, shared interrupts, end-of-interrupt clearing, and rate changes from the PMU clock.

qemu/hw/timer/fh-timer.c

fh-wdt.cAdd the Fullhan DesignWare watchdog +252/-0

Add the Fullhan DesignWare watchdog

• Models sticky enable, timeout and kick behavior, interrupt-before-reset mode, and the PMU-controlled pause input.

qemu/hw/watchdog/fh-wdt.c

0014-dwc2-ghwcfg-props-host-descriptor-dma.patchSupport legacy Fullhan USB host descriptor DMA +349/-0

Support legacy Fullhan USB host descriptor DMA

• Adds configurable DWC2 synthesis registers and non-isochronous host descriptor DMA, including descriptor completion and frame-scheduled interrupt polling for legacy fh_otg kernels.

qemu/patches/0014-dwc2-ghwcfg-props-host-descriptor-dma.patch

Bug fix (2) +67 / -0
0012-arm-ccsidr-missing-cache-reads-l1d.patchPrevent an FH865x U-Boot cache-clean hang +31/-0

Prevent an FH865x U-Boot cache-clean hang

• Makes a missing cache’s CCSIDR read use L1 data-cache geometry instead of zero, accommodating the vendor bootloader’s stray cache selection.

qemu/patches/0012-arm-ccsidr-missing-cache-reads-l1d.patch

0013-m25p80-clear-wel-after-program-erase.patchClear NOR write enable after program and erase +36/-0

Clear NOR write enable after program and erase

• Corrects m25p80 write-enable-latch behavior so older Fullhan kernels do not time out flash operations or reject freshly erased JFFS2 blocks.

qemu/patches/0013-m25p80-clear-wel-after-program-erase.patch

Documentation (3) +411 / -2
CLAUDE.mdRecord Fullhan implementation and firmware compatibility notes +25/-0

Record Fullhan implementation and firmware compatibility notes

• Explains boot, interrupt, clock, SPI/DMA, and USB behaviors that affect work on the new machine family.

CLAUDE.md

README.mdList Fullhan machines and usage in the project overview +53/-2

List Fullhan machines and usage in the project overview

• Adds the ten machines to the supported count, summarizes boot support and limitations, and points readers to the new files and documentation.

README.md

fullhan.mdDocument Fullhan generations, boot chain, and device map +333/-0

Document Fullhan generations, boot chain, and device map

• Describes machine differences, memory and interrupt maps, firmware-image booting, device behavior, OpenIPC workflows, QEMU patches, and unmodelled media hardware.

docs/fullhan.md

Other (4) +953 / -0
ci.ymlAdd Fullhan machine, device, and OpenIPC boot CI checks +48/-0

Add Fullhan machine, device, and OpenIPC boot CI checks

• Checks that all ten machines are registered and runs the new device tests. Adds a two-SoC OpenIPC boot matrix covering login, DHCP, and persistent flash writes.

.github/workflows/ci.yml

test-fullhan-boot.shCheck OpenIPC login, networking, and flash persistence +115/-0

Check OpenIPC login, networking, and flash persistence

• Boots an OpenIPC kernel against a temporary NOR image and checks the serial login, DHCP lease, and JFFS2 overlay written back to flash.

qemu-boot/test-fullhan-boot.sh

setup.shRegister and build Fullhan machine and device sources +58/-0

Register and build Fullhan machine and device sources

• Copies the new sources into the QEMU tree and wires FULLHAN Kconfig and Meson entries, including the GalaxyCore sensor model.

qemu/setup.sh

test-fullhan.pyExercise Fullhan registers, DMA paths, crypto, and migration +732/-0

Exercise Fullhan registers, DMA paths, crypto, and migration

• Adds qtest coverage for boot staging, generation-specific peripherals, SPI-to-memory DMA, sensor identification, and AES test vectors. Also checks representative machine-state save and load.

qemu/tests/test-fullhan.py

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (5) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Five machines skip device smoke tests ✓ Resolved
Description
test_migration exercises only five of the ten newly registered Fullhan machines. The remaining
five registrations receive machine-help checks but no initialization or device-behavior test, so
wiring regressions in those machines can reach CI undetected.
Code

qemu/tests/test-fullhan.py[R676-679]

+def test_migration(qemu, tmp):
+    print('migration')
+    for m in ('fh8856v200', 'fh8652', 'fh8852v100', 'fh8626v100', 'fh8632'):
+        mig = os.path.join(tmp, m + '.mig')
Evidence
The checklist requires new machines to have applicable initialization and changed-device coverage.
The test suite's migration loop lists only five machines, while the CI machine-help loop separately
confirms ten registrations; therefore five newly added machines are not exercised beyond
registration.

CLAUDE.md: Test Machine Initialization and Changed Device Behavior: CLAUDE.md: Test Machine Initialization and Changed Device Behavior: CLAUDE.md: Test Machine Initialization and Changed Device Behavior: CLAUDE.md: Test Machine Initialization and Changed Device Behavior: CLAUDE.md: Test Machine Initialization and Changed Device Behavior: CLAUDE.md: Test Machine Initialization and Changed Device Behavior: CLAUDE.md: Test Machine Initialization and Changed Device Behavior: CLAUDE.md: Test Machine Initialization and Changed Device Behavior: CLAUDE.md: Test Machine Initialization and Changed Device Behavior: CLAUDE.md: Test Machine Initialization and Changed Device Behavior: CLAUDE.md: Test Machine Initialization and Changed Device Behavior: CLAUDE.md: Test Machine Initialization and Changed Device Behavior: CLAUDE.md: Test Machine Initialization and Changed Device Behavior: CLAUDE.md: Test Machine Initialization and Changed Device Behavior: CLAUDE.md: Test Machine Initialization and Changed Device Behavior: CLAUDE.md: Test Machine Initialization and Changed Device Behavior
qemu/tests/test-fullhan.py[676-679]
qemu/tests/test-fullhan.py[720-726]
.github/workflows/ci.yml[64-68]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The Fullhan PR registers ten machines, but the test suite exercises only five of them and does not initialize the other five in CI.
## Fix Focus Areas
- qemu/tests/test-fullhan.py[676-679]
- qemu/tests/test-fullhan.py[720-726]
## Recommended Fix
Add smoke or register-level initialization coverage for `fh8852v200`, `fh8858v200`, `fh8656`, `fh8658`, and `fh8856v100`, or parameterize the existing applicable checks so every registered machine starts successfully and reports initialization errors, assertions, and crashes.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Watchdog reset never follows its warning 🐞 Bug ≡ Correctness
Description
fh_wdt_expire() returns after the first interrupt-mode expiry without restarting its one-shot
timer. When response mode is set, no second callback reaches watchdog_perform_action() unless the
guest separately kicks or reconfigures the watchdog.
Code

qemu/hw/watchdog/fh-wdt.c[R89-92]

+    if ((s->cr & CR_RMOD) && !s->stat) {
+        s->stat = true;
+        fh_wdt_update(s);
+        return;
Evidence
The first callback returns without a timer restart, while the reset action requires another
callback; the timer is run as one-shot.

qemu/hw/watchdog/fh-wdt.c[74-96]
qemu/hw/watchdog/fh-wdt.c[126-148]
qemu/hw/watchdog/fh-wdt.c[187-197]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Interrupt-first watchdog mode stops after its warning instead of resetting on the next expiry.
## Fix Focus Areas
- qemu/hw/watchdog/fh-wdt.c[85-96]
## Recommended Fix
Rearm the timer for a second interval when the first expiry sets the interrupt status, preserving the second-expiry reset behavior.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Flash reads start before commands finish ✓ Resolved
Description
fh_spi_dr_write() clocks the entire EEPROM-mode receive phase immediately after each command-frame
write. When a command or address spans multiple frames, the first write clocks dummy data before the
remaining frames arrive and shifts the flash response.
Code

qemu/hw/ssi/fh-spi.c[R301-303]

+        fh_spi_xfer_frame(s, val);
+        s->rx_pending = s->ctrlr1 + 1;
+        fh_spi_rx_fill(s);
Evidence
Each write immediately sets rx_pending and calls the function that clocks pending frames, with no
command-phase queue or completion check.

qemu/hw/ssi/fh-spi.c[260-267]
qemu/hw/ssi/fh-spi.c[277-304]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
EEPROM-mode SPI reception begins after the first command frame, even if more command frames remain.
## Fix Focus Areas
- qemu/hw/ssi/fh-spi.c[277-304]
- qemu/hw/ssi/fh-spi.c[250-267]
## Recommended Fix
Track or queue the complete command phase and begin clocking receive frames only when its transmit frames have drained.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

4. Repeated-start reads retain the old transfer 🐞 Bug ≡ Correctness
Description
fh_i2c_data_cmd() calls i2c_start_transfer() for a repeated start without ending its active QEMU
I²C transfer. A write-then-repeated-start-read sequence therefore skips the target's transfer-end
handling; the other I²C controller explicitly performs that transition before starting again.
Code

qemu/hw/i2c/fh-i2c.c[R170-173]

+    if (s->xfer != want || (cmd & CMD_RESTART)) {
+        s->raw |= INTR_START_DET | INTR_ACTIVITY;
+        if (i2c_start_transfer(s->bus, addr, rd)) {
+            fh_i2c_abort(s, ABRT_7B_ADDR_NOACK);
Evidence
The restart branch does not end the prior transfer, whereas the existing controller's repeated-start
path does so explicitly.

qemu/hw/i2c/fh-i2c.c[142-177]
qemu/hw/i2c/sstar-i2c.c[117-135]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Repeated-start commands start another QEMU I²C transfer without closing the active one.
## Fix Focus Areas
- qemu/hw/i2c/fh-i2c.c[161-177]
## Recommended Fix
End an active bus transfer before calling `i2c_start_transfer()` for a direction change or explicit restart, and keep the controller's transfer state consistent on failure.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. Failed transfers appear to complete ✓ Resolved
Description
fh_dmac_item() and the AHB DMA transfer path discard the results of guest-memory reads and writes
before advancing their transfer counters. If an address cannot be read or written, either controller
can raise transfer-complete instead of reporting the bus error, and a failed read can send
zero-filled data onward.
Code

qemu/hw/dma/fh-axi-dmac.c[R212-218]

+    dma_memory_read(s->as, c->sar, buf, sw, MEMTXATTRS_UNSPECIFIED);
+    if (!(c->ctl_lo & CTL_SRC_FIX)) {
+        c->sar += sw;
+    }
+    if (!(c->ctl_lo & CTL_DST_FIX)) {
+        dma_memory_write(s->as, c->dar, buf, sw, MEMTXATTRS_UNSPECIFIED);
+        c->dar += sw;
Evidence
The AXI path ignores memory-access results and increments done; the AHB path likewise ignores
access results and proceeds to completion.

qemu/hw/dma/fh-axi-dmac.c[202-248]
qemu/hw/dma/fh-ahb-dmac.c[181-240]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Both DMA engines count failed guest-memory accesses as successful transfer items.
## Fix Focus Areas
- qemu/hw/dma/fh-axi-dmac.c[202-248]
- qemu/hw/dma/fh-ahb-dmac.c[181-240]
## Recommended Fix
Check every DMA read and write result in both engines; stop the affected transfer and set its appropriate error status rather than advancing or signaling completion.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


6. RTC status interrupts never reach the CPU 🐞 Bug ≡ Correctness
Description
fh_rtc_command() sets the core-idle status bit, but neither it nor the interrupt-enable write
drives the RTC's IRQ output. On machines that connect this output to the interrupt controller,
software enabling a completion interrupt cannot receive one.
Code

qemu/hw/rtc/fh-rtc.c[140]

+    s->int_status |= ST_CORE_IDLE;
Evidence
Command completion changes status and writes change the enable value, but the RTC only
initializes—never drives—its IRQ output, which the machine connects.

qemu/hw/rtc/fh-rtc.c[116-141]
qemu/hw/rtc/fh-rtc.c[200-213]
qemu/hw/rtc/fh-rtc.c[253-261]
qemu/hw/arm/fullhan.c[720-728]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The RTC updates interrupt status and enable registers but never updates its connected IRQ line.
## Fix Focus Areas
- qemu/hw/rtc/fh-rtc.c[116-141]
- qemu/hw/rtc/fh-rtc.c[191-213]
## Recommended Fix
Update the IRQ level from the supported pending and enabled status bits after commands and relevant register writes or clears.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View review recommended (5)
7. Timers start at half their intended rate 🐞 Bug ≡ Correctness
Description
fh_pmu_reset() installs CLK_DIV5 = 0x0B63C700 for PLL schemes 0 and 1, which
fh_pmu_update_clocks() divides by 12 and then 200. With the reset 1.2 GHz PLL, the connected timer
receives 500 kHz rather than the reset comment's 1 MHz, doubling guest timer periods until
reprogrammed.
Code

qemu/hw/misc/fh-pmu.c[R264-265]

+    /* sys / 12 = 100 MHz; timers 100 / 100, the watchdog 100 / 200 */
+    s->regs[PMU_CLK_DIV5 >> 2] = 0x0B63C700;
Evidence
The PLL reset value yields 1.2 GHz; the divider expression and value yield 1.2 GHz / 12 / 200, and
the machine wires this clock directly into the timer.

qemu/hw/misc/fh-pmu.c[115-137]
qemu/hw/misc/fh-pmu.c[149-160]
qemu/hw/misc/fh-pmu.c[256-267]
qemu/hw/arm/fullhan.c[743-750]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The PMU reset divider produces a 500 kHz timer clock where the modeled reset rate is 1 MHz.
## Fix Focus Areas
- qemu/hw/misc/fh-pmu.c[149-160]
- qemu/hw/misc/fh-pmu.c[256-267]
## Recommended Fix
Set reset divider fields that yield the intended timer frequency for schemes 0 and 1, and verify the result through the connected timer clock.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


8. Fuse checks ignore programmed bits 🐞 Bug ≡ Correctness
Description
fh_efuse_cmd() sets both programmable-bit status registers to all ones for every OP_CHECK,
regardless of s->fuse. After an OP_WRITE sets bits permanently in the modeled fuse array, a
subsequent check still reports the blank-device map to guest software.
Code

qemu/hw/misc/fh-efuse.c[R67-70]

+    case OP_CHECK:
+        s->regs[R_STATUS1 >> 2] = 0xFFFFFFFF;
+        s->regs[R_STATUS2 >> 2] = 0xFFFFFFFF;
+        break;
Evidence
Programming persists bits with OR, while the documented programmable-bit map is populated without
consulting that stored value.

qemu/hw/misc/fh-efuse.c[5-16]
qemu/hw/misc/fh-efuse.c[56-70]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The eFuse check operation reports every bit as programmable after programming.
## Fix Focus Areas
- qemu/hw/misc/fh-efuse.c[56-75]
## Recommended Fix
Compute the check status registers from the requested fuse entries and their stored programmed bits instead of assigning constant all-ones values.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


9. Isochronous USB transfers use wrong buffers ✓ Resolved
Description
dwc2_handle_packet() enters descriptor-DMA processing whenever HCFG_DESCDMA is set, without
excluding isochronous endpoints. On an isochronous channel, it reads and writes a non-isochronous
descriptor layout and substitutes its buffer and length for the programmed transfer, despite this
implementation supporting only non-isochronous lists.
Code

qemu/patches/0014-dwc2-ghwcfg-props-host-descriptor-dma.patch[R94-95]

++    ddma = s->hcfg & HCFG_DESCDMA;
++    if (ddma) {
Evidence
The new descriptor branch is gated only by the global descriptor-DMA bit and reads descriptor words
before any endpoint-type exclusion, although its own comment restricts the layout to non-isochronous
lists.

qemu/patches/0014-dwc2-ghwcfg-props-host-descriptor-dma.patch[83-105]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Host descriptor DMA processes isochronous channels using a non-isochronous descriptor format.
## Fix Focus Areas
- qemu/patches/0014-dwc2-ghwcfg-props-host-descriptor-dma.patch[83-105]
## Recommended Fix
Guard the non-isochronous descriptor path by endpoint type and handle unsupported isochronous descriptor DMA explicitly without interpreting its buffer as this descriptor format.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


10. A guest's zero-length DMA chain hangs the emulator ✓ Resolved
Description
fh_ahb_run() decrements budget only after moving an item, but a block with BLOCK_TS == 0 goes
straight to fh_ahb_block_done(), which calls fh_ahb_fetch() and loops again without spending any
budget. If the guest builds a linked list whose descriptors point back to themselves (or form a
cycle) with zero-length blocks and LLP enabled, the bottom half never returns, so the whole QEMU
main loop hangs.
Code

qemu/hw/dma/fh-ahb-dmac.c[R230-234]

+    while (c->en && c->active && budget) {
+        if (c->done >= (c->ctl_hi & 0xFFF)) {
+            fh_ahb_block_done(s, c, n);
+            continue;
+        }
Evidence
When done >= (ctl_hi & 0xFFF), which holds immediately if the field is 0, the loop calls
fh_ahb_block_done and then continue. fh_ahb_block_done re-fetches from the guest-controlled
llp, and fh_ahb_fetch sets active = true and done = 0. budget-- is reached only on the
item-transfer path, so a self-referencing zero-length LLI loops forever.

qemu/hw/dma/fh-ahb-dmac.c[226-244]
qemu/hw/dma/fh-ahb-dmac.c[132-158]
qemu/hw/dma/fh-ahb-dmac.c[205-224]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
In `fh_ahb_run`, a zero-length block followed by a cyclic LLI chain loops forever, because the budget is only decremented per transferred item.
## Fix Focus Areas
- qemu/hw/dma/fh-ahb-dmac.c[226-244]
## Recommended Fix
Decrement `budget` (or a separate per-run block counter) in the block-done branch as well, before `continue`. When the budget runs out, the existing code reschedules the BH, so the transfer continues later without starving the main loop.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


11. A guest-set crypto length can abort the emulator ✓ Resolved
Description
fh_aes_run() takes the guest-programmed 32-bit R_DMA_SIZE and passes it straight to two
g_malloc(len) calls, with no upper bound. A guest that writes a value near 0xFFFFFFFF and starts
DMA makes QEMU try to allocate about 8 GiB, and g_malloc aborts the process when that allocation
fails.
Code

qemu/hw/misc/fh-aes.c[R189-192]

+    if (c && len) {
+        len -= len % bs;
+        in = g_malloc(len);
+        out = g_malloc(len);
Evidence
len comes from s->regs[R_DMA_SIZE], which the guest writes. It is only rounded down to a whole
block before in = g_malloc(len); out = g_malloc(len);.

qemu/hw/misc/fh-aes.c[174-201]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`fh_aes_run` allocates two heap buffers sized by the guest-controlled DMA length, so a guest can force allocations of several gigabytes and abort QEMU.
## Fix Focus Areas
- qemu/hw/misc/fh-aes.c[174-201]
## Recommended Fix
Work through the transfer in a fixed-size stack or heap chunk (for example 4 KiB, a multiple of the block size). Read one chunk, encrypt or decrypt its blocks, write it back, and advance src and dst until `len` is used up. Alternatively, cap `len` at a sane maximum and log a LOG_GUEST_ERROR when the guest asks for more.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can enable the Remediation agent and Qodo fixes findings in a dedicated fix PR

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread qemu/tests/test-fullhan.py
Comment thread qemu/hw/watchdog/fh-wdt.c
Comment thread qemu/hw/ssi/fh-spi.c Outdated
Comment thread qemu/hw/i2c/fh-i2c.c
Comment on lines +170 to +173
if (s->xfer != want || (cmd & CMD_RESTART)) {
s->raw |= INTR_START_DET | INTR_ACTIVITY;
if (i2c_start_transfer(s->bus, addr, rd)) {
fh_i2c_abort(s, ABRT_7B_ADDR_NOACK);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

4. Repeated-start reads retain the old transfer 🐞 Bug ≡ Correctness

fh_i2c_data_cmd() calls i2c_start_transfer() for a repeated start without ending its active QEMU
I²C transfer. A write-then-repeated-start-read sequence therefore skips the target's transfer-end
handling; the other I²C controller explicitly performs that transition before starting again.
Agent Prompt
## Issue description
Repeated-start commands start another QEMU I²C transfer without closing the active one.
## Fix Focus Areas
- qemu/hw/i2c/fh-i2c.c[161-177]
## Recommended Fix
End an active bus transfer before calling `i2c_start_transfer()` for a direction change or explicit restart, and keep the controller's transfer state consistent on failure.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Partly: QEMU's I2C core treats a start on a busy bus as a repeated start by design (no END in between, matching I2C, where there is no STOP), so that is intended. The real gap was a repeated start to a different address keeping the old target; fixed in 3de1e4e by ending the first transfer in that case.

Comment thread qemu/hw/dma/fh-axi-dmac.c Outdated
Comment thread qemu/hw/misc/fh-pmu.c
Comment thread qemu/hw/misc/fh-efuse.c
Comment thread qemu/patches/0014-dwc2-ghwcfg-props-host-descriptor-dma.patch
Comment thread qemu/hw/dma/fh-ahb-dmac.c
Comment thread qemu/hw/misc/fh-aes.c Outdated
…reads

* fh-axi-dmac / fh-ahb-dmac: a failed read or write stops the channel
  with SRC_DEC_ERR / DST_DEC_ERR (AXI) or ERR (AHB) instead of carrying
  on to transfer-complete; every loop iteration spends the yield budget,
  so a cycle of zero-length LLIs no longer pins the main loop.
* fh-spi: in EEPROM mode the response is clocked in once the command
  phase is over (the next register read, or 10 us after the last command
  frame), not between the frames of a multi-frame command.
* fh-i2c: a repeated start to another address ends the transfer with the
  first target before addressing the second.
* fh-aes: transfers move through a fixed 4 KiB buffer, not one sized by
  the guest's DMA length.
* dwc2 (patch 0014): isochronous channels under descriptor DMA are
  refused (LOG_UNIMP, XactErr) rather than read with the non-isochronous
  descriptor layout.
* test-fullhan.py: every machine's chip / package IDs and a migration
  round trip for all ten.
…ckend

CI builds QEMU without gnutls, nettle or gcrypt, and the stub backend
refuses every cipher: the engine moved no data and test-fullhan.py's AES
checks read back zeros.  AES now uses crypto/aes.h, which QEMU always
builds; DES and 3DES still need a backend and are logged as unimplemented
without one, and the qtest skips the DES check when the build has none.
@dimerr
dimerr merged commit 4748eee into OpenIPC:master Oct 25, 2026
25 checks passed
@dimerr
dimerr deleted the fullhan branch October 25, 2026 20:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant