[doc] Created readme2index pre-processor

We use the built-in `index` preprocessor to rename our `README.md` files
to `index.md`, but it doesn't currently fixup links:
<https://github.com/rust-lang/mdBook/issues/984>.

This is a temporary preprocessor to regex replace those links until
upstream gets fixed.

Signed-off-by: Hugo McNally <hugo.mcnally@gmail.com>
diff --git a/book.toml b/book.toml
index 47d0292..1a1eaf2 100644
--- a/book.toml
+++ b/book.toml
@@ -15,3 +15,6 @@
 git-repository-url = "https://github.com/lowrisc/opentitan"
 edit-url-template = "https://github.com/lowrisc/opentitan/edit/master/{path}"
 curly-quotes = true
+
+[preprocessor.readme2index]
+command = "./util/mdbook_readme2index.py"
diff --git a/util/mdbook/__init__.py b/util/mdbook/__init__.py
new file mode 100644
index 0000000..fee181e
--- /dev/null
+++ b/util/mdbook/__init__.py
@@ -0,0 +1,4 @@
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+"""Common module for mdbook pre-processors."""
diff --git a/util/mdbook/utils.py b/util/mdbook/utils.py
new file mode 100644
index 0000000..cf0c320
--- /dev/null
+++ b/util/mdbook/utils.py
@@ -0,0 +1,18 @@
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+"""Common utilities used by mdbook pre-processors."""
+
+from typing import List, Any, Dict, Generator
+
+
+def chapters(items: List[Dict[str, Any]]) -> Generator[Dict[str, Any], None, None]:
+    """Recursively yields all chapters"""
+    for chapter in (item.get("Chapter") for item in items):
+        if not chapter:
+            continue
+
+        for sub_chapter in chapters(chapter["sub_items"]):
+            yield sub_chapter
+
+        yield chapter
diff --git a/util/mdbook_readme2index.py b/util/mdbook_readme2index.py
new file mode 100755
index 0000000..d436318
--- /dev/null
+++ b/util/mdbook_readme2index.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python3
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+"""mdbook preprocessor that converts README.md to index.md
+
+We use the built-in `index` preprocessor to rename our `README.md` files to `index.md`,
+but it doesn't currently fixup links: https://github.com/rust-lang/mdBook/issues/984.
+This is a temporary preprocessor to regex replace those links until upstream gets fixed.
+"""
+
+import json
+import sys
+import re
+
+import mdbook.utils as md_utils
+
+# Finds all markdown links, `[...](...)`,
+# which link to a file called readme.md
+# To check it isn't a link no colon, `:`, is allowed before the readme.md .
+# `#` and '?' also aren't allowed before the readme.md,
+# in case `readme.md` is not the filename but in fact a fragment or selector.
+# It matches the link content before and after the readme into groups
+# so that it can be substituted back into the file.
+RM2IDX_PATTERN_INLINE = re.compile(
+    r"(\[[^\]]*\]\([^\)|#|:|\?]*)readme(\.md[^\)]*\))",
+    re.IGNORECASE,
+)
+
+# Similar to the pattern above but for `[...]: ...` style links.
+RM2IDX_PATTERN_NOT_INLINE = re.compile(
+    r"^(\[[^\]]*\]:\s+[^\n|#|:|\?]*)readme(\.md[^\n]*$)",
+    re.IGNORECASE | re.MULTILINE,
+)
+
+
+def main() -> None:
+    if len(sys.argv) > 2:
+        if (sys.argv[1], sys.argv[2]) == ("supports", "html"):
+            sys.exit(0)
+        else:
+            sys.exit(1)
+
+    # load both the context and the book from stdin
+    context, book = json.load(sys.stdin)
+
+    for chapter in md_utils.chapters(book["sections"]):
+        chapter["content"] = RM2IDX_PATTERN_INLINE.sub(r"\1index\2", chapter["content"])
+        chapter["content"] = RM2IDX_PATTERN_NOT_INLINE.sub(r"\1index\2", chapter["content"])
+
+    # dump the book into stdout
+    print(json.dumps(book))
+
+
+if __name__ == "__main__":
+    main()