| /* |
| * QEMU Intel IGD Passthrough Host Bridge Emulation |
| * |
| * Copyright (c) 2006 Fabrice Bellard |
| * |
| * SPDX-License-Identifier: MIT |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a copy |
| * of this software and associated documentation files (the "Software"), to deal |
| * in the Software without restriction, including without limitation the rights |
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| * copies of the Software, and to permit persons to whom the Software is |
| * furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
| * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| * THE SOFTWARE. |
| */ |
| |
| #include "qemu/osdep.h" |
| #include "hw/pci/pci.h" |
| #include "hw/pci/pci_host.h" |
| #include "hw/pci-host/i440fx.h" |
| #include "qapi/error.h" |
| |
| typedef struct { |
| uint8_t offset; |
| uint8_t len; |
| } IGDHostInfo; |
| |
| /* Here we just expose minimal host bridge offset subset. */ |
| static const IGDHostInfo igd_host_bridge_infos[] = { |
| {PCI_REVISION_ID, 2}, |
| {PCI_SUBSYSTEM_VENDOR_ID, 2}, |
| {PCI_SUBSYSTEM_ID, 2}, |
| {0x50, 2}, /* SNB: processor graphics control register */ |
| {0x52, 2}, /* processor graphics control register */ |
| {0xa4, 4}, /* SNB: graphics base of stolen memory */ |
| {0xa8, 4}, /* SNB: base of GTT stolen memory */ |
| }; |
| |
| static void host_pci_config_read(int pos, int len, uint32_t *val, Error **errp) |
| { |
| int rc, config_fd; |
| /* Access real host bridge. */ |
| char *path = g_strdup_printf("/sys/bus/pci/devices/%04x:%02x:%02x.%d/%s", |
| 0, 0, 0, 0, "config"); |
| |
| config_fd = open(path, O_RDWR); |
| if (config_fd < 0) { |
| error_setg_errno(errp, errno, "Failed to open: %s", path); |
| goto out; |
| } |
| |
| if (lseek(config_fd, pos, SEEK_SET) != pos) { |
| error_setg_errno(errp, errno, "Failed to seek: %s", path); |
| goto out_close_fd; |
| } |
| |
| do { |
| rc = read(config_fd, (uint8_t *)val, len); |
| } while (rc < 0 && (errno == EINTR || errno == EAGAIN)); |
| if (rc != len) { |
| error_setg_errno(errp, errno, "Failed to read: %s", path); |
| } |
| |
| out_close_fd: |
| close(config_fd); |
| out: |
| g_free(path); |
| } |
| |
| static void igd_pt_i440fx_realize(PCIDevice *pci_dev, Error **errp) |
| { |
| uint32_t val = 0; |
| size_t i; |
| int pos, len; |
| Error *local_err = NULL; |
| |
| for (i = 0; i < ARRAY_SIZE(igd_host_bridge_infos); i++) { |
| pos = igd_host_bridge_infos[i].offset; |
| len = igd_host_bridge_infos[i].len; |
| host_pci_config_read(pos, len, &val, &local_err); |
| if (local_err) { |
| error_propagate(errp, local_err); |
| return; |
| } |
| pci_default_write_config(pci_dev, pos, val, len); |
| } |
| } |
| |
| static void igd_passthrough_i440fx_class_init(ObjectClass *klass, void *data) |
| { |
| DeviceClass *dc = DEVICE_CLASS(klass); |
| PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); |
| |
| k->realize = igd_pt_i440fx_realize; |
| dc->desc = "IGD Passthrough Host bridge"; |
| } |
| |
| static const TypeInfo igd_passthrough_i440fx_info = { |
| .name = TYPE_IGD_PASSTHROUGH_I440FX_PCI_DEVICE, |
| .parent = TYPE_I440FX_PCI_DEVICE, |
| .instance_size = sizeof(PCII440FXState), |
| .class_init = igd_passthrough_i440fx_class_init, |
| }; |
| |
| static void igd_pt_i440fx_register_types(void) |
| { |
| type_register_static(&igd_passthrough_i440fx_info); |
| } |
| |
| type_init(igd_pt_i440fx_register_types) |