@bogikornel As discussed in the thread, pygrub doesn't work.
(It might be fixable, because copying kernel and initrd to dom-0 and directly setting PV-kernel, PV-ramdisk and PV-args works, which is basically what pygrub is supposed to do. But I stopped investigating as pvhgrub is a much better option which acually works.)
You have 3 options
copy kernel and initrd do dom-0 and configure
xe vm-param-set uuid=... domain-type=pvh
xe vm-param-set uuid=... PV-kernel=/dom-0/path/to/kernel
xe vm-param-set uuid=... PV-ramdisk=/dom-0/path/to/initrd
xe vm-param-set uuid=... PV-args="root=... ro console=hvc0 ..."
xe vm-param-clear uuid=... param-name=PV-bootloader
Not a very practical option, just PoC.
use pvh-ovmf, but this requires UEFI-enabled VM (ie. GPT disk layout with EFI partition and some EFI bootloader or kernel directly in EFI with proper config (or as UKI)
use pvhgrub. You need a recent grub2 to build the image for i386-xen_pvh target. Or just get the blob - this one is from Alpine package
curl https://dl-cdn.alpinelinux.org/alpine/edge/main/x86_64/grub-xenhost-2.12-r8.apk | tar -xzf - --strip-components=3 usr/lib/grub-xen/grub-i386-xen_pvh.bin
Save it to dom-0 (e.g. to /var/lib/xcp/guest/grub-i386-xen_pvh.bin) and configure the VM
xe vm-param-set uuid=... domain-type=pvh
xe vm-param-set uuid=... PV-kernel=/var/lib/xcp/guest/grub-i386-xen_pvh.bin
xe vm-param-clear uuid=... param-name=PV-ramdisk
xe vm-param-clear uuid=... param-name=PV-args
xe vm-param-clear uuid=... param-name=PV-bootloader
If the VM has valid grub2 config, it should work. Of course, you need a linux kernel with CONFIG_XEN_PVH enabled.