e1000e_write_packet_to_guest(E1000ECore *core, struct NetRxPkt *pkt,

                             const E1000E_RxRing *rxr,

                             const E1000E_RSSInfo *rss_info)

{

    PCIDevice *d = core->owner;

    dma_addr_t base;

    uint8_t desc[E1000_MAX_RX_DESC_LEN];

    size_t desc_size;

    size_t desc_offset = 0;

    size_t iov_ofs = 0;



    struct iovec *iov = net_rx_pkt_get_iovec(pkt);

    size_t size = net_rx_pkt_get_total_len(pkt);

    size_t total_size = size + e1000x_fcs_len(core->mac);

    const E1000E_RingInfo *rxi;

    size_t ps_hdr_len = 0;

    bool do_ps = e1000e_do_ps(core, pkt, &ps_hdr_len);



    rxi = rxr->i;



    do {

        hwaddr ba[MAX_PS_BUFFERS];

        e1000e_ba_state bastate = { { 0 } };

        bool is_last = false;

        bool is_first = true;



        desc_size = total_size - desc_offset;



        if (desc_size > core->rx_desc_buf_size) {

            desc_size = core->rx_desc_buf_size;

        }



        base = e1000e_ring_head_descr(core, rxi);



        pci_dma_read(d, base, &desc, core->rx_desc_len);



        trace_e1000e_rx_descr(rxi->idx, base, core->rx_desc_len);



        e1000e_read_rx_descr(core, desc, &ba);



        if (ba[0]) {

            if (desc_offset < size) {

                static const uint32_t fcs_pad;

                size_t iov_copy;

                size_t copy_size = size - desc_offset;

                if (copy_size > core->rx_desc_buf_size) {

                    copy_size = core->rx_desc_buf_size;

                }



                /* For PS mode copy the packet header first */

                if (do_ps) {

                    if (is_first) {

                        size_t ps_hdr_copied = 0;

                        do {

                            iov_copy = MIN(ps_hdr_len - ps_hdr_copied,

                                           iov->iov_len - iov_ofs);



                            e1000e_write_hdr_to_rx_buffers(core, &ba, &bastate,

                                                      iov->iov_base, iov_copy);



                            copy_size -= iov_copy;

                            ps_hdr_copied += iov_copy;



                            iov_ofs += iov_copy;

                            if (iov_ofs == iov->iov_len) {

                                iov++;

                                iov_ofs = 0;

                            }

                        } while (ps_hdr_copied < ps_hdr_len);



                        is_first = false;

                    } else {

                        /* Leave buffer 0 of each descriptor except first */

                        /* empty as per spec 7.1.5.1                      */

                        e1000e_write_hdr_to_rx_buffers(core, &ba, &bastate,

                                                       NULL, 0);

                    }

                }



                /* Copy packet payload */

                while (copy_size) {

                    iov_copy = MIN(copy_size, iov->iov_len - iov_ofs);



                    e1000e_write_to_rx_buffers(core, &ba, &bastate,

                                            iov->iov_base + iov_ofs, iov_copy);



                    copy_size -= iov_copy;

                    iov_ofs += iov_copy;

                    if (iov_ofs == iov->iov_len) {

                        iov++;

                        iov_ofs = 0;

                    }

                }



                if (desc_offset + desc_size >= total_size) {

                    /* Simulate FCS checksum presence in the last descriptor */

                    e1000e_write_to_rx_buffers(core, &ba, &bastate,

                          (const char *) &fcs_pad, e1000x_fcs_len(core->mac));

                }

            }

            desc_offset += desc_size;

            if (desc_offset >= total_size) {

                is_last = true;

            }

        } else { /* as per intel docs; skip descriptors with null buf addr */

            trace_e1000e_rx_null_descriptor();

        }



        e1000e_write_rx_descr(core, desc, is_last ? core->rx_pkt : NULL,

                           rss_info, do_ps ? ps_hdr_len : 0, &bastate.written);

        pci_dma_write(d, base, &desc, core->rx_desc_len);



        e1000e_ring_advance(core, rxi,

                            core->rx_desc_len / E1000_MIN_RX_DESC_LEN);



    } while (desc_offset < total_size);



    e1000e_update_rx_stats(core, size, total_size);

}
