Initial commit (Bazel version)

* Add Bazel build environment setup
* Kelvin startup asm and linker script
* A hello_world example

Change-Id: I7b3b246f1f22900d47475d0aa50204d2e174193a
diff --git a/.bazelrc b/.bazelrc
new file mode 100644
index 0000000..99fc0bd
--- /dev/null
+++ b/.bazelrc
@@ -0,0 +1,8 @@
+build --action_env=BAZEL_CXXOPTS="-std=gnu++14"
+build --cxxopt='-std=gnu++14'
+build --conlyopt='-std=gnu11'
+
+# Enable toolchain resolution with cc
+build --incompatible_enable_cc_toolchain_resolution
+
+build:kelvin --platforms=//platforms/riscv32:kelvin
diff --git a/.bazelversion b/.bazelversion
new file mode 100644
index 0000000..ac14c3d
--- /dev/null
+++ b/.bazelversion
@@ -0,0 +1 @@
+5.1.1
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ac51a05
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+bazel-*
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3cc5013
--- /dev/null
+++ b/README.md
@@ -0,0 +1,51 @@
+# Kelvin SW Repository
+
+This project contains the BSP to build the SW artifact that can run on the
+Kelvin core, and integrated as part of the Shodan repository.
+
+The project supports two build systems -- Bazel and CMake -- for OSS integration
+reasons. Bazel is used by [TFLM](https://github.com/tensorflow/tflite-micro)
+flow, while CMake is the build system for [IREE](https://github.com/openxla/iree).
+
+## Prerequisite
+
+If you get this project from Project Shodan manifest, you are all set. If not,
+you need to have following projects as well to build the project successfully.
+
+* Kelvin crosscompile toolchain: Under `<dir>/cache/toolchain_kelvin`
+
+This project needs to be at `<dir>/sw/kelvin`.
+
+## Code structure
+
+* build_tools: Build tool/rules for both Bazel and CMake
+* crt: Kelvin BSP
+* examples: Source code to build Kelvin SW artifacts.
+* platforms: Crosscompile platform setup for Bazel.
+* third_party: Third party repositories for Bazel.
+* toolchains: Crosscomple toolchain setup for Bazel.
+
+## Build the project
+
+### Bazel
+
+The project uses Bazel 5.1.1, to align with
+[OpenTitan](https://github.com/lowRISC/opentitan) build system requirements.
+
+```bash
+bazel build //...
+```
+
+### CMake
+
+```note
+TODO: Add CMake flow
+```
+
+## Run the executable
+
+```note
+TODO: Add kelvin simulator
+```
+
+Load the generated `.bin` binaries to matcha FPGA emulator.
diff --git a/WORKSPACE b/WORKSPACE
new file mode 100644
index 0000000..60c42e0
--- /dev/null
+++ b/WORKSPACE
@@ -0,0 +1,33 @@
+workspace(name = "kelvin")
+
+load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe")
+load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
+
+# Kelvin toolchain
+new_local_repository(
+    name = "kelvin-gcc",
+    build_file = "third_party/kelvin-gcc/BUILD",
+    path = "../../cache/toolchain_kelvin",
+)
+
+# CRT is the Compiler Repository Toolkit.  It contains the configuration for
+# the windows compiler.
+maybe(
+    http_archive,
+    name = "crt",
+    url = "https://github.com/lowRISC/crt/archive/refs/tags/v0.3.4.tar.gz",
+    sha256 = "01a66778d1a0d5bbfb4ba30e72bd6876d0c20766d0b1921ab36ca3350cb48c60",
+    strip_prefix = "crt-0.3.4",
+)
+
+load("@crt//:repos.bzl", "crt_repos")
+
+crt_repos()
+
+load("@crt//:deps.bzl", "crt_deps")
+
+crt_deps()
+
+load("//platforms:registration.bzl", "kelvin_register_toolchain")
+
+kelvin_register_toolchain()
diff --git a/build_tools/bazel/BUILD b/build_tools/bazel/BUILD
new file mode 100644
index 0000000..ffd0fb0
--- /dev/null
+++ b/build_tools/bazel/BUILD
@@ -0,0 +1 @@
+package(default_visibility = ["//visibility:public"])
diff --git a/build_tools/bazel/kelvin.bzl b/build_tools/bazel/kelvin.bzl
new file mode 100644
index 0000000..84d037b
--- /dev/null
+++ b/build_tools/bazel/kelvin.bzl
@@ -0,0 +1,183 @@
+"""Rules to build Kelvin SW objects"""
+
+load("@rules_cc//cc:find_cc_toolchain.bzl", "find_cc_toolchain")
+
+
+KELVIN_PLATFORM = "//platforms/riscv32:kelvin"
+
+def _kelvin_transition_impl(_settings, _attr):
+    return {"//command_line_option:platforms": KELVIN_PLATFORM}
+
+kelvin_transition = transition(
+    implementation = _kelvin_transition_impl,
+    inputs = [],
+    outputs = ["//command_line_option:platforms"],
+)
+
+def kelvin_rule(**kwargs):
+    """Kelvin-specific transition rule.
+
+    A wrapper over rule() for creating rules that trigger
+    the transition to the kelvin platform config.
+
+    Args:
+      **kwargs: params forwarded to the implementation.
+    Returns:
+      Kelvin transition rule.
+    """
+    attrs = kwargs.pop("attrs", {})
+    if "platform" not in attrs:
+        attrs["platform"] = attr.string(default = KELVIN_PLATFORM)
+    attrs["_allowlist_function_transition"] = attr.label(
+        default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
+    )
+
+    return rule(
+        cfg = kelvin_transition,
+        attrs = attrs,
+        **kwargs
+    )
+
+def _kelvin_binary_impl(ctx):
+    """Implements compilation for kelvin executables.
+
+    This rule compiles and links provided input into an executable
+    suitable for use on the Kelvin core. Generates both an ELF
+    and a BIN.
+
+    Args:
+      ctx: context for the rules.
+        srcs: Input source files.
+        deps: Target libraries that the binary depends upon.
+        hdrs: Header files that are local to the binary.
+        copts: Flags to pass along to the compiler.
+        defines: Preprocessor definitions.
+        linkopts: Flags to pass along to the linker.
+
+    Output:
+        OutputGroupsInfo to allow definition of filegroups
+        containing the output ELF and BIN.
+    """
+    cc_toolchain = find_cc_toolchain(ctx).cc
+    feature_configuration = cc_common.configure_features(
+        ctx = ctx,
+        cc_toolchain = cc_toolchain,
+        requested_features = ctx.features,
+        unsupported_features = ctx.disabled_features,
+    )
+    compilation_contexts = []
+    linking_contexts = []
+    for dep in ctx.attr.deps:
+        if CcInfo in dep:
+            compilation_contexts.append(dep[CcInfo].compilation_context)
+            linking_contexts.append(dep[CcInfo].linking_context)
+    (_compilation_context, compilation_outputs) = cc_common.compile(
+        actions = ctx.actions,
+        cc_toolchain = cc_toolchain,
+        feature_configuration = feature_configuration,
+        name = ctx.label.name,
+        srcs = ctx.files.srcs,
+        compilation_contexts = compilation_contexts,
+        private_hdrs = ctx.files.hdrs,
+        user_compile_flags = ctx.attr.copts,
+        defines = ctx.attr.defines,
+    )
+    linking_outputs = cc_common.link(
+        name = "{}.elf".format(ctx.label.name),
+        actions = ctx.actions,
+        feature_configuration = feature_configuration,
+        cc_toolchain = cc_toolchain,
+        compilation_outputs = compilation_outputs,
+        linking_contexts = linking_contexts,
+        user_link_flags = ctx.attr.linkopts + [
+            "-Wl,-T,{}".format(ctx.file.linker_script.path),
+        ],
+        additional_inputs = depset([ctx.file.linker_script] + ctx.files.linker_script_includes),
+        output_type = "executable",
+    )
+
+    binary = ctx.actions.declare_file(
+        "{}.bin".format(
+            ctx.attr.name,
+        ),
+    )
+    ctx.actions.run(
+        outputs = [binary],
+        inputs = [linking_outputs.executable] + cc_toolchain.all_files.to_list(),
+        arguments = [
+            "-g",
+            "-O",
+            "binary",
+            linking_outputs.executable.path,
+            binary.path,
+        ],
+        executable = cc_toolchain.objcopy_executable,
+    )
+
+    return [
+        DefaultInfo(
+            files = depset([linking_outputs.executable, binary]),
+        ),
+        OutputGroupInfo(
+            all_files = depset([linking_outputs.executable, binary]),
+            elf_file = depset([linking_outputs.executable]),
+            bin_file = depset([binary]),
+        ),
+    ]
+
+kelvin_binary_impl = kelvin_rule(
+    implementation = _kelvin_binary_impl,
+    attrs = {
+        "srcs": attr.label_list(allow_files = True),
+        "deps": attr.label_list(allow_empty = True, providers = [CcInfo]),
+        "hdrs": attr.label_list(allow_files = [".h"], allow_empty = True),
+        "copts": attr.string_list(),
+        "defines": attr.string_list(),
+        "linkopts": attr.string_list(),
+        "linker_script": attr.label(allow_single_file = True),
+        "linker_script_includes": attr.label_list(default = [], allow_files = True),
+        "_cc_toolchain": attr.label(default = Label("@bazel_tools//tools/cpp:current_cc_toolchain")),
+    },
+    fragments = ["cpp"],
+    toolchains = ["@rules_cc//cc:toolchain_type"],
+)
+
+def kelvin_binary(name, srcs, **kwargs):
+    """A helper macro for generating binary artifacts for the kelvin core.
+
+    This macro uses the kelvin toolchain, kelvin-specific starting asm,
+    and kelvin linker script to build kelvin binaries.
+
+    Args:
+      name: The name of this rule.
+      srcs: The c source files.
+      **kwargs: Additional arguments forward to cc_binary.
+    Emits rules:
+      filegroup              named: <name>.bin
+        Containing the binary output for the target.
+      filegroup              named: <name>.elf
+        Containing all elf output for the target.
+    """
+    srcs = srcs + [
+        "//crt:kelvin_gloss.cc",
+        "//crt:kelvin_start.S",
+        "//crt:crt.S",
+    ]
+    kelvin_binary_impl(
+        name = name,
+        srcs = srcs,
+        linker_script = "//crt:kelvin.ld",
+        **kwargs
+    )
+
+    # Need to create the following filegroups to make the output discoverable.
+    native.filegroup(
+        name = "{}.bin".format(name),
+        srcs = [name],
+        output_group = "bin_file",
+    )
+    native.filegroup(
+        name = "{}.elf".format(name),
+        srcs = [name],
+        output_group = "elf_file",
+    )
diff --git a/crt/BUILD b/crt/BUILD
new file mode 100644
index 0000000..72a00c5
--- /dev/null
+++ b/crt/BUILD
@@ -0,0 +1,9 @@
+package(default_visibility = ["//visibility:public"])
+
+# Exports the kelvin files to be used by the kelvin_binary()
+exports_files([
+    "crt.S",
+    "kelvin.ld",
+    "kelvin_gloss.cc",
+    "kelvin_start.S",
+])
diff --git a/crt/crt.S b/crt/crt.S
new file mode 100644
index 0000000..e5c34f5
--- /dev/null
+++ b/crt/crt.S
@@ -0,0 +1,141 @@
+// Copyright 2023 Google LLC.
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+/**
+ * CRT library
+ *
+ * Utility functions written in assembly that can be used before the C
+ * runtime has been initialized. These functions should not be used once
+ * the C runtime has been initialized.
+ *
+ * The name of this library is historical. In many toolchains, a file called
+ * "crt0.o" is linked into each executable, which does something similar to
+ * what these functions
+ */
+
+  // NOTE: The "ax" flag below is necessary to ensure that this section
+  // is allocated space in ROM by the linker.
+  .section .crt, "ax", @progbits
+
+  /**
+   * Write zeros into the section bounded by the start and end pointers.
+   * The section must be word (4 byte) aligned. It is valid for the section
+   * to have a length of 0 (i.e. the start and end pointers may be equal).
+   *
+   * This function follows the standard ILP32 calling convention for arguments
+   * but does not require a valid stack pointer, thread pointer or global
+   * pointer.
+   *
+   * Clobbers a0 and t0.
+   *
+   * @param a0 pointer to start of section to clear (inclusive).
+   * @param a1 pointer to end of section to clear (exclusive).
+   */
+  .balign 4
+  .global crt_section_clear
+  .type crt_section_clear, @function
+crt_section_clear:
+
+  // Check that start is before end.
+  bgeu a0, a1, .L_clear_nothing
+
+  // Check that start and end are word aligned.
+  or   t0, a0, a1
+  andi t0, t0, 0x3
+  bnez t0, .L_clear_error
+
+.L_clear_loop:
+  // Write zero into section memory word-by-word.
+  // TODO: unroll
+  sw   zero, 0(a0)
+  addi a0, a0, 4
+  bltu a0, a1, .L_clear_loop
+  ret
+
+.L_clear_nothing:
+  // If section length is 0 just return. Otherwise end is before start
+  // which is invalid so trigger an error.
+  bne a0, a1, .L_clear_error
+  ret
+
+.L_clear_error:
+  ebreak
+
+  // Set function size to allow disassembly.
+  .size crt_section_clear, .-crt_section_clear
+
+// -----------------------------------------------------------------------------
+
+  /**
+   * Copy data from the given source into the section bounded by the start and
+   * end pointers. Both the section and the source must be word (4 byte) aligned.
+   * It is valid for the section to have a length of 0 (i.e. the start and end
+   * pointers may be equal). The source is assumed to have the same length as the
+   * target section.
+   *
+   * The destination section and the source must not overlap.
+   *
+   * This function follows the standard ILP32 calling convention for arguments
+   * but does not require a valid stack pointer, thread pointer or global
+   * pointer.
+   *
+   * Clobbers a0, a2, t0 and t1.
+   *
+   * @param a0 pointer to start of destination section (inclusive).
+   * @param a1 pointer to end of destination section (exclusive).
+   * @param a2 pointer to source data (inclusive).
+   */
+  .balign 4
+  .global crt_section_copy
+  .type crt_section_copy, @function
+crt_section_copy:
+
+  // Check that start is before end.
+  bgeu a0, a1, .L_copy_nothing
+
+  // Check that start, end and src are word aligned.
+  or   t0, a0, a1
+  or   t0, t0, a2
+  andi t0, t0, 0x3
+  bnez t0, .L_copy_error
+
+  // Check that source does not destructively overlap destination
+  // (assuming a forwards copy).
+  //
+  // src  start          end
+  //  |     |             |
+  //  +-------------+     |
+  //  |   source    |     |
+  //  +-------------+     |
+  //        +-------------+
+  //        | destination |
+  //        +-------------+
+  //        |             |
+  //      start          end
+  //
+  // TODO: disallow all overlap since it indicates API misuse?
+  sub  t0, a0, a2           // (start - src) mod 2**32
+  sub  t1, a1, a0           // end - start
+  bltu t0, t1, .L_copy_error
+
+.L_copy_loop:
+  // Copy data from src into section word-by-word.
+  // TODO: unroll
+  lw   t0, 0(a2)
+  addi a2, a2, 4
+  sw   t0, 0(a0)
+  addi a0, a0, 4
+  bltu a0, a1, .L_copy_loop
+  ret
+
+.L_copy_nothing:
+  // If section length is 0 just return. Otherwise end is before start
+  // which is invalid so trigger an error.
+  bne a0, a1, .L_copy_error
+  ret
+
+.L_copy_error:
+  ebreak
+  .size crt_section_copy, .-crt_section_copy
diff --git a/crt/kelvin.ld b/crt/kelvin.ld
new file mode 100644
index 0000000..08f3542
--- /dev/null
+++ b/crt/kelvin.ld
@@ -0,0 +1,135 @@
+/* Copyright 2023 Google LLC. */
+/* Licensed under the Apache License, Version 2.0, see LICENSE for details. */
+/* SPDX-License-Identifier: Apache-2.0 */
+
+/**
+ *  A simple linker script for kelvin core.
+ *
+ * Memory layout:
+ *
+ *  TCM_ORIGIN          --->   +=====================+
+ *                             |                     |
+ *                             |       .text         |
+ *                             +---------------------|
+ *                             |       .crt          |
+ *                             +---------------------+
+ *                             |      .rodata        |
+ *                             +---------------------+
+ *                             |    .init_array      |
+ *                             +---------------------+
+ *                             |       .data         |
+ *                             +---------------------+
+ *                             |       .bss          |
+ *                             +---------------------+
+ *                             |       .heap         |
+ *                             |  (All unclamied     |
+ *                             |       memory)       |
+ *                             |                     |
+ *  (TCM_END - stack    --->   +---------------------+
+ *     - model_output          |       .stack        |
+ *     - output_header)        +---------------------+
+ *                             |   .model_output     |
+ *  output_header (64B) --->   +---------------------+
+ *                             |   .output_header    |
+ *  TCM_END             --->   +=====================+
+ **/
+
+MEMORY {
+  TCM(rwx): ORIGIN = 0x00000000, LENGTH = 0x400000
+}
+
+STACK_SIZE = DEFINED(STACK_SIZE) ? STACK_SIZE : DEFINED(__stack_size__) ? __stack_size__ : 0x4000;
+OUTPUT_SIZE = DEFINED(OUTPUT_SIZE) ? OUTPUT_SIZE : DEFINED(__output_size__) ? __output_size__ : 0x40;
+__output_header_size__ = 0x40;
+
+ENTRY(_start)
+
+SECTIONS {
+  . = ORIGIN(TCM);
+  .text : ALIGN(4) {
+    *(._init)
+    *(.text)
+    *(.text.*)
+    . = ALIGN(4);
+    __etext = .;
+  } > TCM
+
+  .init.array : ALIGN(4) {
+    __init_array_start = .;
+    *(.init_array)
+    *(.init_array.*)
+    . = ALIGN(4);
+    __init_array_end = .;
+  } > TCM
+
+  .rodata : ALIGN(4) {
+    *(.srodata)
+    *(.srodata.*)
+    *(.rodata)
+    *(.rodata.*)
+    . = ALIGN(4);
+  } > TCM
+
+  .data : ALIGN(4) {
+    __data_start__ = .;
+    /**
+    * This will get loaded into `gp`, and the linker will use that register for
+    * accessing data within [-2048,2047] of `__global_pointer$`.
+    *
+    * This is much cheaper (for small data) than materializing the
+    * address and loading from that (which will take one extra instruction).
+    */
+    _global_pointer = . + 0x800;
+    *(.sdata)
+    *(.sdata.*)
+    *(.data)
+    *(.data.*)
+    /* Align on 256 width. */
+    . = ALIGN(256);
+      __data_end__ = .;
+  } > TCM
+
+  .bss : ALIGN(4) {
+    __bss_start__ = .;
+    *(.sbss)
+    *(.sbss.*)
+    *(.bss)
+    *(.bss.*)
+    __bss_end__ = .;
+  } > TCM
+
+   /* All the unclaimed memory is used in heap */
+  .heap (NOLOAD) : {
+    . = ALIGN(64);
+    __heap_start__ = .;
+    . = ORIGIN(TCM) + LENGTH(TCM) - STACK_SIZE - OUTPUT_SIZE - __output_header_size__ - 63;
+    . = ALIGN(64);
+    __heap_end__ = .;
+  } > TCM
+
+  .stack ORIGIN(TCM) + LENGTH(TCM) - STACK_SIZE - OUTPUT_SIZE - __output_header_size__ (NOLOAD) : {
+    __stack_start__ = .;
+    . += STACK_SIZE;
+    . = ALIGN(64);
+    __stack_end__ = .;
+    _stack_end = .;
+  } > TCM
+
+  .model_output ORIGIN(TCM) + LENGTH(TCM) - OUTPUT_SIZE - __output_header_size__ (NOLOAD) : {
+    . = ALIGN(64);
+    __model_output_start__ = .;
+    *(.model_output)
+    __model_output_end__ = .;
+  } > TCM
+
+  .output_header ORIGIN(TCM) + LENGTH(TCM) - __output_header_size__ (NOLOAD) : {
+    /*
+     * Model output information (return code and output location/length) is
+     * always at the end of TCM.
+     */
+    __output_header_start__ = .;
+    _ret = .;
+    *(.model_output_header*)
+    __output_header_end__ = .;
+  } > TCM
+}
diff --git a/crt/kelvin_gloss.cc b/crt/kelvin_gloss.cc
new file mode 100644
index 0000000..c5859f1
--- /dev/null
+++ b/crt/kelvin_gloss.cc
@@ -0,0 +1,104 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+// Syscall stubs for newlib on Kelvin
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+void* __dso_handle = reinterpret_cast<void*>(&__dso_handle);
+
+extern "C" int _close(int file) { return -1; }
+
+extern "C" int _fstat(int file, struct stat* st) {
+  if (file != STDOUT_FILENO && file != STDERR_FILENO) {
+    errno = EBADF;
+    return -1;
+  }
+
+  if (st == NULL) {
+    errno = EFAULT;
+    return -1;
+  }
+
+  st->st_mode = S_IFCHR;
+  return 0;
+}
+
+extern "C" int _isatty(int file) {
+  errno = ENOTTY;
+  return 1;
+}
+
+extern "C" int _lseek(int file, int ptr, int dir) {
+  if (file != STDOUT_FILENO && file != STDERR_FILENO) {
+    errno = EBADF;
+    return -1;
+  }
+
+  return 0;
+}
+
+extern "C" int _read(int file, char* ptr, int len) {
+  errno = EBADF;
+  return -1;
+}
+
+// TODO(hcindyl/lundong): implement printf properly.
+extern "C" int _write(int file, char* ptr, int len) {
+  errno = EBADF;
+  return -1;
+}
+
+extern "C" int _open(const char* path, int flags, ...) { return -1; }
+
+extern "C" void _exit(int status) {
+  asm volatile("ebreak");
+  while (1) {
+  }
+}
+
+extern "C" int _kill(int pid, int sig) {
+  asm volatile("ebreak");
+  return -1;
+}
+
+extern "C" int _getpid(void) {
+  asm volatile("ebreak");
+  return -1;
+}
+
+char* _heap_ptr;  // Set to __heap_start__ in kelvin_start.S
+
+extern "C" void* _sbrk(int bytes) {
+  extern char __heap_end__;
+  char* prev_heap_end;
+  if ((bytes < 0) || (_heap_ptr + bytes > &__heap_end__)) {
+    errno = ENOMEM;
+    return reinterpret_cast<void*>(-1);
+  }
+
+  prev_heap_end = _heap_ptr;
+  _heap_ptr += bytes;
+
+  return reinterpret_cast<void*>(prev_heap_end);
+}
+
+void operator delete(void* p) noexcept { free(p); }
+
+extern "C" void operator delete(void* p, uint32_t c) noexcept {
+  operator delete(p);
+}
diff --git a/crt/kelvin_start.S b/crt/kelvin_start.S
new file mode 100644
index 0000000..b162fdc
--- /dev/null
+++ b/crt/kelvin_start.S
@@ -0,0 +1,78 @@
+// Copyright 2023 Google LLC.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+//
+// A starting functions for simple kelvin programs.
+
+/**
+ * Entry point.
+ */
+        .section ._init
+        .balign 4
+        .global _start
+        .type _start, @function
+_start:
+        ###############################################
+        # Put all scalar registers into a known state #
+        ###############################################
+        la   sp, _stack_end
+        la   gp, _global_pointer
+        la   s0, __heap_start__
+        sw   s0, _heap_ptr, s1
+        mv   tp, zero
+        mv   t1, zero
+        mv   t2, zero
+        mv   s0, zero
+        mv   s1, zero
+        mv   a1, zero
+        mv   a2, zero
+        mv   a3, zero
+        mv   a4, zero
+        mv   a5, zero
+        mv   a6, zero
+        mv   a7, zero
+        mv   s2, zero
+        mv   s3, zero
+        mv   s4, zero
+        mv   s5, zero
+        mv   s6, zero
+        mv   s7, zero
+        mv   s8, zero
+        mv   s9, zero
+        mv   s10, zero
+        mv   s11, zero
+        mv   t3, zero
+        mv   t4, zero
+        mv   t5, zero
+        mv   t6, zero
+
+        # Zero out the bss section
+        la   a0, __bss_start__
+        la   a1, __bss_end__
+        call crt_section_clear
+
+        # Initialize arrays
+        la   s0, __init_array_start
+        la   s1, __init_array_end
+        bgeu s0, s1, init_array_loop_end
+init_array_loop:
+        lw   t0, 0(s0)
+        jalr t0
+        addi s0, s0, 0x4
+        bltu s0, s1, init_array_loop
+init_array_loop_end:
+
+
+        #############
+        # Call main #
+        #############
+        li   a0, 0  # argv
+        li   a1, 0  # argc
+        la   ra, main
+        jalr ra, ra
+        # Store the application's return value at _ret
+        la   t0, _ret
+        sw   a0, 0(t0)
+        mpause      # Kelvin end of program op
+1:
+        j    1b
diff --git a/examples/hello_world/BUILD b/examples/hello_world/BUILD
new file mode 100644
index 0000000..6f91382
--- /dev/null
+++ b/examples/hello_world/BUILD
@@ -0,0 +1,8 @@
+load("//build_tools/bazel:kelvin.bzl", "kelvin_binary")
+
+kelvin_binary(
+    name = "hello_world",
+    srcs = [
+        "hello_world.c",
+    ],
+)
diff --git a/examples/hello_world/hello_world.c b/examples/hello_world/hello_world.c
new file mode 100644
index 0000000..e47bd5e
--- /dev/null
+++ b/examples/hello_world/hello_world.c
@@ -0,0 +1,52 @@
+// Copyright 2023 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// A Simple kelvin program.
+
+#include <stddef.h>
+#include <stdint.h>
+
+typedef struct {
+  uint32_t return_code;  // Populated in kelvin_start.S.
+  uint32_t output_ptr;
+  uint32_t length;
+} OutputHeader;
+
+__attribute__((section(".model_output_header"))) OutputHeader output_header = {
+    .output_ptr = 0,
+    .length = 0,
+};
+
+__attribute__((section(".model_output"))) uint32_t output;
+
+int main(int argc, char *argv[]) {
+  const uint32_t kDataSize = 0x1000 / sizeof(uint32_t);
+  uint32_t data[kDataSize];
+
+  for (int i = 0; i < sizeof(data) / sizeof(uint32_t); ++i) {
+    data[i] = i;
+  }
+
+  for (int i = 0; i < sizeof(data) / sizeof(uint32_t); ++i) {
+    data[i] += 1;
+  }
+
+  // Setup output.
+  output_header.length = sizeof(output);
+  output = data[kDataSize - 1];
+  output_header.output_ptr = (uint32_t)&output;
+
+  // invalidate all cache.
+  asm volatile("flushall");
+  return 0;
+}
diff --git a/platforms/BUILD b/platforms/BUILD
new file mode 100644
index 0000000..bb7a41a
--- /dev/null
+++ b/platforms/BUILD
@@ -0,0 +1,5 @@
+licenses(["notice"])
+
+package(
+    default_visibility = ["//visibility:public"],
+)
diff --git a/platforms/cpu/BUILD b/platforms/cpu/BUILD
new file mode 100644
index 0000000..4a03609
--- /dev/null
+++ b/platforms/cpu/BUILD
@@ -0,0 +1,12 @@
+licenses(["notice"])
+
+package(
+    default_visibility = ["//visibility:public"],
+)
+
+constraint_setting(name = "cpu")
+
+constraint_value(
+    name = "kelvin",
+    constraint_setting = ":cpu",
+)
diff --git a/platforms/registration.bzl b/platforms/registration.bzl
new file mode 100644
index 0000000..43e75c5
--- /dev/null
+++ b/platforms/registration.bzl
@@ -0,0 +1,5 @@
+"""Kelvin toolchain registration."""
+
+def kelvin_register_toolchain(name = "kelvin"):
+    native.register_execution_platforms("//platforms/riscv32:kelvin")
+    native.register_toolchains("//toolchains/kelvin:all")
diff --git a/platforms/riscv32/BUILD b/platforms/riscv32/BUILD
new file mode 100644
index 0000000..39cfec3
--- /dev/null
+++ b/platforms/riscv32/BUILD
@@ -0,0 +1,9 @@
+package(default_visibility = ["//visibility:public"])
+
+platform(
+    name = "kelvin",
+    constraint_values = [
+        "//platforms/cpu:kelvin",
+        "@platforms//os:none",
+    ],
+)
diff --git a/platforms/riscv32/devices.bzl b/platforms/riscv32/devices.bzl
new file mode 100644
index 0000000..3e0878b
--- /dev/null
+++ b/platforms/riscv32/devices.bzl
@@ -0,0 +1,19 @@
+load("@crt//config:device.bzl", "device_config")
+
+DEVICES = [
+    device_config(
+        name = "kelvin",
+        architecture = "rv32im",
+        feature_set = "//platforms/riscv32/features:rv32im",
+        constraints = [
+            "//platforms/cpu:kelvin",
+            "@platforms//os:none",
+        ],
+        substitutions = {
+            "ARCHITECTURE": "rv32im",
+            "ABI": "ilp32",
+            "CMODEL": "medany",
+            "[STACK_PROTECTOR]": "-fstack-protector-strong",
+        },
+    ),
+]
diff --git a/platforms/riscv32/features/BUILD b/platforms/riscv32/features/BUILD
new file mode 100644
index 0000000..98de11f
--- /dev/null
+++ b/platforms/riscv32/features/BUILD
@@ -0,0 +1,86 @@
+load(
+    "@crt//config:features.bzl",
+    "CPP_ALL_COMPILE_ACTIONS",
+    "C_ALL_COMPILE_ACTIONS",
+    "LD_ALL_ACTIONS",
+    "feature",
+    "feature_set",
+    "flag_group",
+    "flag_set",
+)
+
+package(default_visibility = ["//visibility:public"])
+
+feature(
+    name = "architecture",
+    enabled = True,
+    flag_sets = [
+        flag_set(
+            actions = CPP_ALL_COMPILE_ACTIONS + C_ALL_COMPILE_ACTIONS + LD_ALL_ACTIONS,
+            flag_groups = [
+                flag_group(
+                    flags = [
+                        "-march=ARCHITECTURE",
+                        "-mabi=ABI",
+                        "-mcmodel=CMODEL",
+                    ],
+                ),
+            ],
+        ),
+    ],
+)
+
+feature(
+    name = "sys_spec",
+    enabled = True,
+    flag_sets = [
+        flag_set(
+            actions = CPP_ALL_COMPILE_ACTIONS,
+            flag_groups = [
+                flag_group(
+                    flags = [
+                        "-march=ARCHITECTURE",
+                        "-mabi=ABI",
+                        "-mcmodel=CMODEL",
+                        "-nostdlib",
+                    ],
+                ),
+            ],
+        ),
+        flag_set(
+            actions = LD_ALL_ACTIONS,
+            flag_groups = [
+                flag_group(
+                    flags = [
+                        "--specs=nano.specs",
+                        "-lm_nano",
+                        "-lc_nano",
+                        "-lg_nano",
+                        "-lgcc",
+                        "-nostartfiles",
+                    ],
+                ),
+            ],
+        ),
+    ],
+)
+
+feature_set(
+    name = "rv32im",
+    feature = [
+        ":architecture",
+        ":sys_spec",
+        "@crt//features/embedded:cc_constructor_destructor",
+        "@crt//features/embedded:exceptions",
+        "@crt//features/embedded:runtime_type_information",
+        "@crt//platforms/riscv32/features:all_warnings_as_errors",
+        "@crt//platforms/riscv32/features:fastbuild",
+        "@crt//features/common:includes",
+        "@crt//features/common:all_warnings",
+        "@crt//features/common:all_warnings_as_errors",
+        "@crt//features/common:reproducible",
+        # TODO(atv): It would be nice to have the feature, but for now enabling
+        # this creates the wrong program.
+        # "@crt//features/common:symbol_garbage_collection",
+    ],
+)
diff --git a/third_party/kelvin-gcc/BUILD b/third_party/kelvin-gcc/BUILD
new file mode 100644
index 0000000..3148231
--- /dev/null
+++ b/third_party/kelvin-gcc/BUILD
@@ -0,0 +1,9 @@
+# Automatically generated by @crt//:compiler.bzl.  Do not edit.
+package(default_visibility = ["//visibility:public"])
+
+filegroup(
+    name = "all",
+    srcs = glob(["**"], exclude=["**/*.html","**/*.pdf"]),
+)
+
+exports_files(["bin/**"])
diff --git a/toolchains/kelvin/BUILD b/toolchains/kelvin/BUILD
new file mode 100644
index 0000000..ef7a6b7
--- /dev/null
+++ b/toolchains/kelvin/BUILD
@@ -0,0 +1,52 @@
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+
+# Forked from @crt//toolchains/lowrisc_rv32imcb/BUILD.bazel
+
+load("@crt//config:compiler.bzl", "setup")
+load("//platforms/riscv32:devices.bzl", "DEVICES")
+
+package(default_visibility = ["//visibility:public"])
+
+SYSTEM_INCLUDE_PATHS = [
+    "external/kelvin-gcc/riscv32-unknown-elf/include/c++/10.2.0",
+    "external/kelvin-gcc/riscv32-unknown-elf/include/c++/10.2.0/backward",
+    "external/kelvin-gcc/riscv32-unknown-elf/include/c++/10.2.0/riscv32-unknown-elf",
+    "external/kelvin-gcc/lib/gcc/riscv32-unknown-elf/10.2.0/include",
+    "external/kelvin-gcc/riscv32-unknown-elf/include",
+    "external/kelvin-gcc/lib/gcc/riscv32-unknown-elf/10.2.0/include-fixed",
+]
+
+filegroup(
+    name = "compiler_components",
+    srcs = [
+        "//toolchains/kelvin/wrappers:all",
+        "@kelvin-gcc//:all",
+    ],
+)
+
+[setup(
+    name = device.name,
+    architecture = device.architecture,
+    artifact_naming = device.artifact_naming,
+    compiler_components = ":compiler_components",
+    constraints = device.constraints,
+    feature_set = device.feature_set,
+    include_directories = SYSTEM_INCLUDE_PATHS,
+    params = {
+        "compiler": "gcc",
+    },
+    substitutions = device.substitutions,
+    tools = {
+        "ar": "wrappers/ar",
+        "cpp": "wrappers/cpp",
+        "gcc": "wrappers/gcc",
+        "gcov": "wrappers/gcov",
+        "ld": "wrappers/ld",
+        "nm": "wrappers/nm",
+        "objcopy": "wrappers/objcopy",
+        "objdump": "wrappers/objdump",
+        "strip": "wrappers/strip",
+    },
+) for device in DEVICES]
diff --git a/toolchains/kelvin/wrappers/BUILD b/toolchains/kelvin/wrappers/BUILD
new file mode 100644
index 0000000..7121e88
--- /dev/null
+++ b/toolchains/kelvin/wrappers/BUILD
@@ -0,0 +1,19 @@
+package(default_visibility = ["//visibility:public"])
+
+exports_files(glob(["*"]))
+
+filegroup(
+    name = "all",
+    srcs = [
+        "ar",
+        "clang",
+        "cpp",
+        "gcc",
+        "gcov",
+        "ld",
+        "nm",
+        "objcopy",
+        "objdump",
+        "strip",
+    ],
+)
diff --git a/toolchains/kelvin/wrappers/ar b/toolchains/kelvin/wrappers/ar
new file mode 120000
index 0000000..da2bdd9
--- /dev/null
+++ b/toolchains/kelvin/wrappers/ar
@@ -0,0 +1 @@
+driver.sh
\ No newline at end of file
diff --git a/toolchains/kelvin/wrappers/clang b/toolchains/kelvin/wrappers/clang
new file mode 120000
index 0000000..da2bdd9
--- /dev/null
+++ b/toolchains/kelvin/wrappers/clang
@@ -0,0 +1 @@
+driver.sh
\ No newline at end of file
diff --git a/toolchains/kelvin/wrappers/cpp b/toolchains/kelvin/wrappers/cpp
new file mode 120000
index 0000000..da2bdd9
--- /dev/null
+++ b/toolchains/kelvin/wrappers/cpp
@@ -0,0 +1 @@
+driver.sh
\ No newline at end of file
diff --git a/toolchains/kelvin/wrappers/driver.sh b/toolchains/kelvin/wrappers/driver.sh
new file mode 100755
index 0000000..eb2994e
--- /dev/null
+++ b/toolchains/kelvin/wrappers/driver.sh
@@ -0,0 +1,21 @@
+#!/bin/bash --norc
+# Copyright lowRISC contributors.
+# Licensed under the Apache License, Version 2.0, see LICENSE for details.
+# SPDX-License-Identifier: Apache-2.0
+
+PROG=$(basename "$0")
+DRIVER_DIR=$(dirname "$0")
+TOOLCHAIN="kelvin-gcc"
+PREFIX="riscv32-unknown-elf"
+
+ARGS=()
+POSTARGS=()
+case "${PROG}" in
+    gcc)
+        ;;
+esac
+
+exec "external/${TOOLCHAIN}/bin/${PREFIX}-${PROG}" \
+    "${ARGS[@]}" \
+    "$@"\
+    "${POSTARGS[@]}"
diff --git a/toolchains/kelvin/wrappers/gcc b/toolchains/kelvin/wrappers/gcc
new file mode 120000
index 0000000..da2bdd9
--- /dev/null
+++ b/toolchains/kelvin/wrappers/gcc
@@ -0,0 +1 @@
+driver.sh
\ No newline at end of file
diff --git a/toolchains/kelvin/wrappers/gcov b/toolchains/kelvin/wrappers/gcov
new file mode 120000
index 0000000..da2bdd9
--- /dev/null
+++ b/toolchains/kelvin/wrappers/gcov
@@ -0,0 +1 @@
+driver.sh
\ No newline at end of file
diff --git a/toolchains/kelvin/wrappers/ld b/toolchains/kelvin/wrappers/ld
new file mode 120000
index 0000000..da2bdd9
--- /dev/null
+++ b/toolchains/kelvin/wrappers/ld
@@ -0,0 +1 @@
+driver.sh
\ No newline at end of file
diff --git a/toolchains/kelvin/wrappers/nm b/toolchains/kelvin/wrappers/nm
new file mode 120000
index 0000000..da2bdd9
--- /dev/null
+++ b/toolchains/kelvin/wrappers/nm
@@ -0,0 +1 @@
+driver.sh
\ No newline at end of file
diff --git a/toolchains/kelvin/wrappers/objcopy b/toolchains/kelvin/wrappers/objcopy
new file mode 120000
index 0000000..da2bdd9
--- /dev/null
+++ b/toolchains/kelvin/wrappers/objcopy
@@ -0,0 +1 @@
+driver.sh
\ No newline at end of file
diff --git a/toolchains/kelvin/wrappers/objdump b/toolchains/kelvin/wrappers/objdump
new file mode 120000
index 0000000..da2bdd9
--- /dev/null
+++ b/toolchains/kelvin/wrappers/objdump
@@ -0,0 +1 @@
+driver.sh
\ No newline at end of file
diff --git a/toolchains/kelvin/wrappers/strip b/toolchains/kelvin/wrappers/strip
new file mode 120000
index 0000000..da2bdd9
--- /dev/null
+++ b/toolchains/kelvin/wrappers/strip
@@ -0,0 +1 @@
+driver.sh
\ No newline at end of file