Tracing the RTL8139c+ NIC Driver with KGDB (within a QEMU Guest machine)

[Note: this is an older post, published now (Nov ’14).]

Driver Source: http://lxr.linux.no/linux+v2.6.28/drivers/net/8139cp.c

We assume you’ve already setup a QEMU guest, which can be traced from the host using KGDB.

In our case, we’re running FreeOpenZoo’s OpenSUSE11 image as the QEMU guest machine; the kernel has been custom-built to support KGDB. Further, the guest machine has been configured to have a “rtl8139” network card emulated. This is actually the RealTek 8139c.

qemu -m 512 -cdrom /dev/cdrom -net nic,model=”rtl8139″ -localtime -kernel-kqemu -serial “stdio” -serial “pty” opensuse11-gnome.qcow.img

The host is Ubuntu 9.04.

Run the QEMU guest machine:

# ./suse11_qemu
Usage: suse11_qemu [mode]
mode: 0 => ‘normal’ boot, kgdb off
1 => ‘debug mode’ boot, kgdb on

# ./suse11_qemu 1kqemu 130052 0Dbg (KGDB) Mode.Once QEMU is running, run ‘gdb vmlinux’ (in another terminal) and connect to the target!
Connect using:


(gdb) target remote /dev/pts/<x>

; where x is the number you see in the message. For example:
“char device redirected to /dev/pts/3”.

Warning: vlan 0 is not connected to host network

char device redirected to /dev/pts/4

<< QEMU pic>>

On the Host:

# gdb vmlinux
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.

License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html&gt;

This is free software: you are free to change and redistribute it.

There is NO WARRANTY, to the extent permitted by law. Type “show copying”

and “show warranty” for details.

This GDB was configured as “i486-linux-gnu”…

(gdb) target remote /dev/pts/4

Remote debugging using /dev/pts/4

kgdb_breakpoint () at kernel/kgdb.c:1721

1721 wmb(); /* Sync point after breakpoint */

(gdb) bt

#0 kgdb_breakpoint () at kernel/kgdb.c:1721

#1 0xc0136608 in kgdb_initial_breakpoint () at kernel/kgdb.c:1631

#2 0xc0137760 in kgdb_register_io_module (new_kgdb_io_ops=<value optimized out>) at kernel/kgdb.c:1673

#3 0xc0247739 in configure_kgdboc () at drivers/serial/kgdboc.c:67

#4 0xc0467f77 in init_kgdboc () at drivers/serial/kgdboc.c:88

#5 0xc0101137 in do_one_initcall (fn=0xc0467f64 <init_kgdboc>) at init/main.c:748

#6 0xc045177b in kernel_init (unused=<value optimized out>) at init/main.c:787

#7 0xc0104be7 in kernel_thread_helper () at arch/x86/kernel/entry_32.S:1070

I Tracing the initialization/probe code

(gdb) b cp_init_one

Breakpoint 1 at 0xc024f708: file drivers/net/8139cp.c, line 1822.

(gdb) b sys_sync

Breakpoint 2 at 0xc01719a3: file fs/sync.c, line 41.

(gdb) c

Continuing.

[New Thread 1]

Breakpoint 1, cp_init_one (pdev=0xdf06d800, ent=0xc043cc0c) at drivers/net/8139cp.c:1822

1822 {

(gdb) bt

#0 cp_init_one (pdev=0xdf06d800, ent=0xc043cc0c) at drivers/net/8139cp.c:1822

#1 0xc01e78c3 in pci_device_probe (dev=<value optimized out>) at drivers/pci/pci-driver.c:207

#2 0xc0249f52 in driver_probe_device (drv=0xc043c9d8, dev=0xdf06d858) at drivers/base/dd.c:122

#3 0xc024a018 in __driver_attach (dev=0xdf06d858, data=<value optimized out>) at drivers/base/dd.c:267

#4 0xc024979c in bus_for_each_dev (bus=<value optimized out>, start=<value optimized out>,

data=0xc043c9d8, fn=0xc0249fcc <__driver_attach>) at drivers/base/bus.c:291

#5 0xc0249df8 in driver_attach (drv=0xc043cc0c) at drivers/base/dd.c:286

#6 0xc0249b18 in bus_add_driver (drv=0xc043c9d8) at drivers/base/bus.c:669

#7 0xc024a23f in driver_register (drv=0xc043c9d8) at drivers/base/driver.c:233

#8 0xc01e798b in __pci_register_driver (drv=0xc043c9a4, owner=0xc043cc0c, mod_name=0xc043cc0c “�20”)

at drivers/pci/pci-driver.c:713

#9 0xc046846a in cp_init () at drivers/net/8139cp.c:2089

#10 0xc0101137 in do_one_initcall (fn=0xc0468456 <cp_init>) at init/main.c:748

#11 0xc045177b in kernel_init (unused=<value optimized out>) at init/main.c:787

#12 0xc0104be7 in kernel_thread_helper () at arch/x86/kernel/entry_32.S:1070

(gdb) l

1817 pci_enable_wake (cp->pdev, 0, 1); /* Enable PME# generation */

1818 pci_set_power_state (cp->pdev, PCI_D3hot);

