i440fx.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. /*
  2. * QEMU i440FX PCI Bridge Emulation
  3. *
  4. * Copyright (c) 2006 Fabrice Bellard
  5. *
  6. * Permission is hereby granted, free of charge, to any person obtaining a copy
  7. * of this software and associated documentation files (the "Software"), to deal
  8. * in the Software without restriction, including without limitation the rights
  9. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. * copies of the Software, and to permit persons to whom the Software is
  11. * furnished to do so, subject to the following conditions:
  12. *
  13. * The above copyright notice and this permission notice shall be included in
  14. * all copies or substantial portions of the Software.
  15. *
  16. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  19. * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  22. * THE SOFTWARE.
  23. */
  24. #include "qemu/osdep.h"
  25. #include "qemu/units.h"
  26. #include "qemu/range.h"
  27. #include "hw/i386/pc.h"
  28. #include "hw/pci/pci.h"
  29. #include "hw/pci/pci_host.h"
  30. #include "hw/pci-host/i440fx.h"
  31. #include "hw/qdev-properties.h"
  32. #include "hw/sysbus.h"
  33. #include "qapi/error.h"
  34. #include "migration/vmstate.h"
  35. #include "qapi/visitor.h"
  36. #include "qemu/error-report.h"
  37. #include "qom/object.h"
  38. /*
  39. * I440FX chipset data sheet.
  40. * https://wiki.qemu.org/File:29054901.pdf
  41. */
  42. OBJECT_DECLARE_SIMPLE_TYPE(I440FXState, I440FX_PCI_HOST_BRIDGE)
  43. struct I440FXState {
  44. PCIHostState parent_obj;
  45. MemoryRegion *system_memory;
  46. MemoryRegion *io_memory;
  47. MemoryRegion *pci_address_space;
  48. MemoryRegion *ram_memory;
  49. Range pci_hole;
  50. uint64_t below_4g_mem_size;
  51. uint64_t above_4g_mem_size;
  52. uint64_t pci_hole64_size;
  53. bool pci_hole64_fix;
  54. char *pci_type;
  55. };
  56. #define I440FX_PAM 0x59
  57. #define I440FX_PAM_SIZE 7
  58. #define I440FX_SMRAM 0x72
  59. /* Keep it 2G to comply with older win32 guests */
  60. #define I440FX_PCI_HOST_HOLE64_SIZE_DEFAULT (1ULL << 31)
  61. /* Older coreboot versions (4.0 and older) read a config register that doesn't
  62. * exist in real hardware, to get the RAM size from QEMU.
  63. */
  64. #define I440FX_COREBOOT_RAM_SIZE 0x57
  65. static void i440fx_realize(PCIDevice *dev, Error **errp)
  66. {
  67. dev->config[I440FX_SMRAM] = 0x02;
  68. if (object_property_get_bool(qdev_get_machine(), "iommu", NULL)) {
  69. warn_report("i440fx doesn't support emulated iommu");
  70. }
  71. }
  72. static void i440fx_update_memory_mappings(PCII440FXState *d)
  73. {
  74. int i;
  75. PCIDevice *pd = PCI_DEVICE(d);
  76. memory_region_transaction_begin();
  77. for (i = 0; i < ARRAY_SIZE(d->pam_regions); i++) {
  78. pam_update(&d->pam_regions[i], i,
  79. pd->config[I440FX_PAM + DIV_ROUND_UP(i, 2)]);
  80. }
  81. memory_region_set_enabled(&d->smram_region,
  82. !(pd->config[I440FX_SMRAM] & SMRAM_D_OPEN));
  83. memory_region_set_enabled(&d->smram,
  84. pd->config[I440FX_SMRAM] & SMRAM_G_SMRAME);
  85. memory_region_transaction_commit();
  86. }
  87. static void i440fx_write_config(PCIDevice *dev,
  88. uint32_t address, uint32_t val, int len)
  89. {
  90. PCII440FXState *d = I440FX_PCI_DEVICE(dev);
  91. /* XXX: implement SMRAM.D_LOCK */
  92. pci_default_write_config(dev, address, val, len);
  93. if (ranges_overlap(address, len, I440FX_PAM, I440FX_PAM_SIZE) ||
  94. range_covers_byte(address, len, I440FX_SMRAM)) {
  95. i440fx_update_memory_mappings(d);
  96. }
  97. }
  98. static int i440fx_post_load(void *opaque, int version_id)
  99. {
  100. PCII440FXState *d = opaque;
  101. i440fx_update_memory_mappings(d);
  102. return 0;
  103. }
  104. static const VMStateDescription vmstate_i440fx = {
  105. .name = "I440FX",
  106. .version_id = 3,
  107. .minimum_version_id = 3,
  108. .post_load = i440fx_post_load,
  109. .fields = (const VMStateField[]) {
  110. VMSTATE_PCI_DEVICE(parent_obj, PCII440FXState),
  111. /* Used to be smm_enabled, which was basically always zero because
  112. * SeaBIOS hardly uses SMM. SMRAM is now handled by CPU code.
  113. */
  114. VMSTATE_UNUSED(1),
  115. VMSTATE_END_OF_LIST()
  116. }
  117. };
  118. static void i440fx_pcihost_get_pci_hole_start(Object *obj, Visitor *v,
  119. const char *name, void *opaque,
  120. Error **errp)
  121. {
  122. I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj);
  123. uint64_t val64;
  124. uint32_t value;
  125. val64 = range_is_empty(&s->pci_hole) ? 0 : range_lob(&s->pci_hole);
  126. value = val64;
  127. assert(value == val64);
  128. visit_type_uint32(v, name, &value, errp);
  129. }
  130. static void i440fx_pcihost_get_pci_hole_end(Object *obj, Visitor *v,
  131. const char *name, void *opaque,
  132. Error **errp)
  133. {
  134. I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj);
  135. uint64_t val64;
  136. uint32_t value;
  137. val64 = range_is_empty(&s->pci_hole) ? 0 : range_upb(&s->pci_hole) + 1;
  138. value = val64;
  139. assert(value == val64);
  140. visit_type_uint32(v, name, &value, errp);
  141. }
  142. /*
  143. * The 64bit PCI hole start is set by the Guest firmware
  144. * as the address of the first 64bit PCI MEM resource.
  145. * If no PCI device has resources on the 64bit area,
  146. * the 64bit PCI hole will start after "over 4G RAM" and the
  147. * reserved space for memory hotplug if any.
  148. */
  149. static uint64_t i440fx_pcihost_get_pci_hole64_start_value(Object *obj)
  150. {
  151. PCIHostState *h = PCI_HOST_BRIDGE(obj);
  152. I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj);
  153. Range w64;
  154. uint64_t value;
  155. pci_bus_get_w64_range(h->bus, &w64);
  156. value = range_is_empty(&w64) ? 0 : range_lob(&w64);
  157. if (!value && s->pci_hole64_fix) {
  158. value = pc_pci_hole64_start();
  159. }
  160. return value;
  161. }
  162. static void i440fx_pcihost_get_pci_hole64_start(Object *obj, Visitor *v,
  163. const char *name,
  164. void *opaque, Error **errp)
  165. {
  166. uint64_t hole64_start = i440fx_pcihost_get_pci_hole64_start_value(obj);
  167. visit_type_uint64(v, name, &hole64_start, errp);
  168. }
  169. /*
  170. * The 64bit PCI hole end is set by the Guest firmware
  171. * as the address of the last 64bit PCI MEM resource.
  172. * Then it is expanded to the PCI_HOST_PROP_PCI_HOLE64_SIZE
  173. * that can be configured by the user.
  174. */
  175. static void i440fx_pcihost_get_pci_hole64_end(Object *obj, Visitor *v,
  176. const char *name, void *opaque,
  177. Error **errp)
  178. {
  179. PCIHostState *h = PCI_HOST_BRIDGE(obj);
  180. I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj);
  181. uint64_t hole64_start = i440fx_pcihost_get_pci_hole64_start_value(obj);
  182. Range w64;
  183. uint64_t value, hole64_end;
  184. pci_bus_get_w64_range(h->bus, &w64);
  185. value = range_is_empty(&w64) ? 0 : range_upb(&w64) + 1;
  186. hole64_end = ROUND_UP(hole64_start + s->pci_hole64_size, 1ULL << 30);
  187. if (s->pci_hole64_fix && value < hole64_end) {
  188. value = hole64_end;
  189. }
  190. visit_type_uint64(v, name, &value, errp);
  191. }
  192. static void i440fx_pcihost_initfn(Object *obj)
  193. {
  194. I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj);
  195. PCIHostState *phb = PCI_HOST_BRIDGE(obj);
  196. memory_region_init_io(&phb->conf_mem, obj, &pci_host_conf_le_ops, phb,
  197. "pci-conf-idx", 4);
  198. memory_region_init_io(&phb->data_mem, obj, &pci_host_data_le_ops, phb,
  199. "pci-conf-data", 4);
  200. object_property_add_link(obj, PCI_HOST_PROP_RAM_MEM, TYPE_MEMORY_REGION,
  201. (Object **) &s->ram_memory,
  202. qdev_prop_allow_set_link_before_realize, 0);
  203. object_property_add_link(obj, PCI_HOST_PROP_PCI_MEM, TYPE_MEMORY_REGION,
  204. (Object **) &s->pci_address_space,
  205. qdev_prop_allow_set_link_before_realize, 0);
  206. object_property_add_link(obj, PCI_HOST_PROP_SYSTEM_MEM, TYPE_MEMORY_REGION,
  207. (Object **) &s->system_memory,
  208. qdev_prop_allow_set_link_before_realize, 0);
  209. object_property_add_link(obj, PCI_HOST_PROP_IO_MEM, TYPE_MEMORY_REGION,
  210. (Object **) &s->io_memory,
  211. qdev_prop_allow_set_link_before_realize, 0);
  212. }
  213. static void i440fx_pcihost_realize(DeviceState *dev, Error **errp)
  214. {
  215. ERRP_GUARD();
  216. I440FXState *s = I440FX_PCI_HOST_BRIDGE(dev);
  217. PCIHostState *phb = PCI_HOST_BRIDGE(dev);
  218. SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
  219. PCIBus *b;
  220. PCIDevice *d;
  221. PCII440FXState *f;
  222. unsigned i;
  223. memory_region_add_subregion(s->io_memory, 0xcf8, &phb->conf_mem);
  224. sysbus_init_ioports(sbd, 0xcf8, 4);
  225. memory_region_add_subregion(s->io_memory, 0xcfc, &phb->data_mem);
  226. sysbus_init_ioports(sbd, 0xcfc, 4);
  227. /* register i440fx 0xcf8 port as coalesced pio */
  228. memory_region_set_flush_coalesced(&phb->data_mem);
  229. memory_region_add_coalescing(&phb->conf_mem, 0, 4);
  230. b = pci_root_bus_new(dev, NULL, s->pci_address_space,
  231. s->io_memory, 0, TYPE_PCI_BUS);
  232. phb->bus = b;
  233. d = pci_create_simple(b, 0, s->pci_type);
  234. f = I440FX_PCI_DEVICE(d);
  235. range_set_bounds(&s->pci_hole, s->below_4g_mem_size,
  236. IO_APIC_DEFAULT_ADDRESS - 1);
  237. /* setup pci memory mapping */
  238. pc_pci_as_mapping_init(s->system_memory, s->pci_address_space);
  239. /* if *disabled* show SMRAM to all CPUs */
  240. memory_region_init_alias(&f->smram_region, OBJECT(d), "smram-region",
  241. s->pci_address_space, SMRAM_C_BASE, SMRAM_C_SIZE);
  242. memory_region_add_subregion_overlap(s->system_memory, SMRAM_C_BASE,
  243. &f->smram_region, 1);
  244. memory_region_set_enabled(&f->smram_region, true);
  245. /* smram, as seen by SMM CPUs */
  246. memory_region_init(&f->smram, OBJECT(d), "smram", 4 * GiB);
  247. memory_region_set_enabled(&f->smram, true);
  248. memory_region_init_alias(&f->low_smram, OBJECT(d), "smram-low",
  249. s->ram_memory, SMRAM_C_BASE, SMRAM_C_SIZE);
  250. memory_region_set_enabled(&f->low_smram, true);
  251. memory_region_add_subregion(&f->smram, SMRAM_C_BASE, &f->low_smram);
  252. object_property_add_const_link(qdev_get_machine(), "smram",
  253. OBJECT(&f->smram));
  254. init_pam(&f->pam_regions[0], OBJECT(d), s->ram_memory, s->system_memory,
  255. s->pci_address_space, PAM_BIOS_BASE, PAM_BIOS_SIZE);
  256. for (i = 0; i < ARRAY_SIZE(f->pam_regions) - 1; ++i) {
  257. init_pam(&f->pam_regions[i + 1], OBJECT(d), s->ram_memory,
  258. s->system_memory, s->pci_address_space,
  259. PAM_EXPAN_BASE + i * PAM_EXPAN_SIZE, PAM_EXPAN_SIZE);
  260. }
  261. ram_addr_t ram_size = s->below_4g_mem_size + s->above_4g_mem_size;
  262. ram_size = ram_size / 8 / 1024 / 1024;
  263. if (ram_size > 255) {
  264. ram_size = 255;
  265. }
  266. d->config[I440FX_COREBOOT_RAM_SIZE] = ram_size;
  267. i440fx_update_memory_mappings(f);
  268. }
  269. static void i440fx_class_init(ObjectClass *klass, void *data)
  270. {
  271. DeviceClass *dc = DEVICE_CLASS(klass);
  272. PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
  273. k->realize = i440fx_realize;
  274. k->config_write = i440fx_write_config;
  275. k->vendor_id = PCI_VENDOR_ID_INTEL;
  276. k->device_id = PCI_DEVICE_ID_INTEL_82441;
  277. k->revision = 0x02;
  278. k->class_id = PCI_CLASS_BRIDGE_HOST;
  279. dc->desc = "Host bridge";
  280. dc->vmsd = &vmstate_i440fx;
  281. /*
  282. * PCI-facing part of the host bridge, not usable without the
  283. * host-facing part, which can't be device_add'ed, yet.
  284. */
  285. dc->user_creatable = false;
  286. dc->hotpluggable = false;
  287. }
  288. static const TypeInfo i440fx_info = {
  289. .name = TYPE_I440FX_PCI_DEVICE,
  290. .parent = TYPE_PCI_DEVICE,
  291. .instance_size = sizeof(PCII440FXState),
  292. .class_init = i440fx_class_init,
  293. .interfaces = (InterfaceInfo[]) {
  294. { INTERFACE_CONVENTIONAL_PCI_DEVICE },
  295. { },
  296. },
  297. };
  298. static const char *i440fx_pcihost_root_bus_path(PCIHostState *host_bridge,
  299. PCIBus *rootbus)
  300. {
  301. return "0000:00";
  302. }
  303. static const Property i440fx_props[] = {
  304. DEFINE_PROP_SIZE(PCI_HOST_PROP_PCI_HOLE64_SIZE, I440FXState,
  305. pci_hole64_size, I440FX_PCI_HOST_HOLE64_SIZE_DEFAULT),
  306. DEFINE_PROP_SIZE(PCI_HOST_BELOW_4G_MEM_SIZE, I440FXState,
  307. below_4g_mem_size, 0),
  308. DEFINE_PROP_SIZE(PCI_HOST_ABOVE_4G_MEM_SIZE, I440FXState,
  309. above_4g_mem_size, 0),
  310. DEFINE_PROP_BOOL("x-pci-hole64-fix", I440FXState, pci_hole64_fix, true),
  311. DEFINE_PROP_STRING(I440FX_HOST_PROP_PCI_TYPE, I440FXState, pci_type),
  312. };
  313. static void i440fx_pcihost_class_init(ObjectClass *klass, void *data)
  314. {
  315. DeviceClass *dc = DEVICE_CLASS(klass);
  316. PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass);
  317. hc->root_bus_path = i440fx_pcihost_root_bus_path;
  318. dc->realize = i440fx_pcihost_realize;
  319. dc->fw_name = "pci";
  320. device_class_set_props(dc, i440fx_props);
  321. /* Reason: needs to be wired up by pc_init1 */
  322. dc->user_creatable = false;
  323. object_class_property_add(klass, PCI_HOST_PROP_PCI_HOLE_START, "uint32",
  324. i440fx_pcihost_get_pci_hole_start,
  325. NULL, NULL, NULL);
  326. object_class_property_add(klass, PCI_HOST_PROP_PCI_HOLE_END, "uint32",
  327. i440fx_pcihost_get_pci_hole_end,
  328. NULL, NULL, NULL);
  329. object_class_property_add(klass, PCI_HOST_PROP_PCI_HOLE64_START, "uint64",
  330. i440fx_pcihost_get_pci_hole64_start,
  331. NULL, NULL, NULL);
  332. object_class_property_add(klass, PCI_HOST_PROP_PCI_HOLE64_END, "uint64",
  333. i440fx_pcihost_get_pci_hole64_end,
  334. NULL, NULL, NULL);
  335. }
  336. static const TypeInfo i440fx_pcihost_info = {
  337. .name = TYPE_I440FX_PCI_HOST_BRIDGE,
  338. .parent = TYPE_PCI_HOST_BRIDGE,
  339. .instance_size = sizeof(I440FXState),
  340. .instance_init = i440fx_pcihost_initfn,
  341. .class_init = i440fx_pcihost_class_init,
  342. };
  343. static void i440fx_register_types(void)
  344. {
  345. type_register_static(&i440fx_info);
  346. type_register_static(&i440fx_pcihost_info);
  347. }
  348. type_init(i440fx_register_types)