#!/bin/bash
set -e -o pipefail

exec 3>&2 2>&1

arch=$1
nic=$2

echo "Testing efi-$nic.rom on $arch."

fw_file="/usr/share/qemu/efi-${nic}.rom"

if [ "$nic" = "virtio" ]; then
    nic="virtio-net-pci"
elif [ "$nic" = "eepro100" ]; then
    nic="i82550"
fi

if [ -f "$fw_file" ]; then
    echo "Using $fw_file"
else
    echo "No $fw_file found" >&3
    exit 1
fi

case "$nic" in
    e1000) nic_vid=$((0x8086)); nic_pid=$((0x100e));;
    e1000e) nic_vid=$((0x8086)); nic_pid=$((0x10d3));;
    i82550) nic_vid=$((0x8086)); nic_pid=$((0x1209));;
    ne2k_pci) nic_vid=$((0x10ec)); nic_pid=$((0x8029));;
    pcnet) nic_vid=$((0x1022)); nic_pid=$((0x2000));;
    rtl8139) nic_vid=$((0x10ec)); nic_pid=$((0x8139));;
    virtio-net-pci) nic_vid=$((0x1af4)); nic_pid=$((0x1000));;
    vmxnet3) nic_vid=$((0x15ad)); nic_pid=$((0x07b0));;
    *) echo "Unknown NIC $nic" >&3; exit 1;;
esac

cp -v "$fw_file" "${AUTOPKGTEST_TMP}/romfile.rom"
fw_file="${AUTOPKGTEST_TMP}/romfile.rom"

# Fix PCI IDs in the ROM file
# See pci_patch_ids() in hw/pci/pci.c of QEMU

slice_bytes () {
    local file="$1"
    local offset="$2"
    local size="$3"
    tail --bytes "+$((offset + 1))" "$file" | head --bytes "$size" | xxd -p
}

get_le16 () {
    local file="$1"
    local offset="$2"
    echo "$((0x$(slice_bytes "$file" "$offset" 2 | tr -d '\n' | tac -rs ..)))"
}

get_ch () {
    local file="$1"
    local offset="$2"
    echo -n "$((0x$(slice_bytes "$file" "$offset" 1)))"
}

write_ch () {
    local file="$1"
    local offset="$2"
    local value="$3"
    printf "%02x" "$(($value & 0xff))" | xxd -r -p | dd of="$file" bs=1 seek="$offset" conv=notrunc status=none
}

write_le16 () {
    local file="$1"
    local offset="$2"
    local value="$3"
    printf "%02x%02x" "$(($value & 0xff))" "$((($value >> 8) & 0xff))" | xxd -r -p | dd of="$file" bs=1 seek="$offset" conv=notrunc status=none
}

checksum_16 () {
    local data="$1"
    echo "$(((($data & 0xff) + (($data >> 8) & 0xff)) & 0xff ))"
}

if [ "55aa" != "$(slice_bytes "$fw_file" 0 2)" ]; then
    echo "Invalid ROM file, incorrect signature" >&3
    exit 1
fi

pcir_offset=$(get_le16 "$fw_file" "$((0x18))")

if [ "PCIR" != "$(slice_bytes "$fw_file" "$(($pcir_offset + 0))" 4 | xxd -r -p)" ]; then
    echo "Invalid ROM file, incorrect PCIR signature" >&3
    exit 1
fi

rom_vendor=$(get_le16 "$fw_file" "$(($pcir_offset + 4))")
rom_device=$(get_le16 "$fw_file" "$(($pcir_offset + 6))")

printf "ROM VID:PID %04x:%04x\n" "$rom_vendor" "$rom_device"

checksum=$(get_ch "$fw_file" 6)
checksum=$(($checksum + $(checksum_16 "$rom_vendor") + $(checksum_16 "$rom_device")))
checksum=$(($checksum + (0x100 - $(checksum_16 "$nic_vid")) + (0x100 - $(checksum_16 "$nic_pid"))))
checksum=$(($checksum & 0xff))

write_ch "$fw_file" 6 "$checksum"
write_le16 "$fw_file" "$(($pcir_offset + 4))" "$nic_vid"
write_le16 "$fw_file" "$(($pcir_offset + 6))" "$nic_pid"

. debian/tests/qemu-cmd

QEMU_CMD+=(-netdev user,id=net0,tftp="${AUTOPKGTEST_TMP}",bootfile=__nonexistent__)
QEMU_CMD+=(-device "$nic",netdev=net0,id=net0,romfile="$fw_file")

expect -- - "$DEB_VERSION" "${QEMU_CMD[@]}"<< 'EOF'
set timeout 120

proc abort {msg} {
  set ferr [open "/dev/fd/3" "w"]
  puts $ferr $msg
  close $ferr
  exit 0
}

set deb_version [lindex $argv 0]

spawn {*}[lrange $argv 1 end]

proc selectMenu {entry} {
  set last_row ""
  for {set i 0} {$i < 20} {incr i} {
    expect {
      -re {\e\[0m\e\[37m\e\[40m\e\[(\d+);\d+H([ -~]+)\e\[0m} {
        if {[regexp "$entry" "$expect_out(2,string)"]} {
          send "\r"
          return
        } else {
          if {"$expect_out(1,string)" != "$last_row"} {
            send "\033\[B"
            set last_row $expect_out(1,string)
          }
        }
      }
      -re {\e\[(\d+);\d+H[^;]*\e\[0m\e\[37m\e\[40m(\e\[\d+C)*([ -~]+)\e\[0m} {
        if {[regexp "$entry" "$expect_out(3,string)"]} {
          send "\r"
          return
        } else {
          if {"$expect_out(1,string)" != "$last_row"} {
            send "\033\[B"
            set last_row $expect_out(1,string)
          }
        }
      }
      eof {
        abort "VM terminates unexpectedly"
      }
      timeout {
        abort "Timeout"
      }
    }
  }
  abort "Cannot find $entry"
}

set timeout 240

set ipxe_version ""

expect {
  "UEFI firmware" {}
  "Shell>" {
    send "exit\r"
  }
  -re {\e\[0;1miPXE (.*)\e\[0m -- Open Source} {
    set ipxe_version $expect_out(1,string)
  }
  eof {
    puts "VM terminates unexpectedly"
    exit 2
  }
  timeout {
    puts "Timeout"
    exit 2
  }
}

if {"$ipxe_version" == ""} {

  set timeout 120

  send "\033\033\033\033"
  selectMenu "Device Manager"
  selectMenu "Network Device List"
  selectMenu "MAC:.*"
  selectMenu "iPXE.*"
  selectMenu "Version"

  expect {
    -re {\e\[30m\e\[47m(\e\[\d+;\d+H)*(.+)\e\[0m} {
      set ipxe_version $expect_out(2,string)
    }
    eof {
      abort "VM terminates unexpectedly"
    }
    timeout {
      abort "Timeout"
    }
  }

  set ipxe_version [regsub -all {\e\[[^a-zA-Z]+[a-zA-Z]} $ipxe_version ""]
  set ipxe_version [regsub -all {[ \t]+} $ipxe_version ""]
}

if {"$ipxe_version" != "$deb_version"} {
  abort "Version mismatch: expected $deb_version, got $ipxe_version"
}

exit 0
EOF
