#!/usr/bin/openrc-run

. /opt/etc/functions.openrc

# TODO: code cleanup

SAV=/var/run/vfio-bind.saved.drivers

extra_commands="restore_drivers"

depend()
{
	need procfs sysfs
	provide ${SVCNAME}
}

IOMMU_ACTIVE=2
iommu_active()
{
	if [ ${IOMMU_ACTIVE} -ge 2 ]; then
		IOMMU_ACTIVE=1
		if [ -d /sys/kernel/iommu_groups ]; then
			[ $(find /sys/kernel/iommu_groups -type l | wc -l) -gt 0 ] && IOMMU_ACTIVE=0
		fi
	fi
	return ${IOMMU_ACTIVE}
}

save_drivers()
{
	if [ -f ${SAV} ]; then
		ewarn "Removing list of driver bindings \"${SAV}\""
		rm ${SAV}
	fi
	for DEV in ${VFIO_PCI_DEVS}; do
		DEVPATH=/sys/bus/pci/devices/${DEV}
		if [ -e ${DEVPATH} ]; then
			if [ -e ${DEVPATH}/driver ]; then
				# Here comes black magick ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
				# Total time consumption for these three lines: 3 hours.
				# Thanks to Linux's sysfs developers.
				# Please, do NOT touch anything.
				WRD=$(readlink ${DEVPATH}/driver)
				if ! (cd ${DEVPATH}/${WRD} > /dev/null 2>&1); then
					cd ${DEVPATH}/../${WRD} > /dev/null 2>&1
				else
					cd ${DEVPATH}/${WRD} > /dev/null 2>&1
				fi
				DRV=$(pwd)
				# EOBM ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
				VEN=$(cat ${DEVPATH}/vendor)
				ID=$(cat ${DEVPATH}/device)
				echo "${DEV} ${DRV}" >> ${SAV}
				einfo "Saved binding to \"${DRV}\" for PCI device ${DEV} [${VEN}:${ID}]"
			fi
		else
			eerror "Unknown device ${DEV}"
		fi
	done
}

restore_drivers()
{
	[ -f ${SAV} ] || return 1
	for DEV in ${VFIO_PCI_DEVS}; do
		if [ -e /sys/bus/pci/devices/${DEV}/driver ]; then
			DRV="$(basename $(readlink /sys/bus/pci/devices/${DEV}/driver))"
			if [ "${DRV}" == "vfio-pci" ]; then
				echo ${DEV} > /sys/bus/pci/devices/${DEV}/driver/unbind
			else
				ewarn "Device ${DEV} is bound to ${DRV} and not to vfio-pci"
				continue
			fi
		fi
		DRV=$(cat ${SAV} | grep ^${DEV} | awk '{print $2;}')
		if [ "${DRV}" != "" ]; then
			echo ${DEV} > ${DRV}/bind
			VEN=$(cat /sys/bus/pci/devices/${DEV}/vendor)
			ID=$(cat /sys/bus/pci/devices/${DEV}/device)
			einfo "PCI device ${DEV} [${VEN}:${ID}] is bound to ${DRV}"
		fi
	done
}

start()
{
	ebegin "Starting \"${SVCNAME}\""
	if ! iommu_active; then
		eerror "IOMMU is inactive!"
		eerror "Check hardware, kernel config and kernel command line for VT-d/AMD-Vi."
		eend 1
		return 1
	fi
	local rv=0
	eindent
	save_drivers
	for DEV in ${VFIO_PCI_DEVS}; do
		DEVPATH=/sys/bus/pci/devices/${DEV}
		if [ -e ${DEVPATH} ]; then
			if [ -e ${DEVPATH}/driver ]; then
				echo ${DEV} > ${DEVPATH}/driver/unbind
			fi
			VEN=$(cat ${DEVPATH}/vendor)
			ID=$(cat ${DEVPATH}/device)
			echo $VEN $ID > /sys/bus/pci/drivers/vfio-pci/new_id
			if [ -e ${DEVPATH}/driver ]; then
				DRV="$(basename $(readlink ${DEVPATH}/driver))"
				if [ "${DRV}" != "vfio-pci" ]; then
					eerror "PCI device ${DEV} [${VEN}:${ID}] is still bound to ${DRV}, not to vfio_pci"
					eend 1
					rv=1
				else
					einfo "PCI device ${DEV} [${VEN}:${ID}] is bound to vfio_pci"
				fi
			else
				ewarn "PCI device ${DEV} [${VEN}:${ID}] is bound to nothing, not to vfio_pci"
			fi
		else
			eerror "Unknown device ${DEV}"
			eend 1
			rv=1
		fi
	done
	eoutdent
	eend ${rv}
	return ${rv}
}

stop()
{
	ebegin "Stopping service \"${SVCNAME}\""
	restore_drivers
	eend 0
	return 0
}
