diff --git a/stubdom/Makefile b/stubdom/Makefile
index 2a81af28a16e..5e919889836b 100644
--- a/stubdom/Makefile
+++ b/stubdom/Makefile
@@ -362,7 +362,7 @@ LINK_STAMPS := $(foreach dir,$(LINK_DIRS),$(dir)/stamp)
 mk-headers-$(XEN_TARGET_ARCH): $(IOEMU_LINKFARM_TARGET) $(LINK_STAMPS)
 	mkdir -p include/xen && \
           ln -sf $(wildcard $(XEN_ROOT)/xen/include/public/*.h) include/xen && \
-          ln -sf $(addprefix $(XEN_ROOT)/xen/include/public/,arch-x86 hvm io xsm) include/xen && \
+          ln -sf $(addprefix $(XEN_ROOT)/xen/include/public/,arch-x86 autogen hvm io xsm) include/xen && \
           ( [ -h include/xen/sys ] || ln -sf $(XEN_ROOT)/tools/include/xen-sys/MiniOS include/xen/sys ) && \
           ( [ -h include/xen/libelf ] || ln -sf $(XEN_ROOT)/tools/include/xen/libelf include/xen/libelf ) && \
 	  mkdir -p include/xen-foreign && \
diff --git a/tools/rust/Makefile b/tools/rust/Makefile
index f5db0a9c5e81..80e2f0e2817e 100644
--- a/tools/rust/Makefile
+++ b/tools/rust/Makefile
@@ -3,6 +3,11 @@ XEN_ROOT=$(CURDIR)/../..
 # Path to the Xen hypercall IDL parser
 XENBINDGEN=$(CURDIR)/xenbindgen
 
+# Output folder for the autogenerated C headers
+#
+# Must contain autogenerated files only because they're all wiped on update
+AUTOGEN_C=$(XEN_ROOT)/xen/include/public/autogen/
+
 # Clippy settings for all Rust projects
 CLIPPY_ARGS=-D warnings \
             -D missing_docs \
@@ -14,6 +19,20 @@ CLIPPY_ARGS=-D warnings \
 .PHONY: all install uninstall clean
 all install uninstall clean:
 
+# Remove all autogenerated files
+.PHONY: clean-autogen
+clean-autogen:
+	rm -rf "${AUTOGEN_C}"
+
+# Refresh autogenerated files. Depending on clean-autogen is required in order
+# for removals of specification files to cause the removal of their
+# autogenerated files.
+.PHONY: update
+update: clean-autogen
+	# Update C bindings
+	cargo run --manifest-path "${XENBINDGEN}/Cargo.toml" -- --lang c \
+	          --indir "${XENBINDGEN}/extra" --outdir "${AUTOGEN_C}"
+
 # Verify Rust crates pass lint checks. This is enforced in CI
 .PHONY: verify
 verify:
diff --git a/tools/rust/xenbindgen/extra/sysctl/readconsole.toml b/tools/rust/xenbindgen/extra/sysctl/readconsole.toml
new file mode 100644
index 000000000000..868743a453ff
--- /dev/null
+++ b/tools/rust/xenbindgen/extra/sysctl/readconsole.toml
@@ -0,0 +1,43 @@
+[[structs]]
+name = "xen_sysctl_readconsole"
+description = "Read console content from Xen buffer ring."
+
+[[structs.fields]]
+name = "clear"
+description = "IN: Non-zero -> clear after reading."
+typ = { tag = "u8" }
+
+[[structs.fields]]
+name = "incremental"
+description = "IN: Non-zero -> start index specified by `index` field."
+typ = { tag = "u8" }
+
+[[structs.fields]]
+name = "_pad"
+description = "Unused."
+typ = { tag = "u16" }
+
+[[structs.fields]]
+name = "index"
+description = """
+IN:  Start index for consuming from ring buffer (if @incremental);
+OUT: End index after consuming from ring buffer."""
+typ = { tag = "u32" }
+
+[[structs.fields]]
+name = "buffer"
+description = """
+IN: Virtual address to write console data.
+
+NOTE: The pointer itself is IN, but the contents of the buffer are OUT."""
+typ = { tag = "ptr", args = { tag = "u8" } }
+
+[[structs.fields]]
+name = "count"
+description = "IN: Size of buffer; OUT: Bytes written to buffer."
+typ = { tag = "u32" }
+
+[[structs.fields]]
+name = "rsvd0_a"
+description = "Tail padding reserved to zero."
+typ = { tag = "u32" }
diff --git a/xen/drivers/char/console.c b/xen/drivers/char/console.c
index 7da8c5296f3b..82f6ad7b32cb 100644
--- a/xen/drivers/char/console.c
+++ b/xen/drivers/char/console.c
@@ -42,6 +42,8 @@
 #include <asm/vpl011.h>
 #endif
 
+#include <public/xen.h>
+
 /* console: comma-separated list of console outputs. */
 static char __initdata opt_console[30] = OPT_CONSOLE_STR;
 string_param("console", opt_console);
@@ -108,8 +110,8 @@ size_param("conring_size", opt_conring_size);
 
 #define _CONRING_SIZE 16384
 #define CONRING_IDX_MASK(i) ((i)&(conring_size-1))
-static char __initdata _conring[_CONRING_SIZE];
-static char *__read_mostly conring = _conring;
+static uint8_t __initdata _conring[_CONRING_SIZE];
+static uint8_t *__read_mostly conring = _conring;
 static uint32_t __read_mostly conring_size = _CONRING_SIZE;
 static uint32_t conringc, conringp;
 
@@ -339,10 +341,10 @@ static void conring_puts(const char *str, size_t len)
 
 long read_console_ring(struct xen_sysctl_readconsole *op)
 {
-    XEN_GUEST_HANDLE_PARAM(char) str;
+    XEN_GUEST_HANDLE_PARAM(uint8_t) str;
     uint32_t idx, len, max, sofar, c, p;
 
-    str   = guest_handle_cast(op->buffer, char),
+    str   = guest_handle_cast(op->buffer, uint8_t),
     max   = op->count;
     sofar = 0;
 
@@ -1052,7 +1054,7 @@ void __init console_init_preirq(void)
 
 void __init console_init_ring(void)
 {
-    char *ring;
+    uint8_t *ring;
     unsigned int i, order, memflags;
     unsigned long flags;
 
diff --git a/xen/include/public/autogen/sysctl.h b/xen/include/public/autogen/sysctl.h
new file mode 100644
index 000000000000..f728b13374d3
--- /dev/null
+++ b/xen/include/public/autogen/sysctl.h
@@ -0,0 +1,35 @@
+/*
+ * sysctl
+ *
+ * AUTOGENERATED. DO NOT MODIFY
+ */
+#ifndef __XEN_AUTOGEN_SYSCTL_H
+#define __XEN_AUTOGEN_SYSCTL_H
+
+/* Read console content from Xen buffer ring. */
+struct xen_sysctl_readconsole {
+    /* IN: Non-zero -> clear after reading. */
+    uint8_t clear;
+    /* IN: Non-zero -> start index specified by `index` field. */
+    uint8_t incremental;
+    /* Unused. */
+    uint16_t _pad;
+    /*
+     * IN:  Start index for consuming from ring buffer (if @incremental);
+     * OUT: End index after consuming from ring buffer.
+     */
+    uint32_t index;
+    /*
+     * IN: Virtual address to write console data.
+     *
+     * NOTE: The pointer itself is IN, but the contents of the buffer are OUT.
+     */
+    XEN_GUEST_HANDLE_64(uint8) buffer;
+    /* IN: Size of buffer; OUT: Bytes written to buffer. */
+    uint32_t count;
+    /* Tail padding reserved to zero. */
+    uint32_t rsvd0_a;
+};
+
+#endif /* __XEN_AUTOGEN_SYSCTL_H */
+
diff --git a/xen/include/public/sysctl.h b/xen/include/public/sysctl.h
index b0fec271d36f..9e773490a5ac 100644
--- a/xen/include/public/sysctl.h
+++ b/xen/include/public/sysctl.h
@@ -18,6 +18,8 @@
 #include "domctl.h"
 #include "physdev.h"
 
+#include "autogen/sysctl.h"
+
 /*
  * The interface version needs to be incremented by 1 in case the interface
  * is modified in an incompatible way AND if the version hasn't been
@@ -30,27 +32,6 @@
  */
 #define XEN_SYSCTL_INTERFACE_VERSION 0x00000015
 
-/*
- * Read console content from Xen buffer ring.
- */
-/* XEN_SYSCTL_readconsole */
-struct xen_sysctl_readconsole {
-    /* IN: Non-zero -> clear after reading. */
-    uint8_t clear;
-    /* IN: Non-zero -> start index specified by @index field. */
-    uint8_t incremental;
-    uint8_t pad0, pad1;
-    /*
-     * IN:  Start index for consuming from ring buffer (if @incremental);
-     * OUT: End index after consuming from ring buffer.
-     */
-    uint32_t index;
-    /* IN: Virtual address to write console data. */
-    XEN_GUEST_HANDLE_64(char) buffer;
-    /* IN: Size of buffer; OUT: Bytes written to buffer. */
-    uint32_t count;
-};
-
 /* Get trace buffers machine base address */
 /* XEN_SYSCTL_tbuf_op */
 struct xen_sysctl_tbuf_op {
diff --git a/xen/include/public/xen.h b/xen/include/public/xen.h
index e051f989a5ca..cc5133fc19b8 100644
--- a/xen/include/public/xen.h
+++ b/xen/include/public/xen.h
@@ -36,6 +36,7 @@ __DEFINE_XEN_GUEST_HANDLE(ulong, unsigned long);
 #endif
 DEFINE_XEN_GUEST_HANDLE(void);
 
+DEFINE_XEN_GUEST_HANDLE(uint8_t);
 DEFINE_XEN_GUEST_HANDLE(uint64_t);
 DEFINE_XEN_GUEST_HANDLE(xen_pfn_t);
 DEFINE_XEN_GUEST_HANDLE(xen_ulong_t);
