用qemu實現tsi107橋摸索(三)
通過上一篇的分析,知道了
- mac_newworld中的 TYPE_U3_AGP_HOST_BRIDGE; TYPE_UNI_NORTH_AGP_HOST_BRIDGE;TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE;TYPE_UNI_NORTH_PCI_HOST_BRIDGE;其實本質上都是UNINHostState (這個有點像C++的從一個類例項化出不同的物件)
- sysbus_mmio_map和 memory_region_add_subregion(get_system_memory(), 0xf2000000,sysbus_mmio_get_region(s, 3));完成了向CPU可以直接訪問到的空間進行的對映。
- UNINHostState具體的實現關聯,是如何新增的到system_memory中的
#define UNI_NORTH_PCI_HOST_BRIDGE(obj) \ OBJECT_CHECK(UNINHostState, (obj), TYPE_UNI_NORTH_PCI_HOST_BRIDGE) #define UNI_NORTH_AGP_HOST_BRIDGE(obj) \ OBJECT_CHECK(UNINHostState, (obj), TYPE_UNI_NORTH_AGP_HOST_BRIDGE) #define UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE(obj) \ OBJECT_CHECK(UNINHostState, (obj), TYPE_UNI_NORTH_INTERNAL_PCI_HOST_BRIDGE) #define U3_AGP_HOST_BRIDGE(obj) \ OBJECT_CHECK(UNINHostState, (obj), TYPE_U3_AGP_HOST_BRIDGE)
今天就讓我們繼續往後分析吧
macIO
/* init basic PC hardware */ pci_bus = PCI_HOST_BRIDGE(uninorth_pci)->bus; /* MacIO */ macio = NEWWORLD_MACIO(pci_create(pci_bus, -1, TYPE_NEWWORLD_MACIO)); dev = DEVICE(macio); qdev_prop_set_uint64(dev, "frequency", tbfreq); qdev_prop_set_bit(dev, "has-pmu", has_pmu); qdev_prop_set_bit(dev, "has-adb", has_adb); object_property_set_link(OBJECT(macio), OBJECT(pic_dev), "pic", &error_abort); qdev_init_nofail(dev);
PCI_HOST_BRIDGE(uninorth_pci)將UNINHostState強制轉換成PCIHostState,並讓pci_bus等於這個PCIHostState中的PCIBus *bus
struct PCIHostState {
SysBusDevice busdev;
MemoryRegion conf_mem;
MemoryRegion data_mem;
MemoryRegion mmcfg;
uint32_t config_reg;
PCIBus *bus;
QLIST_ENTRY(PCIHostState) next;
};
macio是建立在pci_bus上的一個PCIDevice
=> PCIDevice *pci_create(PCIBus *bus, int devfn, const char *name)
=> PCIDevice *pci_create_multifunction(PCIBus *bus, int devfn, bool multifunction,
const char *name)
pci_create_multifunction(bus, devfn, false, name);
==> dev = qdev_create(&bus->qbus, name);
===> dev = qdev_try_create(bus, name);
然後就是對這個裝置的建立了,會呼叫到TYPE_NEWWORLD_MACIO其的instance_init和class_init
#define NEWWORLD_MACIO(obj) \
OBJECT_CHECK(NewWorldMacIOState, (obj), TYPE_NEWWORLD_MACIO)
typedef struct NewWorldMacIOState {
/*< private >*/
MacIOState parent_obj;
/*< public >*/
bool has_pmu;
bool has_adb;
OpenPICState *pic;
MACIOIDEState ide[2];
MacIOGPIOState gpio;
} NewWorldMacIOState;
-
static void macio_newworld_init(Object *obj)
static void macio_newworld_init(Object *obj)
{
MacIOState *s = MACIO(obj);
NewWorldMacIOState *ns = NEWWORLD_MACIO(obj);
int i;
object_property_add_link(obj, "pic", TYPE_OPENPIC,
(Object **) &ns->pic,
qdev_prop_allow_set_link_before_realize,
0, NULL);
sysbus_init_child_obj(obj, "gpio", &ns->gpio, sizeof(ns->gpio),
TYPE_MACIO_GPIO);
for (i = 0; i < 2; i++) {
macio_init_ide(s, &ns->ide[i], sizeof(ns->ide[i]), i);
}
}
可以從這段程式碼中看出:
- 首先添加了pic的屬性(所以後面的epic可以在這裡修改)
- 初始化了孩子物件 sysbus_init_child_obj(obj, "gpio", &ns->gpio, sizeof(ns->gpio), TYPE_MACIO_GPIO);
#define TYPE_MACIO_GPIO "macio-gpio"
#define MACIO_GPIO(obj) OBJECT_CHECK(MacIOGPIOState, (obj), TYPE_MACIO_GPIO)
typedef struct MacIOGPIOState {
/*< private >*/
SysBusDevice parent;
/*< public >*/
OpenPICState *pic;
MemoryRegion gpiomem;
qemu_irq gpio_extirqs[10];
uint8_t gpio_levels[8];
uint8_t gpio_regs[36]; /* XXX Check count */
} MacIOGPIOState;
static const TypeInfo macio_gpio_init_info = {
.name = TYPE_MACIO_GPIO,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(MacIOGPIOState),
.instance_init = macio_gpio_init,
.class_init = macio_gpio_class_init,
.interfaces = (InterfaceInfo[]) {
{ TYPE_NMI },
{ }
},
};
static void macio_gpio_init(Object *obj)
{
SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
MacIOGPIOState *s = MACIO_GPIO(obj);
object_property_add_link(obj, "pic", TYPE_OPENPIC,
(Object **) &s->pic,
qdev_prop_allow_set_link_before_realize,
0, NULL);
memory_region_init_io(&s->gpiomem, OBJECT(s), &macio_gpio_ops, obj,
"gpio", 0x30);
sysbus_init_mmio(sbd, &s->gpiomem);
}
從上面可以看出,這裡是通過
memory_region_init_io(&s->gpiomem, OBJECT(s), &macio_gpio_ops, obj,
"gpio", 0x30);
sysbus_init_mmio(sbd, &s->gpiomem);
完成了mmio_init(就想上一篇所說的一樣)
-
macio_gpio_ops
static const MemoryRegionOps macio_gpio_ops = {
.read = macio_gpio_read,
.write = macio_gpio_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.impl = {
.min_access_size = 1,
.max_access_size = 1,
},
};
//macio_gpio_read
static uint64_t macio_gpio_read(void *opaque, hwaddr addr, unsigned size)
{
MacIOGPIOState *s = opaque;
uint64_t val = 0;
/* Levels regs */
if (addr < 8) {
val = s->gpio_levels[addr];
} else {
addr -= 8;
if (addr < 36) {
val = s->gpio_regs[addr];
}
}
trace_macio_gpio_write(addr, val);
return val;
}
//macio_gpio_write
static void macio_gpio_write(void *opaque, hwaddr addr, uint64_t value,
unsigned size)
{
MacIOGPIOState *s = opaque;
uint8_t ibit;
trace_macio_gpio_write(addr, value);
/* Levels regs are read-only */
if (addr < 8) {
return;
}
addr -= 8;
if (addr < 36) {
value &= ~2;
if (value & 4) {
ibit = (value & 1) << 1;
} else {
ibit = s->gpio_regs[addr] & 2;
}
s->gpio_regs[addr] = value | ibit;
}
}
- sysbus_init_child_obj(obj, "gpio", &ns->gpio, sizeof(ns->gpio), TYPE_MACIO_GPIO);
void sysbus_init_child_obj(Object *parent, const char *childname, void *child,
size_t childsize, const char *childtype)
{
object_initialize_child(parent, childname, child, childsize, childtype,
&error_abort, NULL);
qdev_set_parent_bus(DEVICE(child), sysbus_get_default());
}
void object_initialize_child(Object *parentobj, const char *propname,
void *childobj, size_t size, const char *type,
Error **errp, ...)
{
va_list vargs;
va_start(vargs, errp);
object_initialize_childv(parentobj, propname, childobj, size, type, errp,
vargs);
va_end(vargs);
}
void object_initialize_childv(Object *parentobj, const char *propname,
void *childobj, size_t size, const char *type,
Error **errp, va_list vargs)
{
Error *local_err = NULL;
Object *obj;
object_initialize(childobj, size, type);
obj = OBJECT(childobj);
object_set_propv(obj, &local_err, vargs);
if (local_err) {
goto out;
}
object_property_add_child(parentobj, propname, obj, &local_err);
if (local_err) {
goto out;
}
if (object_dynamic_cast(obj, TYPE_USER_CREATABLE)) {
user_creatable_complete(obj, &local_err);
if (local_err) {
object_unparent(obj);
goto out;
}
}
/*
* Since object_property_add_child added a reference to the child object,
* we can drop the reference added by object_initialize(), so the child
* property will own the only reference to the object.
*/
object_unref(obj);
out:
if (local_err) {
error_propagate(errp, local_err);
object_unref(obj);
}
}
通過這句話object_initialize(childobj, size, type);完成物件的初始化
通過qdev_set_parent_bus(DEVICE(child), sysbus_get_default());把這個裝置新增到main_system_bus上,所以這些MACIO是在sysbus上的裝置
讓我們繼續看macio_init_ide
for (i = 0; i < 2; i++) {
macio_init_ide(s, &ns->ide[i], sizeof(ns->ide[i]), i);
}
static void macio_init_ide(MacIOState *s, MACIOIDEState *ide, size_t ide_size,
int index)
{
gchar *name = g_strdup_printf("ide[%i]", index);
sysbus_init_child_obj(OBJECT(s), name, ide, ide_size, TYPE_MACIO_IDE);
memory_region_add_subregion(&s->bar, 0x1f000 + ((index + 1) * 0x1000),
&ide->mem);
g_free(name);
}
typedef struct MACIOIDEState {
/*< private >*/
SysBusDevice parent_obj;
/*< public >*/
uint32_t channel;
qemu_irq real_ide_irq;
qemu_irq real_dma_irq;
qemu_irq ide_irq;
qemu_irq dma_irq;
MemoryRegion mem;
IDEBus bus;
IDEDMA dma;
void *dbdma;
bool dma_active;
uint32_t timing_reg;
uint32_t irq_reg;
} MACIOIDEState;
這裡注意IDE是sysbusdevice!
typedef struct MacIOState {
/*< private >*/
PCIDevice parent;
/*< public >*/
MemoryRegion bar;
CUDAState cuda;
PMUState pmu;
DBDMAState dbdma;
ESCCState escc;
uint64_t frequency;
} MacIOState;
在bar中添加了兩塊IDE中的MemoryRegion&ide->mem
再來看看macio_newwold的class_init
static void macio_newworld_class_init(ObjectClass *oc, void *data)
{
PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc);
DeviceClass *dc = DEVICE_CLASS(oc);
pdc->realize = macio_newworld_realize;
pdc->device_id = PCI_DEVICE_ID_APPLE_UNI_N_KEYL;
dc->vmsd = &vmstate_macio_newworld;
dc->props = macio_newworld_properties;
大致是這樣一個函式呼叫關係吧
在macio完成實現之後:
/* We only emulate 2 out of 3 IDE controllers for now */
ide_drive_get(hd, ARRAY_SIZE(hd));
macio_ide = MACIO_IDE(object_resolve_path_component(OBJECT(macio),
"ide[0]"));
macio_ide_init_drives(macio_ide, hd);
macio_ide = MACIO_IDE(object_resolve_path_component(OBJECT(macio),
"ide[1]"));
macio_ide_init_drives(macio_ide, &hd[MAX_IDE_DEVS]);
這些函式,最後實現了macio_ide的驅動初始化
總結一下:
- MACIO裡面實現了一個BAR, 裡面完成了關於DBDMA , TIMER, OPENPIC等功能,很像這張圖:
後續的思路,是看Tsi107的手冊,重點看地址對映,PCI配置暫存器以及DMA的部分