1819 }

1820

1821 static int cp_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)

1822 {

1823 struct net_device *dev;

1824 struct cp_private *cp;

1825 int rc;

1826 void __iomem *regs;

(gdb)

1827 resource_size_t pciaddr;

1828 unsigned int addr_len, i, pci_using_dac;

1829 DECLARE_MAC_BUF(mac);

1830

1831 #ifndef MODULE

1832 static int version_printed;

1833 if (version_printed++ == 0)

1834 printk(“%s”, version);

1835 #endif

1836

(gdb) help tb

Set a temporary breakpoint.

Like “break” except the breakpoint is only temporary,

so it will be deleted when hit. Equivalent to “break” followed

by using “enable delete” on the breakpoint number.

tbreak [LOCATION] [thread THREADNUM] [if CONDITION]

LOCATION may be a line number, function name, or “*” and an address.

If a line number is specified, break at start of code for that line.

If a function is specified, break at start of code for that function.

If an address is specified, break at that exact address.

With no LOCATION, uses current execution address of selected stack frame.

This is useful for breaking on return to a stack frame.

THREADNUM is the number from “info threads”.

CONDITION is a boolean expression.

Multiple breakpoints at one place are permitted, and useful if conditional.

Do “help breakpoints” for info on other commands dealing with breakpoints.

(gdb) tb drivers/net/8139cp.c:1837

Breakpoint 4 at 0xc024f72a: file drivers/net/8139cp.c, line 1837.

(gdb) c

Continuing.

cp_init_one (pdev=0xdf06d800, ent=0xc0429444) at drivers/net/8139cp.c:1837

1837 if (pdev->vendor == PCI_VENDOR_ID_REALTEK &&

(gdb) bt

#0 cp_init_one (pdev=0xdf06d800, ent=0xc0429444) at drivers/net/8139cp.c:1837

#1 0xc01e78c3 in pci_device_probe (dev=<value optimized out>) at drivers/pci/pci-driver.c:207

#2 0xc0249f52 in driver_probe_device (drv=0xc043c9d8, dev=0xdf06d858) at drivers/base/dd.c:122

#3 0xc024a018 in __driver_attach (dev=0xdf06d858, data=<value optimized out>) at drivers/base/dd.c:267

#4 0xc024979c in bus_for_each_dev (bus=<value optimized out>, start=<value optimized out>,

data=0xc043c9d8, fn=0xc0249fcc <__driver_attach>) at drivers/base/bus.c:291

#5 0xc0249df8 in driver_attach (drv=0xc03f3d45) at drivers/base/dd.c:286

#6 0xc0249b18 in bus_add_driver (drv=0xc043c9d8) at drivers/base/bus.c:669

#7 0xc024a23f in driver_register (drv=0xc043c9d8) at drivers/base/driver.c:233

#8 0xc01e798b in __pci_register_driver (drv=0xc043c9a4, owner=0xc0429444, mod_name=0xc03f3d45 “%s”)

at drivers/pci/pci-driver.c:713

#9 0xc046846a in cp_init () at drivers/net/8139cp.c:2089

#10 0xc0101137 in do_one_initcall (fn=0xc0468456 <cp_init>) at init/main.c:748

#11 0xc045177b in kernel_init (unused=<value optimized out>) at init/main.c:787

#12 0xc0104be7 in kernel_thread_helper () at arch/x86/kernel/entry_32.S:1070

(gdb) l

1832 static int version_printed;

1833 if (version_printed++ == 0)

1834 printk(“%s”, version);

1835 #endif

1836

1837 if (pdev->vendor == PCI_VENDOR_ID_REALTEK &&

1838 pdev->device == PCI_DEVICE_ID_REALTEK_8139 && pdev->revision < 0x20) {

1839 dev_info(&pdev->dev,

1840 “This (id %04x:%04x rev %02x) is not an 8139C+ compatible chip, use 8139too\n”,

1841 pdev->vendor, pdev->device, pdev->revision);

(gdb) p pdev

$2 = (struct pci_dev *) 0xdf06d800

(gdb) set print pretty

(gdb) p *pdev << Lets peek at the pci_dev structure >>

$3 = {

bus_list = {

next = 0xdf06d400,

prev = 0xdf06dc00

},

bus = 0xdf059200,

subordinate = 0x0,

sysdata = 0xdf0443c0,

procent = 0xdd2cacc0,

slot = 0x0,

devfn = 24,

vendor = 4332,

device = 33081,

subsystem_vendor = 6900,

subsystem_device = 4352,

class = 131072,

revision = 32 ‘ ‘,

hdr_type = 0 ”,

pcie_type = 0 ”,

rom_base_reg = 48 ‘0’,

pin = 1 ’01’,

driver = 0x0,

dma_mask = 4294967295,

dma_parms = {

max_segment_size = 65536,

segment_boundary_mask = 4294967295

},

current_state = 5,

pm_cap = 0,

pme_support = 0,

d1_support = 0,

d2_support = 0,

no_d1d2 = 0,

error_state = 1,

dev = {

klist_children = {

bus = 0xc0433ddc,

driver = 0xc043c9d8,

driver_data = 0x0,

platform_data = 0x0,

dma_mask = 0xdf06d838,

coherent_dma_mask = 4294967295,

cfg_size = 256,

irq = 11,

resource = {{

start = 49408,

end = 49663,

name = 0xdf06d8e4 “0000:00:03.0”,

flags = 131329,

parent = 0xc042946c,

sibling = 0xdf06d590,

child = 0x0

}, {

start = 4060090368,

end = 4060090623,

name = 0xdf06d8e4 “0000:00:03.0”,

flags = 131584,

parent = 0xc0429488,

sibling = 0xc140120c,

child = 0x0

}, {

start = 0,

end = 0,

}

(gdb) p /x pdev->vendor

$5 = 0x10ec

(gdb) p /x pdev->device

$6 = 0x8139

(gdb) l 1837

1832 static int version_printed;

1833 if (version_printed++ == 0)

1834 printk(“%s”, version);

1835 #endif

1836

1837 if (pdev->vendor == PCI_VENDOR_ID_REALTEK &&

1838 pdev->device == PCI_DEVICE_ID_REALTEK_8139 && pdev->revision < 0x20) {

1839 dev_info(&pdev->dev,

1840 “This (id %04x:%04x rev %02x) is not an 8139C+ compatible chip, use 8139too\n”,

1841 pdev->vendor, pdev->device, pdev->revision);

(gdb) n

1845 dev = alloc_etherdev(sizeof(struct cp_private));

(gdb) p dev

$7 = <value optimized out>

(gdb) p sizeof(struct cp_private)

$8 = 896

(gdb) n << ‘n’ seems to be stepping into the function ?? >>

alloc_etherdev_mq (sizeof_priv=896, queue_count=1) at net/ethernet/eth.c:369

369 {

(gdb) tb cp_init_one:1863 << to get around this, we set a temporary breakpoint to

the place we want to reach, and continue >>

No source file named cp_init_one.

Make breakpoint pending on future shared library load? (y or [n])

(gdb) tb drivers/net/8139cp.c:1863

Breakpoint 5 at 0xc024f839: file drivers/net/8139cp.c, line 1863.

(gdb) c

Continuing.

cp_init_one (pdev=0xdf06d800, ent=<value optimized out>) at drivers/net/8139cp.c:1863

1863 rc = pci_enable_device(pdev);

(gdb) l

1910

1911 cp->cpcmd = (pci_using_dac ? PCIDAC : 0) |

1912 PCIMulRW | RxChkSum | CpRxOn | CpTxOn;

1913

1914 regs = ioremap(pciaddr, CP_REGS_SIZE);

1915 if (!regs) {

1916 rc = -EIO;

1917 dev_err(&pdev->dev, “Cannot map PCI MMIO (%Lx@%Lx)\n”,

1918 (unsigned long long)pci_resource_len(pdev, 1),

1919 (unsigned long long)pciaddr);

(gdb) p /x pciaddr << the start of the PCI (NIC’s) memory region – bus address >>

$15 = 0xf2001000

(gdb) p /x regs << the ioremap-ped start of the PCI (NIC’s) memory region –

kernel virtual address >>

$16 = 0xc01908c9

(gdb) l

1920 goto err_out_res;

1921 }

1922 dev->base_addr = (unsigned long) regs;

1923 cp->regs = regs;

1924

1925 cp_stop_hw(cp);

1926

1927 /* read MAC address from EEPROM */

1928 addr_len = read_eeprom (regs, 0, 8) == 0x8129 ? 8 : 6;

1929 for (i = 0; i < 3; i++)

(gdb) tb drivers/net/8139cp.c:1965

Breakpoint 7 at 0xc024fb2c: file drivers/net/8139cp.c, line 1965.

(gdb) c

Continuing.

cp_init_one (pdev=0xdf06d800, ent=<value optimized out>) at drivers/net/8139cp.c:1965

1965 rc = register_netdev(dev);

(gdb) p /x *dev

$17 = {

name = {0x65, 0x74, 0x68, 0x25, 0x64, 0x0 <repeats 11 times>},

name_hlist = {

next = 0x0,

pprev = 0x0

},

ifalias = 0x0,

mem_end = 0x0,

mem_start = 0x0,

base_addr = 0xe0814000,

irq = 0xb,

if_port = 0x0,

dma = 0x0,

state = 0x0,

hard_start_xmit = 0xc0250289,

trans_start = 0x0,

watchdog_timeo = 0x708,

watchdog_timer = {

entry = {

next = 0x0,

open = 0xc0250f06,

stop = 0xc0250772,

change_rx_flags = 0x0,

set_rx_mode = 0x0,

set_multicast_list = 0xc02506d5,

set_mac_address = 0xc02ce851,

validate_addr = 0xc02ce56e,

do_ioctl = 0xc0250b61,

set_config = 0x0,

change_mtu = 0xc02ce54c,

tx_timeout = 0xc0250812,

vlan_rx_register = 0xc024fbca,

}

(gdb)

rtl8139cp_Data_Structures_2

RTL8139cp.c NIC Linux driver – Major Data Structures
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s