diff --git a/20230515/bs-20230515.txt b/20230515/bs-20230515.txt
index 8f14e3a364f0602129f111d4feae6f83d446d36f..e7f437d115db2602c1ef99fe9f37afcddfab6e80 100644
--- a/20230515/bs-20230515.txt
+++ b/20230515/bs-20230515.txt
@@ -1,5 +1,5 @@
-Von "Hello, world!n" bis zum Kernel, 15.05.2023, 16:20:31
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Von "Hello, world!\n" bis zum Kernel, 15.05.2023, 16:20:31
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 printf ("Hello, world!\n"); wird zu puts ("Hello, world!"); optimiert.
 
 Quelltext von puts(): apt-get source glibc
diff --git a/20230522/bs-20230522.txt b/20230522/bs-20230522.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3767f5926ce59b033f4de227fbd2c281cfeb00f7
--- /dev/null
+++ b/20230522/bs-20230522.txt
@@ -0,0 +1,247 @@
+Vom syscall-Befehl bis zum Treiber-Modul, 22.05.2023, 16:11:55
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+arch/x86/entry/entry_64.S:
+  Assembler-Code für den Einsprung, nachdem das Benutzerprogramm
+  den syscall-Befehl aufgerufen hat
+  (einschließlich "trampoline": Code, der ermöglicht, ein Callback
+  auch mit einer anderen als der vorgesehenen Anzahl von Parametern
+  aufzurufen)
+  --> Funktionsaufruf do_syscall_64 (unsigned long a, struct pt_regs *b)
+  a = Nummer des Syscall-Aufrufs
+  b = Zeiger auf eine Datenstruktur auf dem CPU-Stack,
+      die die Registerinhalte des Benutzerprogramms enthält:
+      Stack-Segment, Stack-Pointer, CPU-Flags, Code-Segment, Instruction Pointer
+      (wichtig für den Zugriff auf Variable des aufrufenden Programms
+      sowie für den Rücksprung)
+
+arch/x86/entry/common.c:
+  if (unlikely(nr >= NR_syscalls))
+          goto bad;
+  --> Hinweis an den Compiler, welcher Zweig einer if-Verzweigung
+      wahrscheinlicher ist, mit dem Ziel, Pipelining möglichst effizient
+      zu unterstützen
+  nr = array_index_nospec(nr, NR_syscalls)
+  --> Mache aus dem Übergebenen Parameter (Nr. des Syscalls)
+      einen Index für ein Array.
+  Wenn alles funktioniert hat:
+    regs->ax = sys_call_table[nr](regs);
+  --> Aufruf der Funktion, die den Syscall durchführt.
+      Das Ergebnis des Funktionsaufrufs speichern wir in der Struktur, die
+      die Registerinhalte des Aufrufers enthält. Der Aufrufer bekommt somit
+      den Funktionsrückgabewert in seinem ax-Register (genauer: %rax).
+  sys_call_table ist eine Tabelle mit Zeigern auf Funktionen.
+  Worauf zeigen diese?
+
+arch/x86/entry/syscalls/syscall_64.tbl:
+  0       common  read                    __x64_sys_read
+  1       common  write                   __x64_sys_write
+  2       common  open                    __x64_sys_open
+  ...
+arch/x86/entry/syscalls/syscalltbl.sh:
+  Dieses Shell-Skript macht aus der o.a. Tabelle eine Datei "syscalls_64.h"
+  mit Aufrufen eines Präprozessor-Macros:
+    __SYSCALL_64(0, __x64_sys_read, )
+    __SYSCALL_64(0, sys_read, )
+    __SYSCALL_64(0, __x64_sys_read, )
+    __SYSCALL_64(0, sys_read, )
+    __SYSCALL_64(1, __x64_sys_write, )
+    __SYSCALL_64(1, sys_write, )
+    __SYSCALL_64(2, __x64_sys_open, )
+    __SYSCALL_64(2, sys_open, )
+    ...
+arch/x86/entry/syscall_64.c:
+  #define __SYSCALL_64(nr, sym, qual) extern asmlinkage long sym(const struct pt_regs *);
+  #include <asm/syscalls_64.h>
+  #undef __SYSCALL_64
+  --> Extern-Deklarationen aller SysCall-Funktionen (Prototypen)
+      Dadurch möglich: Zeiger auf diese Funktionen zeigen lassen
+  #define __SYSCALL_64(nr, sym, qual) [nr] = sym,
+
+  asmlinkage const sys_call_ptr_t sys_call_table[__NR_syscall_max+1] = {
+          /*
+           * Smells like a compiler bug -- it doesn't work
+           * when the & below is removed.
+           */
+          [0 ... __NR_syscall_max] = &sys_ni_syscall,
+  #include <asm/syscalls_64.h>
+  };
+  --> Dies ist die Tabelle, ein initialisiertes Array.
+      Bei jedem Index "nr" steht ein Zeiger auf die Funktion "sym".
+      Vorher initialisieren wir das ganze Array auf "&sys_ni_syscall"
+      ("not implemented syscall").
+
+Wie sorgen wir nun dafür, daß unser Einsprungpunkt "entry_SYSCALL_64_trampoline"
+tatsächlich aufgerufen wird, wenn ein Benutzerprogramm den syscall-Befehl aufruft?
+
+arch/x86/kernel/cpu/common.c:
+  void syscall_init(void)
+  {       
+    ...
+    wrmsr(MSR_STAR, 0, (__USER32_CS << 16) | __KERNEL_CS);
+    if (static_cpu_has(X86_FEATURE_PTI))
+            wrmsrl(MSR_LSTAR, SYSCALL64_entry_trampoline);
+    else    
+            wrmsrl(MSR_LSTAR, (unsigned long)entry_SYSCALL_64);
+    ...
+  }
+  --> Hier hinterlegen wir die Adresse von SYSCALL64_entry_trampoline
+      in einem internen Prozessorregister, das genau für diesen Aufruf
+      zuständig ist.
+      wrmsrl: In ein solches Register schreiben
+      MSR_STAR: Nummer des Registers, das dafür zuständig ist, sich die Segmente zu merken
+      (__KERNEL_CS = Code-Segment des Kernels)
+      MSR_LSTAR: Nummer des Registers, das dafür zuständig ist, sich das Offset zu merken
+
+Wo ist die Funktion sys_write() denn nun definiert?
+
+  include/linux/syscalls.h:
+
+    /* fs/read_write.c */
+    ...
+    asmlinkage long sys_write(unsigned int fd, const char __user *buf,
+                              size_t count);
+
+  fs/read_write.c:
+
+    ssize_t ksys_write(unsigned int fd, const char __user *buf, size_t count)
+    {       
+            struct fd f = fdget_pos(fd);
+            ssize_t ret = -EBADF;
+
+            if (f.file) {
+                    loff_t pos = file_pos_read(f.file);
+                    ret = vfs_write(f.file, buf, count, &pos);
+                    if (ret >= 0)
+                            file_pos_write(f.file, pos);
+                    fdput_pos(f);
+            }
+            
+            return ret;
+    }
+
+    SYSCALL_DEFINE3(write, unsigned int, fd, const char __user *, buf,
+                    size_t, count)
+    {       
+            return ksys_write(fd, buf, count);
+    }                                                                                                                                            
+
+  ebenfalls in fs/read_write.c:
+
+    ssize_t __vfs_write(struct file *file, const char __user *p, size_t count,
+                        loff_t *pos)
+    {       
+            if (file->f_op->write)
+                    return file->f_op->write(file, p, count, pos);
+            else if (file->f_op->write_iter)
+                    return new_sync_write(file, p, count, pos);
+            else    
+                    return -EINVAL;
+    }
+
+Damit sind wir eigentlich am Ziel.
+
+Noch offen: Wie gelangen die "fops"-Callbacks aus dem Kernel-Modul
+in die "struct file"-Datenstruktur?
+
+Dies führt uns zu dem Konzept der inodes (index nodes).
+
+  include/linux/fs.h:
+
+    struct file {
+            union {
+                    struct llist_node       fu_llist;
+                    struct rcu_head         fu_rcuhead;
+            } f_u;
+            struct path             f_path;
+            struct inode            *f_inode;       /* cached value */
+            const struct file_operations    *f_op;
+
+            /*
+             * Protects f_ep_links, f_flags.
+             * Must not be taken from IRQ context.
+             */
+            spinlock_t              f_lock;
+            enum rw_hint            f_write_hint;
+            atomic_long_t           f_count;
+            unsigned int            f_flags;
+            fmode_t                 f_mode;
+            struct mutex            f_pos_lock;
+            loff_t                  f_pos;
+            struct fown_struct      f_owner;
+            const struct cred       *f_cred;
+            struct file_ra_state    f_ra;
+
+            u64                     f_version;
+    #ifdef CONFIG_SECURITY
+            void                    *f_security;
+    #endif
+            /* needed for tty driver, and maybe others */
+            void                    *private_data;
+
+    #ifdef CONFIG_EPOLL
+            /* Used by fs/eventpoll.c to link all the hooks to this file */
+            struct list_head        f_ep_links;
+            struct list_head        f_tfile_llink;
+    #endif /* #ifdef CONFIG_EPOLL */
+            struct address_space    *f_mapping;
+            errseq_t                f_wb_err;
+    } __randomize_layout
+      __attribute__((aligned(4)));  /* lest something weird decides that 2 is OK */
+
+    ...
+
+    struct file_operations {
+      ...
+      ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
+      ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
+      ...
+    } __randomize_layout;
+
+Dieselbe "struct file_operations" übergeben wir in Kernel-Modulen beim Aufruf
+von register_chrdev().
+
+  ebenfalls in include/linux/fs.h:
+
+    struct inode {
+            ...
+            dev_t                   i_rdev;
+            ...
+            const struct file_operations    *i_fop; /* former ->i_op->default_file_ops */
+            ...
+            union {
+                    struct pipe_inode_info  *i_pipe;
+                    struct block_device     *i_bdev;
+                    struct cdev             *i_cdev;
+                    char                    *i_link;
+                    unsigned                i_dir_seq;
+            };
+            ...
+    } __randomize_layout;
+
+  --> Ein inode repräsentiert entweder eine Pipe oder ein Block Device oder ein Char Device
+      oder einen Symlink oder ein Verzeichnis oder eine normale Datei.
+
+  include/linux/cdev.h:
+
+    struct cdev {
+            struct kobject kobj;
+            struct module *owner;
+            const struct file_operations *ops;
+            struct list_head list;
+            dev_t dev;
+            unsigned int count;
+    } __randomize_layout;
+
+  include/linux/types.h:
+
+    typedef u32 __kernel_dev_t;
+    ...
+    typedef __kernel_dev_t          dev_t;
+
+  include/linux/kdev_t.h:
+
+    #define MAJOR(dev)      ((unsigned int) ((dev) >> MINORBITS))
+    #define MINOR(dev)      ((unsigned int) ((dev) & MINORMASK))
+
+Damit haben wir auch gefunden, wie die Datei ihre file_operations
+aus dem Kernel-Modul bekommt (nämlich über den inode).
diff --git a/20230522/trampoline-01.pas b/20230522/trampoline-01.pas
new file mode 100644
index 0000000000000000000000000000000000000000..465cae325ee8ee10198ff58e7ef9948abd553eb8
--- /dev/null
+++ b/20230522/trampoline-01.pas
@@ -0,0 +1,15 @@
+program Trampoline;
+
+procedure MacheWas (procedure Callback (a: Integer));
+begin
+  Callback (42)
+end;
+
+procedure Answer (a: Integer);
+begin
+  WriteLn ('The answer is: ', a)
+end;
+
+begin
+  MacheWas (Answer)
+end.
diff --git a/20230522/trampoline-02.pas b/20230522/trampoline-02.pas
new file mode 100644
index 0000000000000000000000000000000000000000..75343c0ba34c8077b92d3f9da72b651bae8199c7
--- /dev/null
+++ b/20230522/trampoline-02.pas
@@ -0,0 +1,24 @@
+program Trampoline;
+
+procedure MacheWas (procedure Callback (a: Integer));
+begin
+  Callback (42)
+end;
+
+procedure Philosophy;
+var
+  Msg: String[42];
+
+  procedure Answer (a: Integer);
+  begin
+    WriteLn (Msg, a)
+  end;
+
+begin
+  Msg := 'The answer is: ';
+  MacheWas (Answer)
+end;
+
+begin
+  Philosophy
+end.