From a13ab5a35bc0435fcfdacb3e171ceedcc077573d Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 27 Mar 2015 15:48:11 -0400 Subject: AHCI: Do not (re)map FB/CLB buffers while not running The FIS Receive Buffer and Command List Buffer pointers should not be edited while the FIS receive engine or Command Receive engines are running. Currently, we attempt to re-map the buffers every time they are adjusted, but while the AHCI engines are off, these registers may contain stale values, so we should not attempt to re-map these values until the engines are reactivated. Reported-by: Jordan Hargrave Signed-off-by: John Snow Reviewed-by: Stefan Hajnoczi Message-id: 1426283454-15590-2-git-send-email-jsnow@redhat.com --- hw/ide/ahci.c | 50 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 17 deletions(-) (limited to 'hw') diff --git a/hw/ide/ahci.c b/hw/ide/ahci.c index 7a223bede7..4ccfcf3311 100644 --- a/hw/ide/ahci.c +++ b/hw/ide/ahci.c @@ -51,6 +51,8 @@ static void ahci_write_fis_d2h(AHCIDevice *ad, uint8_t *cmd_fis); static void ahci_init_d2h(AHCIDevice *ad); static int ahci_dma_prepare_buf(IDEDMA *dma, int is_write); static void ahci_commit_buf(IDEDMA *dma, uint32_t tx_bytes); +static bool ahci_map_clb_address(AHCIDevice *ad); +static bool ahci_map_fis_address(AHCIDevice *ad); static uint32_t ahci_port_read(AHCIState *s, int port, int offset) @@ -202,25 +204,15 @@ static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val) switch (offset) { case PORT_LST_ADDR: pr->lst_addr = val; - map_page(s->as, &s->dev[port].lst, - ((uint64_t)pr->lst_addr_hi << 32) | pr->lst_addr, 1024); - s->dev[port].cur_cmd = NULL; break; case PORT_LST_ADDR_HI: pr->lst_addr_hi = val; - map_page(s->as, &s->dev[port].lst, - ((uint64_t)pr->lst_addr_hi << 32) | pr->lst_addr, 1024); - s->dev[port].cur_cmd = NULL; break; case PORT_FIS_ADDR: pr->fis_addr = val; - map_page(s->as, &s->dev[port].res_fis, - ((uint64_t)pr->fis_addr_hi << 32) | pr->fis_addr, 256); break; case PORT_FIS_ADDR_HI: pr->fis_addr_hi = val; - map_page(s->as, &s->dev[port].res_fis, - ((uint64_t)pr->fis_addr_hi << 32) | pr->fis_addr, 256); break; case PORT_IRQ_STAT: pr->irq_stat &= ~val; @@ -234,11 +226,21 @@ static void ahci_port_write(AHCIState *s, int port, int offset, uint32_t val) pr->cmd = val & ~(PORT_CMD_LIST_ON | PORT_CMD_FIS_ON); if (pr->cmd & PORT_CMD_START) { - pr->cmd |= PORT_CMD_LIST_ON; + if (ahci_map_clb_address(&s->dev[port])) { + pr->cmd |= PORT_CMD_LIST_ON; + } else { + error_report("AHCI: Failed to start DMA engine: " + "bad command list buffer address"); + } } if (pr->cmd & PORT_CMD_FIS_RX) { - pr->cmd |= PORT_CMD_FIS_ON; + if (ahci_map_fis_address(&s->dev[port])) { + pr->cmd |= PORT_CMD_FIS_ON; + } else { + error_report("AHCI: Failed to start FIS receive engine: " + "bad FIS receive buffer address"); + } } /* XXX usually the FIS would be pending on the bus here and @@ -565,6 +567,23 @@ static void debug_print_fis(uint8_t *fis, int cmd_len) #endif } +static bool ahci_map_fis_address(AHCIDevice *ad) +{ + AHCIPortRegs *pr = &ad->port_regs; + map_page(ad->hba->as, &ad->res_fis, + ((uint64_t)pr->fis_addr_hi << 32) | pr->fis_addr, 256); + return ad->res_fis != NULL; +} + +static bool ahci_map_clb_address(AHCIDevice *ad) +{ + AHCIPortRegs *pr = &ad->port_regs; + ad->cur_cmd = NULL; + map_page(ad->hba->as, &ad->lst, + ((uint64_t)pr->lst_addr_hi << 32) | pr->lst_addr, 1024); + return ad->lst != NULL; +} + static void ahci_write_fis_sdb(AHCIState *s, int port, uint32_t finished) { AHCIDevice *ad = &s->dev[port]; @@ -1360,12 +1379,9 @@ static int ahci_state_post_load(void *opaque, int version_id) for (i = 0; i < s->ports; i++) { ad = &s->dev[i]; - AHCIPortRegs *pr = &ad->port_regs; - map_page(s->as, &ad->lst, - ((uint64_t)pr->lst_addr_hi << 32) | pr->lst_addr, 1024); - map_page(s->as, &ad->res_fis, - ((uint64_t)pr->fis_addr_hi << 32) | pr->fis_addr, 256); + ahci_map_clb_address(ad); + ahci_map_fis_address(ad); /* * If an error is present, ad->busy_slot will be valid and not -1. * In this case, an operation is waiting to resume and will re-check -- cgit v1.2.3