4GB境界を跨ぐメモリをvm1に割り当てると、vm1の再起動時にAHCIがタイムアウトする
AHCIドライバが実行するコマンドのアドレスと、IOMMUがページフォールトしたアドレスが一致していた。
AHCI controller at a0.0, iobase f7c00000, irq 255 AHCI/1: send cmd ... cmd 0x0xcefe3c00 buffer 0x0x00006ba4 > sendint sendint> vtddump send 0 to vtddump VT-d fault status 0x00000000 VT-d fault status 0x00000003 VT-d fault record[0] 0x8000000500000500_00000001CEFE3000
--- bios/src/ahci.c (リビジョン 108) +++ bios/src/ahci.c (作業コピー) @@ -135,7 +135,7 @@ SET_LOWFLAT(list[0].base, ((u32)(cmd))); SET_LOWFLAT(list[0].baseu, 0); - dprintf(8, "AHCI/%d: send cmd ...\n", pnr); + dprintf(1, "AHCI/%d: send cmd ... cmd 0x%p buffer 0x%p\n", pnr, cmd, buffer); intbits = ahci_port_readl(ctrl, pnr, PORT_IRQ_STAT); if (intbits) ahci_port_writel(ctrl, pnr, PORT_IRQ_STAT, intbits);
yuichi_xy への返信
AHCIドライバが実行するコマンドのアドレスと、IOMMUがページフォールトしたアドレスが一致していた。
一致していると思ったら、不一致だった。 AHCIドライバが実行するコマンドのアドレスは 0xcefe3c00 で、IOMMU がページフォールトしたアドレスは 0x1cefe3000 だった。 IOMMU がページフォールトしたアドレスが 4GB 超えのアドレスになった原因を調査する必要がある。
{{{ AHCI controller at a0.0, iobase f7c00000, irq 255 AHCI/1: send cmd ... cmd 0x0xcefe3c00 buffer 0x0x00006ba4
sendint
sendint> vtddump send 0 to vtddump VT-d fault status 0x00000000 VT-d fault status 0x00000003 VT-d fault record0 0x8000000500000500_00000001CEFE3000 }}} {{{ --- bios/src/ahci.c (リビジョン 108) +++ bios/src/ahci.c (作業コピー) @@ -135,7 +135,7 @@ SET_LOWFLAT(list0.base, ((u32)(cmd))); SET_LOWFLAT(list0.baseu, 0); - dprintf(8, "AHCI/%d: send cmd ...\n", pnr); + dprintf(1, "AHCI/%d: send cmd ... cmd 0x%p buffer 0x%p\n", pnr, cmd, buffer); intbits = ahci_port_readl(ctrl, pnr, PORT_IRQ_STAT); if (intbits) ahci_port_writel(ctrl, pnr, PORT_IRQ_STAT, intbits); }}}
コマンドのアドレスは、コマンドリストに格納されるが、上位32ビットを示す baseu は 0 に設定されている。
ahci.c
// submit ahci command + wait for result static int ahci_command(struct ahci_port_s *port, int iswrite, int isatapi, void *buffer, u32 bsize) { u32 val, status, success, flags, intbits, error; struct ahci_ctrl_s *ctrl = GET_GLOBAL(port->ctrl); struct ahci_cmd_s *cmd = GET_GLOBAL(port->cmd); struct ahci_fis_s *fis = GET_GLOBAL(port->fis); struct ahci_list_s *list = GET_GLOBAL(port->list); u32 pnr = GET_GLOBAL(port->pnr); u64 end; SET_LOWFLAT(cmd->fis.reg, 0x27); SET_LOWFLAT(cmd->fis.pmp_type, (1 << 7)); /* cmd fis */ SET_LOWFLAT(cmd->prdt[0].base, ((u32)buffer)); SET_LOWFLAT(cmd->prdt[0].baseu, 0); SET_LOWFLAT(cmd->prdt[0].flags, bsize-1); flags = ((1 << 16) | /* one prd entry */ (iswrite ? (1 << 6) : 0) | (isatapi ? (1 << 5) : 0) | (5 << 0)); /* fis length (dwords) */ SET_LOWFLAT(list[0].flags, flags); SET_LOWFLAT(list[0].bytes, 0); SET_LOWFLAT(list[0].base, ((u32)(cmd))); SET_LOWFLAT(list[0].baseu, 0); <-------- ここ
AHCI ドライバのソースコードを調査していたところ、 BIOS の AHCI ドライバが、コマンドリストや FIS のアドレスを設定する時に、上位 32 ビットのアドレスを設定していないことに気づきました。
r110 で 0 に設定するようにしたところ、 AHCI のタイムアウトが発生しなくなりました。
vm1 の再起動時、 Secondary Bus Reset を発行しているので、コマンドリストや FIS のアドレスもクリアされると考えていたのですが、クリアされないようです。
4GB境界を跨ぐメモリをvm1に割り当てると、vm1の再起動時にAHCIがタイムアウトする。