diff --git a/README.adoc b/README.adoc index cf183d8..6e8030b 100644 --- a/README.adoc +++ b/README.adoc @@ -320,52 +320,6 @@ All of this put together makes the safe procedure acceptably fast for regular de It is also easy to GDB step debug kernel modules with our setup, see: xref:gdb-step-debug-kernel-module[xrefstyle=full]. -===== Your first QEMU hack - -Not satisfied with mere software? OK then, let's hack up the QEMU x86 CPU identification: - -.... -vim submodules/qemu/target/i386/cpu.c -.... - -and modify: - -.... -.model_id = "QEMU Virtual CPU version " QEMU_HW_VERSION, -.... - -to contain: - -.... -.model_id = "QEMU Virtual CPU version HACKED " QEMU_HW_VERSION, -.... - -then as usual rebuild and re-run: - -..... -./build-qemu -./run --eval-after 'grep "model name" /proc/cpuinfo' -..... - -and once again, there is your message: QEMU communicated it to the Linux kernel, which printed it out. - -You have now gone from newb to hardware hacker in a mere 15 minutes, your rate of progress is truly astounding!!! - -Seriously though, if you want to be a real hardware hacker, it just can't be done with open source tools as of 2018. The root obstacle is that: - -* https://en.wikipedia.org/wiki/Semiconductor_fabrication_plant[Silicon fabs] don't publish reveal their https://en.wikipedia.org/wiki/Design_rule_checking[design rules] -* which implies that there are no decent https://en.wikipedia.org/wiki/Standard_cell[standard cell libraries]. See also: https://www.quora.com/Are-there-good-open-source-standard-cell-libraries-to-learn-IC-synthesis-with-EDA-tools/answer/Ciro-Santilli -* which implies that people can't develop open source https://en.wikipedia.org/wiki/Electronic_design_automation[EDA tools] -* which implies that you can't get decent https://community.cadence.com/cadence_blogs_8/b/di/posts/hls-ppa-is-it-all-you-need-to-know[power, performance and area] estimates - -The only thing you can do with open source is purely functional designs with https://en.wikipedia.org/wiki/Verilator[Verilator], but you will never know if it can be actually produced and how efficient it can be. - -If you really want to develop semiconductors, your only choice is to join an university or a semiconductor company that has the EDA licenses. - -See also: xref:should-you-waste-your-life-with-systems-programming[xrefstyle=full]. - -While hacking QEMU, you will likely want to GDB step its source. That is trivial since QEMU is just another userland program like any other, but our setup has a shortcut to make it even more convenient, see: xref:debug-the-emulator[xrefstyle=full]. - ===== Your first glibc hack We use <>, and it is tracked as an unmodified submodule at link:submodules/glibc[], at the exact same version that Buildroot has it, which can be found at: https://github.com/buildroot/buildroot/blob/2018.05/package/glibc/glibc.mk#L13[package/glibc/glibc.mk]. Buildroot 2018.05 applies no patches. @@ -888,8 +842,6 @@ Be saner and use our custom built QEMU instead: ./run .... -This also allows you to <> if you're into that sort of thing. - To build the kernel modules as in <> do: .... @@ -3748,8 +3700,6 @@ Interestingly, using initramfs significantly slows down the gem5 boot, even thou The device tree is a Linux kernel defined data structure that serves to inform the kernel how the hardware is setup. -<> contains a minimal runnable example of device tree manipulation. - Device trees serve to reduce the need for hardware vendors to patch the kernel: they just provide a device tree file instead, which is much simpler. x86 does not use it device trees, but many other archs to, notably ARM. @@ -8228,8 +8178,6 @@ However, this module is intended to fire only once as can be seen from its sourc and furthermore interrupt `1` and `12` happen immediately TODO why, were they somehow pending? -So so see something interesting, you need to monitor an interrupt that is more rare than the keyboard, e.g. <>. - ==== /proc/interrupts In the guest with <>: @@ -10203,57 +10151,11 @@ For the more complex interfaces, we focus on simplified educational devices, eit * present in the QEMU upstream: ** <> -* added in https://github.com/cirosantilli/qemu[our fork of QEMU]: -** <> -** <> ==== PCI Only tested in x86. -[[pci-min]] -===== pci_min - -PCI driver for our minimal `pci_min.c` QEMU fork device: - -.... -./run -- -device lkmc_pci_min -.... - -then: - -.... -insmod pci_min.ko -.... - -Sources: - -* Kernel module: link:kernel_modules/pci_min.c[]. -* QEMU device: https://github.com/cirosantilli/qemu/blob/lkmc/hw/misc/lkmc_pci_min.c - -Outcome: - -.... -<4>[ 10.608241] pci_min: loading out-of-tree module taints kernel. -<6>[ 10.609935] probe -<6>[ 10.651881] dev->irq = 11 -lkmc_pci_min mmio_write addr = 0 val = 12345678 size = 4 -<6>[ 10.668515] irq_handler irq = 11 dev = 251 -lkmc_pci_min mmio_write addr = 4 val = 0 size = 4 -.... - -What happened: - -* right at probe time, we write to a register -* our hardware model is coded such that it generates an interrupt when written to -* the Linux kernel interrupt handler write to another register, which tells the hardware to stop sending interrupts - -Kernel messages and printks from inside QEMU are shown all together, to see that more clearly, run in <> instead. - -We don't enable the device by default because it does not work for vanilla QEMU, which we often want to test with this repository. - -Probe already does a MMIO write, which generates an IRQ and tests everything. - [[qemu-edu]] ===== QEMU edu PCI device @@ -10307,7 +10209,7 @@ In our case for example, we see: 00:07.0 Unclassified device [00ff]: Device 1234:11e9 .... -which we identify as being `edu` and `pci_min` respectively by the magic numbers: `1234:11e?` +which we identify as being <> by the magic number: `1234:11e8`. Alternatively, we can also do use the QEMU monitor: @@ -10318,16 +10220,6 @@ Alternatively, we can also do use the QEMU monitor: which gives: .... - dev: lkmc_pci_min, id "" - addr = 07.0 - romfile = "" - rombar = 1 (0x1) - multifunction = false - command_serr_enable = true - x-pcie-lnksta-dllla = true - x-pcie-extcap-init = true - class Class 00ff, addr 00:07.0, pci id 1234:11e9 (sub 1af4:1100) - bar 0: mem at 0xfeb54000 [0xfeb54007] dev: edu, id "" addr = 06.0 romfile = "" @@ -10358,7 +10250,7 @@ Get the values of a given config register from its human readable name, either w .... setpci -s 0000:00:06.0 BASE_ADDRESS_0 -setpci -d 1234:11e9 BASE_ADDRESS_0 +setpci -d 1234:11e8 BASE_ADDRESS_0 .... Note however that `BASE_ADDRESS_0` also appears when you do: @@ -10379,13 +10271,11 @@ Then you can try messing with that address with <>: devmem 0xfeb54000 w 0x12345678 .... -which writes to the first register of our <> device. +which writes to the first register of the edu device. -The device then fires an interrupt at irq 11, which is unhandled, which leads the kernel to say you are a bad boy: +The device then fires an interrupt at irq 11, which is unhandled, which leads the kernel to say you are a bad person: .... -lkmc_pci_min mmio_write addr = 0 val = 12345678 size = 4 -<5>[ 1064.042435] random: crng init done <3>[ 1065.567742] irq 11: nobody cared (try booting with the "irqpoll" option) .... @@ -10398,7 +10288,7 @@ insmod irq.ko devmem 0xfeb54000 w 0x12345678 .... -Our kernel module handles the interrupt, but does not acknowledge it like our proper <> kernel module, and so it keeps firing, which leads to infinitely many messages being printed: +Our kernel module handles the interrupt, but does not acknowledge it like our proper edu kernel module, and so it keeps firing, which leads to infinitely many messages being printed: .... handler irq = 11 dev = 251 @@ -10569,55 +10459,6 @@ Relevant kernel files: * `drivers/leds/led-class.c` * `drivers/leds/leds-sysctl.c` -[[platform-device]] -==== platform_device - -Minimal platform device example coded into the `-M versatilepb` SoC of our QEMU fork. - -Using this device now requires checking out to the branch: - -.... -git checkout platform-device -git submodule sync -.... - -before building, it does not work on master. - -Rationale: we found out that the kernels that build for `qemu -M versatilepb` don't work on gem5 because `versatilepb` is an old pre-v7 platform, and gem5 requires armv7. So we migrated over to `-M virt` to have a single kernel for both gem5 and QEMU, and broke this since the single kernel was more important. TODO port to `-M virt`. - -The module itself can be found at: https://github.com/cirosantilli/linux-kernel-module-cheat/blob/platform-device/kernel_modules/platform_device.c - -Uses: - -* `hw/misc/lkmc_platform_device.c` minimal device added in our QEMU fork to `-M versatilepb` -* the device tree entry we added to our Linux kernel fork: https://github.com/cirosantilli/linux/blob/361bb623671a52a36a077a6dd45843389a687a33/arch/arm/boot/dts/versatile-pb.dts#L42 - -Expected outcome after insmod: - -* QEMU reports MMIO with printfs -* IRQs are generated and handled by this module, which logs to dmesg - -Without insmoding this module, try writing to the register with <>: - -.... -devmem 0x101e9000 w 0x12345678 -.... - -We can also observe the interrupt with <>: - -.... -modprobe dummy-irq irq=34 -insmod platform_device.ko -.... - -The IRQ number `34` was found by on the dmesg after: - -.... -insmod platform_device.ko -.... - -Bibliography: https://stackoverflow.com/questions/28315265/how-to-add-a-new-device-in-qemu-source-code/44612957#44612957 - ==== gem5 educational hardware models TODO get some working! diff --git a/kernel_modules/pci_min.c b/kernel_modules/pci_min.c deleted file mode 100644 index 8049093..0000000 --- a/kernel_modules/pci_min.c +++ /dev/null @@ -1,91 +0,0 @@ -/* https://cirosantilli.com/linux-kernel-module-cheat#pci-min */ - -#include -#include -#include -#include -#include -#include -#include -#include /* copy_from_user, copy_to_user */ - -#define BAR 0 -#define CDEV_NAME "lkmc_hw_pci_min" -#define EDU_DEVICE_ID 0x11e9 -#define QEMU_VENDOR_ID 0x1234 - -static struct pci_device_id id_table[] = { - { PCI_DEVICE(QEMU_VENDOR_ID, EDU_DEVICE_ID), }, - { 0, } -}; -MODULE_DEVICE_TABLE(pci, id_table); -static int major; -static struct pci_dev *pdev; -static void __iomem *mmio; -static struct file_operations fops = { - .owner = THIS_MODULE, -}; - -static irqreturn_t irq_handler(int irq, void *dev) -{ - pr_info("irq_handler irq = %d dev = %d\n", irq, *(int *)dev); - iowrite32(0, mmio + 4); - return IRQ_HANDLED; -} - -static int probe(struct pci_dev *dev, const struct pci_device_id *id) -{ - pr_info("probe\n"); - major = register_chrdev(0, CDEV_NAME, &fops); - pdev = dev; - if (pci_enable_device(dev) < 0) { - dev_err(&(pdev->dev), "pci_enable_device\n"); - goto error; - } - if (pci_request_region(dev, BAR, "myregion0")) { - dev_err(&(pdev->dev), "pci_request_region\n"); - goto error; - } - mmio = pci_iomap(pdev, BAR, pci_resource_len(pdev, BAR)); - pr_info("dev->irq = %u\n", dev->irq); - if (request_irq(dev->irq, irq_handler, IRQF_SHARED, "pci_irq_handler0", &major) < 0) { - dev_err(&(dev->dev), "request_irq\n"); - goto error; - } - iowrite32(0x12345678, mmio); - return 0; -error: - return 1; -} - -static void remove(struct pci_dev *dev) -{ - pr_info("remove\n"); - free_irq(dev->irq, &major); - pci_release_region(dev, BAR); - unregister_chrdev(major, CDEV_NAME); -} - -static struct pci_driver pci_driver = { - .name = CDEV_NAME, - .id_table = id_table, - .probe = probe, - .remove = remove, -}; - -static int myinit(void) -{ - if (pci_register_driver(&pci_driver) < 0) { - return 1; - } - return 0; -} - -static void myexit(void) -{ - pci_unregister_driver(&pci_driver); -} - -module_init(myinit); -module_exit(myexit); -MODULE_LICENSE("GPL");