2022年4月2日 星期六

run Cortex-A57 with kernel 5.4 on qemu


這篇文章只是拿來記錄compile Kernel for Cortex-A57, 用於研究PCIe Driver, 其餘的rootfs與busybox請參考附錄
[brook@:~/Projects/qemu/linux-virt]$ sudo apt-get install gcc-aarch64-linux-gnu #for ARM64
[brook@:~/Projects/qemu/linux-virt]$ export ARCH=arm64
[brook@:~/Projects/qemu/linux-virt]$ export CROSS_COMPILE=aarch64-linux-gnu-
[brook@:~/Projects/qemu/linux-virt]$ cp arch/arm64/configs/defconfig .config
[brook@:~/Projects/qemu/linux-virt]$ make olddefconfig
  HOSTCC  scripts/basic/fixdep
  HOSTCC  scripts/kconfig/conf.o
  HOSTCC  scripts/kconfig/confdata.o
  HOSTCC  scripts/kconfig/expr.o
  LEX     scripts/kconfig/lexer.lex.c
  YACC    scripts/kconfig/parser.tab.[ch]
  HOSTCC  scripts/kconfig/lexer.lex.o
  HOSTCC  scripts/kconfig/parser.tab.o
  HOSTCC  scripts/kconfig/preprocess.o
  HOSTCC  scripts/kconfig/symbol.o
  HOSTLD  scripts/kconfig/conf
scripts/kconfig/conf  --olddefconfig Kconfig
#
# configuration written to .config
#
[brook@:~/Projects/qemu/linux-virt]$ make -j16
[brook@:~/Projects/qemu/linux-virt]$ qemu-system-aarch64 -machine virt -cpu cortex-a57 -smp 8 -m 4096 -kernel  ./arch/arm64/boot/Image -append "console=ttyAMA0 root=/dev/vda" -nographic -initrd ../initrd-arm.img
[    0.000000] Booting Linux on physical CPU 0x0000000000 [0x411fd070]
...
Please press Enter to activate this console.
/ #
/ # lspci -k
00:01.0 Class 0200: 1af4:1000 virtio-pci
00:00.0 Class 0600: 1b36:0008
/ # uname -r
5.4.0
1af4:1000是Virtio network device, 而1b36:0008是QEMU PCIe Host bridge
ETH PCIe driver在"drivers/virtio/virtio_pci_common.c", 其vendor ID是0x1af4, 當device插入時, 就會去比對ID, match後就會載入該module並probe
/* Qumranet donated their vendor ID for devices 0x1000 thru 0x10FF. */
static const struct pci_device_id virtio_pci_id_table[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_REDHAT_QUMRANET, PCI_ANY_ID) },
        { 0 }
};

MODULE_DEVICE_TABLE(pci, virtio_pci_id_table);
...
static struct pci_driver virtio_pci_driver = {
        .name           = "virtio-pci",
        .id_table       = virtio_pci_id_table,
        .probe          = virtio_pci_probe,
        .remove         = virtio_pci_remove,
#ifdef CONFIG_PM_SLEEP
        .driver.pm      = &virtio_pci_pm_ops,
#endif
        .sriov_configure = virtio_pci_sriov_configure,
};

module_pci_driver(virtio_pci_driver);

