view rathaxes_add_lkm_ethernet_sample.patch @ 38:f43900ad7e66

Fold All The Patches on the LKM Sample
author Louis Opter <louis@lse.epitech.net>
date Sun, 08 Jan 2012 01:51:34 +0100
parents rathaxes_add_a_linux_lkm.patch@44a25ffd5c8c
children d761c8c625d3
line wrap: on
line source

# HG changeset patch
# Parent b995d8934956b83383c144303178f3eb383d0acf
rathaxes: add the PCI/Ethernet part of a Linux Intel e1000 network card driver

diff --git a/rathaxes/samples/CMakeLists.txt b/rathaxes/samples/CMakeLists.txt
--- a/rathaxes/samples/CMakeLists.txt
+++ b/rathaxes/samples/CMakeLists.txt
@@ -1,2 +1,3 @@
 ADD_SUBDIRECTORY(helloworld)
+ADD_SUBDIRECTORY(lkm)
 ADD_SUBDIRECTORY(syntax)
diff --git a/rathaxes/samples/lkm/CMakeLists.txt b/rathaxes/samples/lkm/CMakeLists.txt
new file mode 100644
--- /dev/null
+++ b/rathaxes/samples/lkm/CMakeLists.txt
@@ -0,0 +1,7 @@
+ADD_RATHAXES_SOURCES(lkm lkm.rtx
+                     RTI log.rti lkm.rti pci.rti socket.rti ethernet.rti e1000.rti
+                     BLT log.blt lkm.blt pci.blt socket.blt ethernet.blt e1000.blt)
+
+# We can't name lkm since it's already used as the target name to generate the
+# source (with ADD_RATHAXES_SOURCES).
+ADD_RATHAXES_LKM(lkm_hello lkm)
diff --git a/rathaxes/samples/lkm/e1000.blt b/rathaxes/samples/lkm/e1000.blt
new file mode 100644
--- /dev/null
+++ b/rathaxes/samples/lkm/e1000.blt
@@ -0,0 +1,80 @@
+with e1000, Ethernet, Socket, PCI, LKM, Log
+{
+    template type   e1000::Context()
+    {
+        chunk   LKM::includes()
+        {
+            /*
+             * Force the generation of the structure in the "headers" part, we
+             * have to do this since we do not use the structure in this blt
+             * (we hacked a bit and used it in ethernet.blt directly).
+             */
+            typedef int ${e1000::Context}; /* CNorm __std__ workaround */
+            ${e1000::Context} force_declaration_in_includes;
+        }
+
+        chunk   ::decl()
+        {
+            struct rtx_e1000_ctx
+            {
+                int                             bars;
+                unsigned char /* __iomem */     *ioaddr;
+            };
+        }
+
+        map
+        {
+        }
+    }
+
+    template sequence   e1000::create_device()
+    {
+        chunk Ethernet::create_device()
+        {
+            rtx_ether_ctx->hw_ctx.bars = pci_select_bars(pdev, IORESOURCE_MEM);
+            if (pci_enable_device_mem(pdev))
+            {
+                ${Log::info("e1000::create: pci_enable_device_mem failed")};
+            }
+
+            if (pci_request_selected_regions(pdev, rtx_ether_ctx->hw_ctx.bars, ${config.name}))
+            {
+                ${Log::info("e1000::create: pci_request_selected_regions failed")};
+            }
+
+            if (${config.set_master})
+            {
+                pci_set_master(pdev);
+            }
+
+            /* 0 here is for BAR_0: */
+            rtx_ether_ctx->hw_ctx.ioaddr = pci_ioremap_bar(pdev, 0);
+            if (!rtx_ether_ctx->hw_ctx.ioaddr)
+            {
+                ${Log::info("e1000::create: pci_ioremap_bar failed")};
+            }
+        }
+
+        chunk ::CALL
+        {
+        }
+    }
+
+    template sequence   e1000::destroy_device()
+    {
+        chunk   Ethernet::destroy_device
+        {
+            /*
+             * Here, we should have some checks to avoid to free resources that
+             * haven't been allocated. (e.g: in case of previous errors).
+             */
+            struct rtx_ethernet_dev* rtx_ether_ctx = netdev_priv(net_dev);
+            iounmap(rtx_ether_ctx->hw_ctx.ioaddr);
+            pci_release_selected_regions(pdev, rtx_ether_ctx->hw_ctx.bars);
+        }
+
+        chunk   ::CALL
+        {
+        }
+    }
+}
diff --git a/rathaxes/samples/lkm/e1000.rti b/rathaxes/samples/lkm/e1000.rti
new file mode 100644
--- /dev/null
+++ b/rathaxes/samples/lkm/e1000.rti
@@ -0,0 +1,24 @@
+interface e1000 : Socket, Ethernet, PCI, LKM
+{
+    provided type   e1000::Context;
+
+    /*
+     * This sequence should receive an argument like Ethernet::Device, but it is
+     * unclear about how this argument should be bound to a variable/argument in
+     * the instrumented C code.
+     *
+     * Here again, we rely on the fact that *we* wrote the parent context and
+     * named the C variables we need/use with the same name everywhere.
+     */
+    provided sequence   e1000::create_device()
+    {
+        provided chunk  Ethernet::create_device;
+        provided chunk  ::CALL;
+    }
+
+    provided sequence   e1000::destroy_device()
+    {
+        provided chunk  Ethernet::destroy_device;
+        provided chunk  ::CALL;
+    }
+}
diff --git a/rathaxes/samples/lkm/ethernet.blt b/rathaxes/samples/lkm/ethernet.blt
new file mode 100644
--- /dev/null
+++ b/rathaxes/samples/lkm/ethernet.blt
@@ -0,0 +1,207 @@
+with Ethernet, PCI, LKM, Log
+{
+    template type   Ethernet::Device()
+    {
+        chunk LKM::includes()
+        {
+            #include <linux/netdevice.h>
+            #include <linux/etherdevice.h>
+        }
+
+        chunk ::decl()
+        {
+            struct rtx_ethernet_dev
+            {
+                /*
+                 * I think it's useless to use the ${PCI::Device} "abstraction"
+                 * here, since we are already in a Linux specific context here.
+                 */
+                struct pci_dev          *pci_dev;
+                struct net_device       *net_dev;
+
+                /* while waiting on issue #8 */
+                struct rtx_e1000_ctx    hw_ctx;
+            };
+        }
+
+        chunk ::init(net_dev)
+        {
+            ${self} = ${net_dev};
+        }
+
+        map
+        {
+        }
+    }
+
+    template sequence   Ethernet::open(Ethernet::Device dev)
+    {
+        chunk LKM::prototypes()
+        {
+            static int  rtx_ethernet_open(struct net_device *);
+        }
+
+        chunk LKM::code()
+        {
+            static int  rtx_ethernet_open(struct net_device *dev)
+            {
+                ${pointcut ::IMPLEMENTATION};
+
+                return 0;
+            }
+        }
+    }
+
+    template sequence   Ethernet::send(Ethernet::Device dev, Socket::SKBuff skb)
+    {
+        chunk LKM::prototypes()
+        {
+            static int  rtx_ethernet_xmit(struct sk_buff* skb, struct net_device *dev);
+        }
+
+        chunk LKM::code()
+        {
+            static int  rtx_ethernet_xmit(struct sk_buff* skb, struct net_device *dev)
+            {
+                ${pointcut ::IMPLEMENTATION};
+
+                return 0;
+            }
+        }
+    }
+
+    template sequence   Ethernet::close(Ethernet::Device dev)
+    {
+        chunk LKM::prototypes()
+        {
+            static int  rtx_ethernet_close(struct net_device *);
+        }
+
+        chunk LKM::code()
+        {
+            static int  rtx_ethernet_close(struct net_device *dev)
+            {
+                ${pointcut ::IMPLEMENTATION};
+
+                return 0;
+            }
+        }
+    }
+
+    template sequence   Ethernet::interrupt_handler(Ethernet::Device dev)
+    {
+        /*
+         * We can't use the irqreturn_t type here because CNornm doesn't know
+         * it.
+         */
+        chunk LKM::prototypes()
+        {
+            static enum irqreturn   rtx_ethernet_interrupt_handler(int, void *);
+        }
+
+        chunk LKM::code()
+        {
+            static enum irqreturn   rtx_ethernet_interrupt_handler(int irq, void *dev_id)
+            {
+                ${pointcut ::IMPLEMENTATION};
+
+                return IRQ_NONE;
+            }
+        }
+    }
+
+    template sequence   Ethernet::init(PCI::Device pdev)
+    {
+        chunk LKM::data()
+        {
+            static const struct net_device_ops  rtx_ether_ops =
+            {
+                .ndo_open = rtx_ethernet_open,
+                .ndo_stop = rtx_ethernet_close,
+                .ndo_start_xmit = rtx_ethernet_xmit,
+            };
+        }
+
+        chunk PCI::pci_probe_hook()
+        {
+            /*
+             * This typedef is needed to workaround a bug in CNorm __std__
+             * dialect.
+             */
+            typedef int ${Ethernet::Device};
+            ${Ethernet::Device} *rtx_ether_ctx;
+            struct net_device   *net_dev;
+            int error;
+
+            error = 0;
+            net_dev = alloc_etherdev(sizeof(*rtx_ether_ctx));
+            if (net_dev == 0)
+            {
+                ${Log::info("Cannot allocate memory")};
+                /*
+                 * Again, the error should be "raised" in the parent context.
+                 *
+                 * Here we know that we should return ENOMEM because *we* wrote
+                 * the parent context.
+                 */
+                return -ENOMEM;
+            }
+            strlcpy(net_dev->name, ${config.ifname}, sizeof(net_dev->name));
+            net_dev->irq = pdev->irq;
+            // Maybe we should try ${rtx_ether_ctx.init()} here:
+            rtx_ether_ctx = netdev_priv(net_dev);
+            //rtx_ether_ctx->pci_dev = ${pdev};
+            rtx_ether_ctx->pci_dev = pdev; // In the meantime do it directly
+            rtx_ether_ctx->net_dev = net_dev;
+
+            /*
+             * The substitution of ${pdev} fails here. I also tried to add a
+             * "substitute method" to the PCI::Device that was just doing
+             * "${self}" but it didn't work either (it was subsituted by a
+             * placeholder, e.g: _1).
+             *
+             * That's why we cheated a bit and named all the arguments pdev.
+             */
+            //SET_NETDEV_DEV(net_dev, &${pdev}->dev);
+            SET_NETDEV_DEV(net_dev, &pdev->dev);
+            net_dev->netdev_ops = &rtx_ether_ops;
+            if ((error = register_netdev(net_dev)))
+            {
+                ${Log::info("Cannot register the driver")};
+                return error;
+            }
+
+            /* same problem as above with ${pdev} */
+            //pci_set_drvdata(${pdev}, net_dev);
+            pci_set_drvdata(pdev, net_dev);
+
+            ${pointcut Ethernet::create_device};
+        }
+
+        chunk   ::CALL
+        {
+        }
+    }
+
+    template sequence   Ethernet::exit(PCI::Device pdev)
+    {
+        chunk   PCI::pci_remove_hook()
+        {
+            struct net_device *net_dev = pci_get_drvdata(pdev);
+
+            ${pointcut Ethernet::destroy_device};
+
+            unregister_netdev(net_dev);
+            /*
+             * If we had some cleanup todo with struct rtx_ether_ctx we would
+             * do a netdev_priv(net_dev) here and do it.
+             */
+            free_netdev(net_dev);
+        }
+
+        chunk ::CALL
+        {
+        }
+    }
+}
+
diff --git a/rathaxes/samples/lkm/ethernet.rti b/rathaxes/samples/lkm/ethernet.rti
new file mode 100644
--- /dev/null
+++ b/rathaxes/samples/lkm/ethernet.rti
@@ -0,0 +1,47 @@
+interface Ethernet : Socket, PCI, LKM
+{
+    provided type       Ethernet::Device;
+
+    required variable ::string  Ethernet::ifname;
+
+    required sequence   Ethernet::open(Ethernet::Device)
+    {
+        provided chunk  LKM::prototypes;
+        provided chunk  LKM::code;
+    }
+
+    required sequence   Ethernet::send(Ethernet::Device dev, Socket::SKBuff skb)
+    {
+        provided chunk  LKM::prototypes;
+        provided chunk  LKM::code;
+    }
+
+    required sequence   Ethernet::close(Ethernet::Device)
+    {
+        provided chunk  LKM::prototypes;
+        provided chunk  LKM::code;
+    }
+
+    required sequence   Ethernet::interrupt_handler(Ethernet::Device)
+    {
+        provided chunk  LKM::prototypes;
+        provided chunk  LKM::code;
+    }
+
+    provided sequence   Ethernet::init(PCI::Device)
+    {
+        provided chunk  LKM::data;
+        provided chunk  PCI::pci_probe_hook;
+        provided chunk  ::CALL;
+
+        provided pointcut   Ethernet::create_device;
+    }
+
+    provided sequence   Ethernet::exit(PCI::Device)
+    {
+        provided chunk  ::CALL;
+        provided chunk  PCI::pci_remove_hook;
+
+        provided pointcut   Ethernet::destroy_device;
+    }
+}
diff --git a/rathaxes/samples/lkm/lkm.blt b/rathaxes/samples/lkm/lkm.blt
new file mode 100644
--- /dev/null
+++ b/rathaxes/samples/lkm/lkm.blt
@@ -0,0 +1,55 @@
+with LKM
+{
+    /* Skel of the generated C file: */
+    ${pointcut LKM::includes};
+    ${pointcut LKM::prototypes};
+    ${pointcut LKM::data};
+    ${pointcut LKM::code};
+
+    template sequence   LKM::init()
+    {
+        chunk   LKM::includes()
+        {
+            #include <linux/module.h>
+            #include <linux/kernel.h>
+        }
+
+        chunk   LKM::data()
+        {
+            MODULE_DESCRIPTION(${config.description});
+            MODULE_AUTHOR(${config.author});
+            MODULE_LICENSE(${config.license});
+        }
+
+        chunk   LKM::code()
+        {
+            /*
+             * Rathaxes doesn't yet support arbitrary "decorators" like __init
+             * or __exit.
+             */
+            static int __attribute__((__section__(".init.text")))  rtx_module_init(void)
+            {
+                ${pointcut ::IMPLEMENTATION};
+                ${pointcut LKM::init_bus_hook};
+
+                return 0;
+            }
+
+            module_init(rtx_module_init);
+        }
+    }
+
+    template sequence   LKM::exit()
+    {
+        chunk   LKM::code()
+        {
+            static void __attribute__((__section__(".exit.text")))   rtx_module_exit(void)
+            {
+                ${pointcut ::IMPLEMENTATION};
+                ${pointcut LKM::deinit_bus_hook};
+            }
+
+            module_exit(rtx_module_exit);
+        }
+    }
+}
diff --git a/rathaxes/samples/lkm/lkm.rti b/rathaxes/samples/lkm/lkm.rti
new file mode 100644
--- /dev/null
+++ b/rathaxes/samples/lkm/lkm.rti
@@ -0,0 +1,25 @@
+interface LKM
+{
+    provided pointcut   LKM::includes;
+    /* maybe it should be possible to use chunk ::decl in sequence templates? */
+    provided pointcut   LKM::prototypes;
+    provided pointcut   LKM::data;
+    provided pointcut   LKM::code;
+
+    required variable ::string  LKM::author;
+    required variable ::string  LKM::description;
+    required variable ::string  LKM::license;
+
+    required sequence   LKM::init()
+    {
+        provided chunk      LKM::includes;
+        provided chunk      LKM::code;
+        provided pointcut   LKM::init_bus_hook;
+    }
+
+    required sequence   LKM::exit()
+    {
+        provided chunk      LKM::code;
+        provided pointcut   LKM::deinit_bus_hook;
+    }
+}
diff --git a/rathaxes/samples/lkm/lkm.rtx b/rathaxes/samples/lkm/lkm.rtx
new file mode 100644
--- /dev/null
+++ b/rathaxes/samples/lkm/lkm.rtx
@@ -0,0 +1,46 @@
+device LKM use LKM, PCI, Ethernet, Log
+{
+    Ethernet::open(Ethernet::Device dev)
+    {
+        Log::info("Open the device");
+    }
+
+    Ethernet::close(Ethernet::Device dev)
+    {
+        Log::info("Close the device");
+    }
+
+    Ethernet::interrupt_handler(Ethernet::Device dev)
+    {
+        Log::info("Got an interruption");
+    }
+
+    Ethernet::send(Ethernet::Device dev, Socket::SKBuff skb)
+    {
+        Log::info("We have one packet to transmit!");
+    }
+
+    LKM::init()
+    {
+        Log::info("Hello this is LKM");
+    }
+
+    LKM::exit()
+    {
+        Log::info("Good bye this was LKM");
+    }
+}
+
+configuration
+{
+    LKM::name = "hello";
+    LKM::author = "Rathaxes";
+    LKM::description = "Hello World Loadable Kernel Module (LKM)";
+    LKM::license = "GPL";
+
+    PCI::vendor_id = 0x8086;
+    PCI::product_id = 0x100f;
+    PCI::set_master = true;
+
+    Ethernet::ifname = "rtx%d";
+}
diff --git a/rathaxes/samples/lkm/log.blt b/rathaxes/samples/lkm/log.blt
new file mode 100644
--- /dev/null
+++ b/rathaxes/samples/lkm/log.blt
@@ -0,0 +1,10 @@
+with Log
+{
+    template sequence   Log::info(::string msg)
+    {
+        chunk   ::CALL
+        {
+            pr_info("%s\n", ${msg});
+        }
+    }
+}
diff --git a/rathaxes/samples/lkm/log.rti b/rathaxes/samples/lkm/log.rti
new file mode 100644
--- /dev/null
+++ b/rathaxes/samples/lkm/log.rti
@@ -0,0 +1,7 @@
+interface Log
+{
+    provided sequence   Log::info(::string)
+    {
+        provided chunk  ::CALL;
+    }
+}
diff --git a/rathaxes/samples/lkm/pci.blt b/rathaxes/samples/lkm/pci.blt
new file mode 100644
--- /dev/null
+++ b/rathaxes/samples/lkm/pci.blt
@@ -0,0 +1,143 @@
+with PCI, LKM, Log
+{
+    template type   PCI::Device()
+    {
+        chunk   LKM::includes()
+        {
+            #include <linux/pci.h>
+        }
+
+        chunk   ::decl()
+        {
+            struct pci_dev;
+        }
+
+        chunk   ::init(pci_dev)
+        {
+            ${self} = ${pci_dev};
+        }
+
+        map
+        {
+        }
+    }
+
+    template sequence   PCI::probe(PCI::Device pdev)
+    {
+        chunk LKM::prototypes()
+        {
+            static int /* __devinit */  rtx_pci_probe(struct pci_dev *,
+                                                      const struct pci_device_id *);
+        }
+
+        chunk LKM::code()
+        {
+            static int /* __devinit */  rtx_pci_probe(struct pci_dev *pdev,
+                                                      const struct pci_device_id *pdev_id)
+            {
+                int             err;
+
+                err = pci_enable_device(pdev);
+                if (err < 0)
+                    goto fail;
+
+                ${pointcut PCI::pci_probe_hook};
+
+                return 0;
+
+                fail:
+                    return err;
+            }
+        }
+
+        chunk   ::CALL
+        {
+        }
+    }
+
+    template sequence   PCI::remove(PCI::Device pdev)
+    {
+        chunk LKM::prototypes()
+        {
+            static void rtx_pci_remove(struct pci_dev *);
+        }
+
+        chunk LKM::code()
+        {
+            static void rtx_pci_remove(struct pci_dev *pdev)
+            {
+                ${pointcut PCI::pci_remove_hook};
+
+                pci_disable_device(pdev);
+            }
+        }
+
+        chunk   ::CALL()
+        {
+        }
+    }
+
+    template sequence   PCI::register()
+    {
+        chunk LKM::data()
+        {
+            /*
+             * CNorm doesn't seem to like "dynamic" arrays (i.e: you always
+             * have to specify the exact size).
+             */
+            static struct pci_device_id rtx_pci_device_table[2] = {
+                { ${config.vendor_id}, ${config.product_id}, PCI_ANY_ID, PCI_ANY_ID },
+                { 0, }
+            };
+
+            static struct pci_driver rtx_pci_driver = {
+                .name = ${config.name},
+                .id_table = rtx_pci_device_table,
+                .probe = rtx_pci_probe,
+                .remove = rtx_pci_remove
+            };
+        }
+
+        chunk LKM::init_bus_hook()
+        {
+            int error;
+            if ((error = pci_register_driver(&rtx_pci_driver)))
+            {
+                 ${Log::info("Cannot register pci driver")};
+                 /*
+                  * So we catched the error but how do we return it to the
+                  * parent context?
+                  *
+                  * Here we know that we can just return error, but that's just
+                  * a coincidence (and, in this case, *we* wrote the parent
+                  * context).
+                  */
+                 return error;
+            }
+        }
+
+        chunk ::CALL()
+        {
+            /*
+             * The implementation of ::CALL is empty. This template sequence is
+             * actually not provided nor required.
+             *
+             * This sequence is just "intermediate" code that will just inject
+             * itself in the hook LKM::init_bus_hook for which this sequence
+             * has a chunk (see above chunk).
+             */
+        }
+    }
+
+    template sequence   PCI::unregister()
+    {
+        chunk   LKM::deinit_bus_hook()
+        {
+            pci_unregister_driver(&rtx_pci_driver);
+        }
+
+        chunk ::CALL
+        {
+        }
+    }
+}
diff --git a/rathaxes/samples/lkm/pci.rti b/rathaxes/samples/lkm/pci.rti
new file mode 100644
--- /dev/null
+++ b/rathaxes/samples/lkm/pci.rti
@@ -0,0 +1,36 @@
+interface PCI : LKM
+{
+    provided type       PCI::Device;
+
+    required variable ::number  PCI::vendor_id;
+    required variable ::number  PCI::product_id;
+
+    provided sequence   PCI::register()
+    {
+        provided chunk  ::CALL;
+        provided chunk  LKM::data;
+        provided chunk  LKM::init_bus_hook;
+    }
+
+    provided sequence   PCI::unregister()
+    {
+        provided chunk  ::CALL;
+        provided chunk  LKM::deinit_bus_hook;
+    }
+
+    provided sequence   PCI::probe(PCI::Device)
+    {
+        provided chunk  LKM::prototypes;
+        provided chunk  LKM::code;
+
+        provided pointcut   PCI::pci_probe_hook;
+    }
+
+    provided sequence   PCI::remove(PCI::Device)
+    {
+        provided chunk  LKM::prototypes;
+        provided chunk  LKM::code;
+
+        provided pointcut   PCI::pci_remove_hook;
+    }
+}
diff --git a/rathaxes/samples/lkm/socket.blt b/rathaxes/samples/lkm/socket.blt
new file mode 100644
--- /dev/null
+++ b/rathaxes/samples/lkm/socket.blt
@@ -0,0 +1,27 @@
+with Socket, LKM
+{
+    template type Socket::SKBuff()
+    {
+        chunk LKM::includes()
+        {
+            #include <linux/skbuff.h>
+        }
+
+        chunk ::decl()
+        {
+            struct sk_buff;
+        }
+
+        chunk ::init()
+        {
+        }
+
+        map
+        {
+            // some work may have to be done here in order
+            // to access to some field of the sk_buff.
+            // We should determine if all the sk_buff managment
+            // can be abstracted from the user.
+        }
+    }
+}
diff --git a/rathaxes/samples/lkm/socket.rti b/rathaxes/samples/lkm/socket.rti
new file mode 100644
--- /dev/null
+++ b/rathaxes/samples/lkm/socket.rti
@@ -0,0 +1,4 @@
+interface Socket : LKM
+{
+    provided type Socket::SKBuff;
+}