blob: b79d87ca32e6329880f35eabf8d6249564340b13 [file] [log] [blame]
# Copyright 2019 The Fuchsia Authors
#
# Use of this source code is governed by a MIT-style
# license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT
import("//build/config/zircon/standard.gni")
import("//build/dist/resource.gni")
import("//build/toolchain/zircon/zircon_toolchain_suite.gni")
import("//build/zbi/zbi.gni")
import("//build/zbi/zbi_input.gni")
import("//build/zircon/c_utils.gni")
import("//build/zircon/zircon_cpu.gni")
import("//src/devices/bus/drivers/pci/pci.gni")
import("//zircon/kernel/lib/code-patching/code-patching.gni")
import("//zircon/kernel/lib/version/version_string.gni")
import("kernel_package.gni")
import("params.gni")
if (current_cpu == "x64") {
declare_args() {
# **TODO(fxbug.dev/32255): This is a temporary switch that will be removed.**
#
# Set this to make the ZBI compatible with older boot loaders such as a
# gigaboot or zedboot image already installed on a machine that's hard to
# update. This is an interim workaround only for people who have machines
# that are not physically accessible to update their boot images, and will
# be removed after everyone has had a chance to get hold of their machines.
deprecated_x86_legacy_boot_protocol = false
}
}
if (platform_enable_user_pci) {
disable_kernel_pci = true
}
if (current_toolchain == default_toolchain) {
# In the default toolchain, just define the kernel toolchains.
foreach(cpu, standard_fuchsia_cpus) {
zircon_toolchain_suite("kernel_$cpu") {
cpu = cpu
os = "fuchsia"
environment = "kernel"
toolchain_tags = [
"kernel",
"standalone",
]
prefix_configs = [
"//zircon/kernel:headers",
# TODO(fxbug.dev/44971): Many include/ headers use <ktl/*.h> headers.
"//zircon/kernel/lib/ktl:headers.config",
"//sdk/lib/fit:headers.config",
"//sdk/lib/stdcompat:headers.config",
# <stdlib.h> has #include <lib/heap.h>.
"//zircon/kernel/lib/heap:headers.config",
# <lib/lockup_detector.h> is included by some "low-level" headers.
"//zircon/kernel/lib/lockup_detector:headers.config",
# <kernel/percpu.h> has #include <lib/lazy_init/lazy_init.h>.
"//zircon/system/ulib/lazy_init:headers.config",
# <kernel/spinlock.h> has #include <lockdep/lock_policy.h>.
"//zircon/system/ulib/lockdep:headers.config",
# <kernel/{scheduler.h,scheduler_state.h> has #include <ffl/fixed.h>.
"//zircon/system/ulib/ffl:headers.config",
# <kernel/thread.h> has #include <vm/kstack.h>.
"//zircon/kernel/vm:headers.config",
# <vm/vm_object.h> has #include <lib/user_copy/user_ptr.h>.
"//zircon/kernel/lib/user_copy:headers.config",
# <lib/ktrace.h> has #include <lib/zircon-internal/ktrace.h>.
"//zircon/system/ulib/zircon-internal:headers.config",
# <lib/ktrace.h> has #include <lib/ktrace/string_ref.h>.
"//zircon/kernel/lib/ktrace:headers.config",
"//zircon/kernel/lib/ktrace:suppress-warning.config",
# <kernel/thread.h> has #include <fbl/macros.h>
"//zircon/system/ulib/fbl:headers.config",
# <dev/iommu.h> has #include <fbl/name.h>
"//zircon/kernel/lib/fbl:headers.config",
# Everywhere has #include <lib/fitx/result.h>
# Everywhere has #include <zx/status.h>
"//zircon/system/ulib/zxc:headers.config",
"//zircon/kernel/lib/backtrace:headers.config",
"//zircon/kernel/lib/version:headers.config",
]
configs = [ "//zircon/kernel:kernel_config" ]
# NOTE: kernel artifacts currently do not build under fuzzer
# variants. This was also true with the Zircon build, but
# the Fuchsia build never invoked it with corresponding
# variant selectors. Using an exclude_variant_tag is
# enough to fix the issue.
exclude_variant_tags = [ "fuzzer" ]
# Always enable frame pointers in the kernel so there are panic
# backtraces and such.
# TODO(mcgrathr): Consider either removing this so there's a
# no-frame-pointers option, or removing the kernel's support for
# !WITH_FRAME_POINTERS if it will never be used.
remove_common_configs = [ "//build/config:default_frame_pointers" ]
configs += [ "//build/config:frame_pointers" ]
if (cpu == "x64") {
# TODO(fxbug.dev/27321): x86 kernel can't be built without --gc-sections
# because of crufty acpica code, and some compiler instrumentation
# is broken wrt --gc-sections semantics.
# Ensure that linker GC is enabled.
configs += [ "//build/config:linker_gc" ]
}
# As a special case, kernel assertions are controlled by the kernel_debug_level
# variable, and not zx_assert_level (though it defaults to the same value, but
# some product configurations may decide to change it).
remove_common_configs += [ "//build/config/zircon:default_assert_level" ]
# Map kernel_debug_level above 2 to an assert_level of 2, as that is the
# highest valid assertion level.
if (kernel_debug_level > 2) {
kernel_assert_level = 2
} else {
kernel_assert_level = kernel_debug_level
}
configs += [ "//build/config/zircon:assert_level_$kernel_assert_level" ]
# TODO(fxbug.dev/71321): the kernel crashes on boot if built with -O0.
_optimize_none = [ "//build/config:optimize_none" ]
if (configs + _optimize_none - _optimize_none != configs) {
configs -= _optimize_none
configs += [ "//build/config:optimize_debug" ]
}
}
}
}
# For any standalone static binary.
config("standalone") {
ldflags = [
"-nostdlib",
"-static",
]
cflags = [
"-ffreestanding",
"-include",
rebase_path("include/hidden.h", root_build_dir),
# We want `.debug_frame` for the kernel (fxbug.dev/30023). And we still want
# asynchronous unwind tables. Alas there's (currently) no way to
# achieve this with our GCC. At the moment we compile with
# `-fno-omit-frame-pointer`, which is good because we link with
# `--gc-sections`, which means `.eh_frame` gets discarded so GCC-built
# kernels don't have any unwind info (except for assembly)!
"-fno-unwind-tables",
]
# Always feed assembler code the `.cfi_sections` directive to
# populate only `.debug_frame` and not `.eh_frame`.
asmflags = [
"-include",
rebase_path("debug-frame.S", root_build_dir),
]
cflags_cc = [
# Underlying kernel heap only has default alignment of 8 bytes, so pass
# this to the compiler as the default new alignment.
"-faligned-new=8",
]
if (current_os != "win") {
if (current_cpu == "x64") {
# This only matters in an environment where interrupt handlers might
# push trap frames in the same privilege level, like the kernel.
# e.g. Multiboot probably doesn't actually need it, but it doesn't hurt.
cflags += [ "-mno-red-zone" ]
} else if (current_cpu == "arm64") {
# This matters if vector registers are not available, e.g. in the kernel
# since the they hold unsaved user state, or in the physmem environment
# because they might not be enabled in hardware yet.
cflags += [ "-mgeneral-regs-only" ]
}
}
if (is_gcc && current_os == "fuchsia") {
cflags += [ "-fpie" ]
}
if (!is_gcc && current_os == "fuchsia") {
# In the Fuchsia-target toolchains there's no way to prevent the
# compiler driver from passing -pie, so negate it. BFD ld doesn't
# have --no-pie, but arm64-elf-gcc doesn't pass -pie either.
ldflags += [ "-Wl,--no-pie" ]
}
configs = [ "//build/config:no_exceptions" ]
}
if (is_kernel) {
# These are needed both in kernel sources (pervasively) and in the linker
# scripts.
kernel_defines = [
# TODO: should not be needed in C, but is in one place now.
"KERNEL_BASE=$kernel_base",
"SMP_MAX_CPUS=$smp_max_cpus",
]
# This is the top config for all kernel code.
config("kernel_config") {
configs = [
":jtrace_config",
":lock_dep",
":scheduler",
":virtual_heap",
":standalone",
":warnings",
"arch/$zircon_cpu:abi",
"arch/$zircon_cpu:kernel",
# include/lib/counters.h and kernel.ld depend on -fdata-sections.
"//build/config/zircon:data_sections",
]
# TODO(fxbug.dev/27083): This dependency is conditional because when built
# with GCC the kernel uses function scoped statics requiring dynamic
# initialization. Once fxbug.dev/27083 is fixed, this dependency can be
# removed.
if (is_gcc) {
# Don't emit extra code making static initializers thread-safe.
configs += [ "//build/config/zircon:no_threadsafe_statics" ]
}
# Always enable frame pointers in the kernel so there are panic
# backtraces and such.
# TODO(mcgrathr): Consider either removing this so there's a
# no-frame-pointers option, or removing the kernel's support for
# !WITH_FRAME_POINTERS if it will never be used.
configs += [ "//build/config:frame_pointers" ]
defines = kernel_defines + kernel_extra_defines
defines += [
"_KERNEL",
"LK",
"ENABLE_PANIC_SHELL",
"ZIRCON_TOOLCHAIN",
"LK_DEBUGLEVEL=$kernel_debug_level",
"DEBUG_PRINT_LEVEL=$kernel_debug_print_level",
"VM_TRACING_LEVEL=$vm_tracing_level",
"FUTEX_TRACING_ENABLED=$futex_tracing_enabled",
]
if (!disable_kernel_pci) {
defines += [ "WITH_KERNEL_PCIE" ]
}
cflags = [ "-fpie" ]
}
group("kernel_config_deps") {
}
config("headers") {
include_dirs = [
"include",
"lib/libc/include",
]
configs = [ "lib/libc:limits-stub" ]
}
config("warnings") {
cflags = [
"-Wformat=2",
"-Wmissing-declarations",
"-Wvla",
]
# GCC supports `-Wformat-signedness` but Clang currently does not.
if (is_gcc) {
cflags += [ "-Wformat-signedness" ]
}
# TODO(fxbug.dev/78934): Eventually enable -Wshadow for GCC. It's currently
# disabled because GCC is more aggressive than Clang.
if (!is_gcc) {
cflags += [ "-Wshadow" ]
}
cflags_c = [ "-Wmissing-prototypes" ]
}
config("lock_dep") {
visibility = [ ":*" ]
defines = []
if (enable_lock_dep) {
defines += [
"WITH_LOCK_DEP=1",
"LOCK_DEP_ENABLE_VALIDATION=1",
]
configs =
[ "//build/config/zircon/instrumentation:instrumented-stack-size" ]
}
if (enable_lock_dep_tests) {
defines += [ "WITH_LOCK_DEP_TESTS=1" ]
}
}
config("scheduler") {
visibility = [ ":*" ]
defines = [
"SCHEDULER_TRACING_LEVEL=$scheduler_tracing_level",
"SCHEDULER_QUEUE_TRACING_ENABLED=$scheduler_queue_tracing_enabled",
"WAIT_QUEUE_DEPTH_TRACING_ENABLED=$wait_queue_depth_tracing_enabled",
]
}
config("virtual_heap") {
visibility = [ ":*" ]
defines = []
if (enable_virtual_heap) {
defines += [ "VIRTUAL_HEAP=1" ]
} else {
defines += [ "VIRTUAL_HEAP=0" ]
}
}
config("persistent_ram_config") {
visibility = [
":*",
"//zircon/kernel/lib/crashlog/*",
"//zircon/kernel/lib/jtrace/*",
"//zircon/kernel/lib/persistent-debuglog/*",
]
defines = [ "PERSISTENT_RAM_ALLOCATION_GRANULARITY=$persistent_ram_allocation_granularity" ]
}
config("jtrace_config") {
visibility = [
":*",
"//zircon/kernel/lib/jtrace/*",
]
if (jtrace_enabled == false) {
defines = [ "JTRACE_TARGET_BUFFER_SIZE=0" ]
} else {
if (jtrace_target_buffer_size == "auto") {
if (jtrace_enabled == "persistent") {
jtrace_target_buffer_size = 4096
} else {
jtrace_target_buffer_size = 32768
}
}
if (jtrace_use_large_entries == "auto") {
if (jtrace_enabled == "persistent") {
jtrace_use_large_entries = false
} else {
jtrace_use_large_entries = true
}
}
defines = [
"JTRACE_TARGET_BUFFER_SIZE=$jtrace_target_buffer_size",
"JTRACE_LAST_ENTRY_STORAGE=$jtrace_last_entry_storage",
"JTRACE_USE_LARGE_ENTRIES=$jtrace_use_large_entries",
]
if (jtrace_enabled == "persistent") {
defines += [ "JTRACE_IS_PERSISTENT=true" ]
} else {
defines += [ "JTRACE_IS_PERSISTENT=false" ]
}
}
}
# This is the kernel proper, an ELF executable with full symbols.
# It's the file to use with a debugger, for example.
executable("zircon.elf") {
visibility = [ ":*" ]
# //zircon/scripts/zircon.elf-gdb.py expects kernel symbols in "zircon.elf".
output_name = "zircon"
output_extension = "elf"
ldflags = [
"-Wl,-T," + rebase_path("kernel.ld", root_build_dir),
"-Wl,--emit-relocs",
]
if (toolchain_variant.tags + [ "lto" ] - [ "lto" ] !=
toolchain_variant.tags) {
# (Thin)LTO linker driver overrides the PIC/PIE metadata embedded in the
# IR and will choose the relocation model based on the output which for
# the kernel would be static rather than PIE. We need to explicitly
# override the relocation via linker flag.
ldflags += [ "-Wl,-mllvm,-relocation-model=pic" ]
}
inputs = [ "kernel.ld" ]
configs += [ ":kernel_defsym" ]
deps = [
":test", # TODO: make optional, add testonly taint
"top",
# Ensures that //docs/gen/boot-options.md is never stale.
"lib/boot-options:check-markdown($default_toolchain)",
]
if (current_cpu == "arm64") {
deps += [ "platform/generic-arm" ]
} else if (current_cpu == "x64") {
deps += [ "target/pc" ]
}
}
zircon_elf_rspfile = "$target_gen_dir/zircon.elf.rsp"
link_output_rspfile("zircon.elf.rsp") {
visibility = [ ":*" ]
deps = [ ":zircon.elf" ]
outputs = [ zircon_elf_rspfile ]
}
# These are needed only in image.S and in the linker scripts.
image_defines = [ "BOOT_HEADER_SIZE=0x50" ]
# This supplies those variables for use in linker scripts.
config("kernel_defsym") {
visibility = [ ":*" ]
ldflags = []
foreach(assignment, kernel_defines + image_defines) {
ldflags += [ "-Wl,-defsym,$assignment" ]
}
}
group("test") {
#TODO: testonly = true
visibility = [ ":*" ]
deps = [
"debugcommands",
"tests",
]
}
# Extract the raw binary image (no ELF headers) of the kernel proper.
image_binary("raw") {
visibility = [ ":*" ]
deps = [ ":zircon.elf" ]
output_name = "zircon"
output_path =
rebase_path("$target_out_dir/$output_name.bin", root_build_dir)
metadata = {
# Picked up by link_output_rspfile(), below.
link_output_path = [ output_path ]
link_output_barrier = []
}
}
image_rspfile = "$target_gen_dir/image.rsp"
link_output_rspfile("image.rsp") {
visibility = [ ":*" ]
outputs = [ image_rspfile ]
deps = [ ":raw" ]
}
toolchain_utils_action("kernel-image.h") {
visibility = [ ":*" ]
outputs = [ "$target_gen_dir/$target_name" ]
script = "gen-kernel-image-header.sh"
utils = [ "nm" ]
deps = [
":image.rsp",
":zircon.elf.rsp",
]
sources = [
image_rspfile,
zircon_elf_rspfile,
]
depfile = "$target_gen_dir/$target_name.d"
args = rebase_path(sources + outputs + [ depfile ], root_build_dir)
}
# Use the --emit-relocs records to extract the fixups needed to relocate
# the kernel at boot. This generates the "kernel-fixups.inc" file that's
# #include'd by "arch/$zircon_cpu/image.S".
toolchain_utils_action("fixups") {
visibility = [ ":*" ]
deps = [
":zircon.elf",
":zircon.elf.rsp",
]
sources = [ zircon_elf_rspfile ]
outputs = [ "$target_gen_dir/kernel-fixups.inc" ]
depfile = "${outputs[0]}.d"
script = "gen-kaslr-fixups.sh"
utils = [
"readelf",
"objdump",
]
args = [
"@" + rebase_path(zircon_elf_rspfile, root_build_dir),
rebase_path(outputs[0], root_build_dir),
rebase_path(depfile, root_build_dir),
]
}
toolchain_utils_action("validate-kernel-symbols") {
visibility = [ ":*" ]
inputs = [ "$root_out_dir/zircon.elf" ]
outputs = [ "$target_gen_dir/$target_name.stamp" ]
script = "//zircon/kernel/scripts/validate-kernel-symbols.py"
utils = [ "nm" ]
deps = [
":zircon.elf",
":zircon.elf.rsp",
]
sources = [ zircon_elf_rspfile ]
depfile = "$target_gen_dir/$target_name.d"
args = rebase_path(sources + outputs + [ depfile ], root_build_dir)
}
# Link the final kernel image layout including the extracted raw binary
# and the generated fixups.
executable("image") {
visibility = [ ":*" ]
configs += [
":image_config",
":kernel_defsym",
]
deps = [
":fixups",
":gdb_extension",
":kernel-image.h",
":raw",
":zircon.elf",
":zircon.elf.rsp",
"//zircon/kernel/lib/version:headers",
]
# TODO(fxbug.dev/27083): This dependency is conditional because when built
# with GCC BootCpuidIo uses a function scoped static requiring dynamic
# initialization, which won't pass symbol validation. Once fxbug.dev/27083
# is fixed, the dynamic initialization can be removed and this dependency
# can be made unconditional.
if (!is_gcc) {
deps += [ ":validate-kernel-symbols" ]
}
sources = [ "arch/$zircon_cpu/image.S" ]
# kernel-image.inc includes this via .incbin, which doesn't get automatic
# dependency tracking.
inputs = [ kernel_version_string_file ]
deps += kernel_version_string_deps
include_dirs = [ "." ] # For kernel-image.inc.
# These need to be here rather than in the config() below because they
# refer directly to inputs related to deps.
ldflags = [
"-Wl,-T," + rebase_path("image.ld", root_build_dir),
"-Wl,--just-symbols,@" + rebase_path(zircon_elf_rspfile, root_build_dir),
]
inputs += [
"image.ld",
zircon_elf_rspfile,
]
# If the span of where fixups might be is short enough, the fixup code
# itself can be shorter. Assume the kernel will fit under the
# threshold (about 1MB) in well-optimized builds.
if (optimize != "none" && optimize != "debug" &&
toolchain_variant.tags + [ "instrumented" ] - [ "instrumented" ] ==
toolchain_variant.tags) {
defines = [ "TINY" ]
}
}
# Copy gdb extension script file needed for kernel debugging.
copy("gdb_extension") {
visibility = [ ":*" ]
sources = [ "scripts/zircon.elf-gdb.py" ]
outputs = [ "$root_out_dir/zircon.elf-gdb.py" ]
}
# This needs to be in a config() rather than directly in the executable()
# so that these switches come after the ones provided by configs. In
# some cases, these switches may be overriding settings done by another
# config(), e.g. --build-id in the GCC build.
config("image_config") {
visibility = [ ":*" ]
include_dirs = [ target_gen_dir ]
defines = image_defines
ldflags = [ "-Wl,--build-id=none" ]
}
# Next, extract the raw image (no ELF headers), which includes its own
# embedded headers to make it a ZBI.
image_binary("zbizircon") {
visibility = [ ":*" ]
deps = [ ":image" ]
output_extension = "zbi"
}
# Then that gets compressed to become part of the ZBI_TYPE_KERNEL_STORAGE
# payload.
resource("zbizircon.input") {
sources = get_target_outputs(":zbizircon")
outputs = [ "kernel.zbi" ]
deps = [ ":zbizircon" ]
}
code_patches("code-patches") {
deps = [ ":zircon.elf" ]
}
kernel_package("zircon") {
deps = [
":code-patches",
":zbizircon.input",
"top",
]
}
# The final kernel ZBI combines physboot with that kernel storage payload.
zbi("kernel") {
deps = []
# **TODO(fxbug.dev/32255): Remove the build argument when nobody needs it.
if (current_cpu == "x64" && deprecated_x86_legacy_boot_protocol) {
# Inject the compatibility boot shim as the first kernel. Note the order
# matters here! The zbi tool sorts kernel items first, but it's a stable
# sort so the shim kernel item will stay before the physboot kernel item.
deps +=
[ "//zircon/kernel/arch/x86/phys/boot-shim:x86-legacy-zbi-boot-shim" ]
}
deps += [
":zircon",
"phys:physboot",
]
output_dir = root_out_dir
output_extension = "zbi"
output_path = rebase_path("$output_dir/$target_name.$output_extension",
root_build_dir)
metadata = {
# For the //:images build_api_module().
images = [
{
label = get_label_info(":$target_name", "label_with_toolchain")
name = "kernel"
tags = [ "incomplete" ]
type = "zbi"
path = output_path
cpu = current_cpu
},
]
}
}
} else {
group("kernel") {
public_deps = [ ":kernel(//zircon/kernel:kernel_$target_cpu)" ]
}
group("tests") {
testonly = true
deps = [
"arch/$zircon_cpu/user-copy:tests",
"dev/coresight/tests($host_toolchain)",
"lib/acpi_lite:tests",
"lib/arch/test:tests",
"lib/boot-options/tests",
"lib/devicetree/tests",
"lib/efi:tests",
"lib/heap/cmpctmalloc/tests($host_toolchain)",
"phys:tests",
"phys/lib:tests",
"target:tests",
]
if (current_cpu == "x64") {
deps += [ "lib/libc/string/arch/x86:tests" ]
}
# TODO(fxbug.dev/27083): This dependency is conditional because when built
# with GCC the kernel uses function scoped statics requiring dynamic
# initialization. Once fxbug.dev/27083 is fixed, this dependency can be made
# unconditional.
if (!is_gcc) {
deps += [ "lib/cxxabi-dynamic-init/tests" ]
}
# Don't let any test ZBIs roll up into the overall product image.
metadata = {
zbi_input_barrier = []
}
}
}