This commit is contained in:
Ciro Santilli 六四事件 法轮功
2020-10-29 00:00:01 +00:00
parent 3990333f3d
commit b44ca1e5de
2 changed files with 5 additions and 255 deletions

View File

@@ -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 <<libc-choice,glibc as our default libc now>>, 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 <<your-first-qemu-hack,modify QEMU>> if you're into that sort of thing.
To build the kernel modules as in <<your-first-kernel-module-hack>> 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.
<<platform-device>> 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. <<platform-device>>.
==== /proc/interrupts
In the guest with <<qemu-graphic-mode>>:
@@ -10203,57 +10151,11 @@ For the more complex interfaces, we focus on simplified educational devices, eit
* present in the QEMU upstream:
** <<qemu-edu>>
* added in https://github.com/cirosantilli/qemu[our fork of QEMU]:
** <<pci-min>>
** <<platform-device>>
==== 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 <<qemu-graphic-mode>> 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 <<qemu-edu>> 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 <<dev-mem>>:
devmem 0xfeb54000 w 0x12345678
....
which writes to the first register of our <<pci-min>> 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 <<pci-min>> 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 <<dev-mem>>:
....
devmem 0x101e9000 w 0x12345678
....
We can also observe the interrupt with <<dummy-irq>>:
....
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!

View File

@@ -1,91 +0,0 @@
/* https://cirosantilli.com/linux-kernel-module-cheat#pci-min */
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/uaccess.h> /* 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");