這裡我把PCI ID移成PCI_DEVICE(PCI_VENDOR_ID_REDHAT_QUMRANET + 1, 再透過echo <vendor_code> <device_code> > /sys/bus/pci/drivers/<pci_device_driver>/new_id動態對PCIe driver新增ID, 讓系統認到網卡
[brook@:~/Projects/qemu/linux-virt]$ git diff .
diff --git a/drivers/virtio/virtio_pci_common.c b/drivers/virtio/virtio_pci_common.c
index f2862f66c2ac..60aef3fea650 100644
--- a/drivers/virtio/virtio_pci_common.c
+++ b/drivers/virtio/virtio_pci_common.c
@@ -492,7 +492,7 @@ static const struct dev_pm_ops virtio_pci_pm_ops = {

 /* Qumranet donated their vendor ID for devices 0x1000 thru 0x10FF. */
 static const struct pci_device_id virtio_pci_id_table[] = {
-       { PCI_DEVICE(PCI_VENDOR_ID_REDHAT_QUMRANET, PCI_ANY_ID) },
+       { PCI_DEVICE(PCI_VENDOR_ID_REDHAT_QUMRANET + 1, PCI_ANY_ID) },
        { 0 }
 };

@@ -514,6 +514,7 @@ static int virtio_pci_probe(struct pci_dev *pci_dev,
 {
        struct virtio_pci_device *vp_dev, *reg_dev = NULL;
        int rc;
+       printk("%s(#%d): Brook\n", __FUNCTION__, __LINE__);

        /* allocate our structure and fill it out */
        vp_dev = kzalloc(sizeof(struct virtio_pci_device), GFP_KERNEL);
lspci會認到1af4:1000, 但是eth driver因為被我跳號, 所以認不到, 再透過/sys/bus/pci/drivers/<pci_device_driver>/new_id將往卡帶起來
/ # lspci
00:01.0 Class 0200: 1af4:1000
00:00.0 Class 0600: 1b36:0008
/ # ifconfig -a
lo        Link encap:Local Loopback
          LOOPBACK  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
/ # echo 1af4 1000 > /sys/bus/pci/drivers/virtio-pci/new_id
[  130.728345] virtio_pci_probe(#517): Brook
[  130.729216] virtio-pci 0000:00:01.0: enabling device (0000 -> 0003)
/ # ifconfig -a
eth0      Link encap:Ethernet  HWaddr 52:54:00:12:34:56
          BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

lo        Link encap:Local Loopback
          LOOPBACK  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

透過echo <Domain:Bus:Device.Function> > /sys/bus/pci/drivers/<pci_device_driver>/unbind將driver移除, 也可以透過echo <Domain:Bus:Device.Function> > /sys/bus/pci/drivers/<pci_device_driver>/bind重新將driver帶上
/ # lspci
00:01.0 Class 0200: 1af4:1000
00:00.0 Class 0600: 1b36:0008
/ # echo 0000:00:01.0 > /sys/bus/pci/drivers/virtio-pci/unbind
[ 5163.097254] hrtimer: interrupt took 87350512 ns
/ # ifconfig -a
lo        Link encap:Local Loopback
          LOOPBACK  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

/ # echo 0000:00:01.0 > /sys/bus/pci/drivers/virtio-pci/bind
[ 5183.251580] virtio_pci_probe(#517): Brook
/ # ifconfig -a
eth0      Link encap:Ethernet  HWaddr 52:54:00:12:34:56
          BROADCAST MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

lo        Link encap:Local Loopback
          LOOPBACK  MTU:65536  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)


    參考資料:
  • https://blog.csdn.net/zhqh100/article/details/51173275, qemu模拟Cortex-A57运行Linux4.5.1
  • Build the Linux Kernel and Busybox for ARM and run them on QEMU
  • https://zhuanlan.zhihu.com/p/113467453, qemu PCIe总线结构
  • https://pci-ids.ucw.cz/read/PC/1af4, The PCI ID Repository
  • https://stackoverflow.com/questions/22901282/hard-time-in-understanding-module-device-tableusb-id-table-usage, Hard time in understanding MODULE_DEVICE_TABLE(usb, id_table) usage




2022年3月26日 星期六

GDB - jump - 17.2 Continuing at a Different Address


jump顧名思義就是跳到某一行開始執行, 而且是會立刻執行直到遇到breakpoint. 因為jump並不會對stack, memory, 或register有任何改變(除了PC/program counter), 也因此jump的範圍需要在同一個function內部, 避免crash.
你也可以用set $pc=<execute_address> + "continue", "next", "step"替代
Type "apropos word" to search for commands related to "word"...
Reading symbols from a.out...done.
(gdb) set listsize unlimited
(gdb) list
1       #include <stdio.h>
2
3       static void possible_crash_1(void)
4       {
5           printf("%s(#%d)\n", __FUNCTION__, __LINE__);
6       }
7
8       static void possible_crash_2(void)
9       {
10          printf("%s(#%d)\n", __FUNCTION__, __LINE__);
11      }
12
13      static void possible_crash_3(void)
14      {
15          printf("%s(#%d)\n", __FUNCTION__, __LINE__);
16      }
17
18      int main(int argc, char *argv[])
19      {
20          possible_crash_1();
21
22          possible_crash_2();
23
24          possible_crash_3();
25
26          return 0;
27      }
28
(gdb) b main
Breakpoint 1 at 0x400576: file jump.c, line 20.
(gdb) run
Starting program: /build/brook/a.out

Breakpoint 1, main (argc=1, argv=0x7fffffffe4e8) at jump.c:20
20          possible_crash_1();
(gdb) j 22
Continuing at 0x40057b.
possible_crash_2(#10)
possible_crash_3(#15)
[Inferior 1 (process 9816) exited normally]
(gdb) run
Starting program: /build/brook/a.out

Breakpoint 1, main (argc=1, argv=0x7fffffffe4e8) at jump.c:20
20          possible_crash_1();
(gdb) set $pc=0x40057b
(gdb) n
possible_crash_2(#10)
24          possible_crash_3();
(gdb) n
possible_crash_3(#15)
26          return 0;



  • 參考資料:
      https://sourceware.org/gdb/download/onlinedocs/gdb/Jumping.html#Jumping, 17.2 Continuing at a Different Address



  • 2022年2月27日 星期日

    lighttpd & CGI note


    CGIC提供了簡單的API, 存取那一些CGIC已經幫你parse好的資料,比如: cgiRequestMethod, cgiRemoteAddr, cgiScriptName, 以及POST/GET資料等等, header的資料可以透過environ讀取
    可以透過cgiFormEntries(char ***result)抓key, 再透過cgiFormString(char *name, char *result, int max)取得資料,
    #include <stdio.h>
    #include <cgic.h>
    extern char **environ;
    
    int cgiMain() {
      char **array, **arrayStep, val[64];
      fprintf(cgiOut, "cgiRequestMethod:%s\n", cgiRequestMethod);
      fprintf(cgiOut, "cgiRemoteAddr:%s\n", cgiRemoteAddr);
      fprintf(cgiOut, "cgiScriptName:%s\n", cgiScriptName);
      fprintf(cgiOut, "cgiContentType:%s\n", cgiContentType);
      fprintf(cgiOut, "used for GET, cgiQueryString:%s\n", cgiQueryString);
    
      if (cgiFormEntries(&array) == cgiFormSuccess) {
          for (arrayStep = array; *arrayStep; arrayStep++) {
              fprintf(cgiOut, "get post by cgiFormEntries():%s\n", *arrayStep);
              cgiFormString(*arrayStep, val, sizeof(val));
              fprintf(cgiOut, "val:%s\n", val);
          }
      }
      for (array = environ; *array; array++) {
          fprintf(cgiOut, "get from env:%s\n", *array);
      }
    
      return 0;
    }
    


    GET

    [brook@:/var/www/html]$ curl -H "xhead: 123" --noproxy 127.0.0.1 http://127.0.0.1/cgic.cgi?xquery=123
    cgiRequestMethod:GET
    cgiRemoteAddr:127.0.0.1
    cgiScriptName:/cgic.cgi
    cgiContentType:
    used for GET, cgiQueryString:xquery=123
    get post by cgiFormEntries():xquery
    val:123
    get from env:CONTENT_LENGTH=0
    get from env:QUERY_STRING=xquery=123
    get from env:REQUEST_URI=/cgic.cgi?xquery=123
    get from env:REDIRECT_STATUS=200
    get from env:SCRIPT_NAME=/cgic.cgi
    get from env:SCRIPT_FILENAME=/var/www/html/cgic.cgi
    get from env:DOCUMENT_ROOT=/var/www/html
    get from env:REQUEST_METHOD=GET
    get from env:SERVER_PROTOCOL=HTTP/1.1
    get from env:SERVER_SOFTWARE=lighttpd/1.4.64
    get from env:GATEWAY_INTERFACE=CGI/1.1
    get from env:REQUEST_SCHEME=http
    get from env:SERVER_PORT=80
    get from env:SERVER_ADDR=127.0.0.1
    get from env:SERVER_NAME=127.0.0.1
    get from env:REMOTE_ADDR=127.0.0.1
    get from env:REMOTE_PORT=52894
    get from env:HTTP_HOST=127.0.0.1
    get from env:HTTP_USER_AGENT=curl/7.47.0
    get from env:HTTP_ACCEPT=*/*
    get from env:HTTP_XHEAD=123
    



    POST

    [brook@:/var/www/html]$ curl -H "xhead: 123" --noproxy 127.0.0.1 -X POST -d 'post1=p1&post2=p2' http://127.0.0.1/cgic.cgi?xquery=123
    cgiRequestMethod:POST
    cgiRemoteAddr:127.0.0.1
    cgiScriptName:/cgic.cgi
    cgiContentType:application/x-www-form-urlencoded
    used for GET, cgiQueryString:xquery=123
    get post by cgiFormEntries():post1
    val:p1
    get post by cgiFormEntries():post2
    val:p2
    get from env:CONTENT_LENGTH=17
    get from env:QUERY_STRING=xquery=123
    get from env:REQUEST_URI=/cgic.cgi?xquery=123
    get from env:REDIRECT_STATUS=200
    get from env:SCRIPT_NAME=/cgic.cgi
    get from env:SCRIPT_FILENAME=/var/www/html/cgic.cgi
    get from env:DOCUMENT_ROOT=/var/www/html
    get from env:REQUEST_METHOD=POST
    get from env:SERVER_PROTOCOL=HTTP/1.1
    get from env:SERVER_SOFTWARE=lighttpd/1.4.64
    get from env:GATEWAY_INTERFACE=CGI/1.1
    get from env:REQUEST_SCHEME=http
    get from env:SERVER_PORT=80
    get from env:SERVER_ADDR=127.0.0.1
    get from env:SERVER_NAME=127.0.0.1
    get from env:REMOTE_ADDR=127.0.0.1
    get from env:REMOTE_PORT=52904
    get from env:HTTP_HOST=127.0.0.1
    get from env:HTTP_USER_AGENT=curl/7.47.0
    get from env:HTTP_ACCEPT=*/*
    get from env:HTTP_XHEAD=123
    get from env:HTTP_CONTENT_LENGTH=17
    get from env:CONTENT_TYPE=application/x-www-form-urlencoded
    



    header的部分會被轉成HTTP_VAR=val方式存在environment中, 相關的code如下,
    http_cgi_encode_varname()
    {
      if (is_http_header) {
        memcpy(p, "HTTP_", 5);
        j = 5; /* "HTTP_" */
      }
      /* uppercase alpha */
      ...
    }
    
    http_cgi_headers()
    {
      ...
      for (n = 0; n < r->rqst_headers.used; n++) {
        http_cgi_encode_varname(tb, BUF_PTR_LEN(&ds->key), 1);
      }
      ...
    }
    
    cgi_create_env()
    {
      /* create environment */
      http_cgi_headers(r, &opts, cgi_env_add, env);
      ...
      pid_t pid = (dfd >= 0) ? fdevent_fork_execve(args[0], args, envp,
        to_cgi_fds[0], from_cgi_fds[1], serrh_fd, dfd): -1;
      ...
    }
    
    mod_cgi_handle_subrequest()
    {
      ...
      cgi_create_env();
      ...
    }
    


    熱門文章