| # 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/toolchain/zircon/zircon_toolchain_suite.gni") |
| import("//build/unification/global_variables.gni") |
| import("//build/zbi/zbi.gni") |
| import("//build/zbi/zbi_input.gni") |
| import("//build/zircon/c_utils.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("params.gni") |
| |
| declare_args() { |
| # **TODO(fxbug.dev/32414): This is a temporary switch that will be removed.** |
| # If true, use the new "physboot" booting path for the Zircon kernel. |
| use_physboot = 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", |
| "//zircon/kernel/lib/ktl:ktl.config", |
| "//sdk/lib/stdcompat:headers.config", |
| |
| # <stdio.h> has #include <lib/io.h>. |
| "//zircon/kernel/lib/io: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> |
| # <mexec.h> has #include <fbl/span.h> |
| "//zircon/kernel/lib/fbl:headers.config", |
| |
| # Everywhere has #include <zx/status.h> |
| "//zircon/system/ulib/zxc: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/zircon:default_frame_pointers" ] |
| configs += [ "//build/config/zircon: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. |
| exclude_variant_tags += [ "breaks-gc-sections" ] |
| remove_common_configs += [ "//build/config/zircon:default_linker_gc" ] |
| configs += [ "//build/config/zircon: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" ] |
| |
| # As a special case, never build the kernel with -O0, since it will |
| # crash immediately on boot. Use -Og instead, which allows debugging the |
| # kernel through QEMU. TODO(fxbug.dev/71321): Find a better way to deal with this. |
| if (optimize == "none") { |
| optimize_override = "debug" |
| } |
| } |
| } |
| } |
| |
| 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 = [ |
| ":lock_dep", |
| ":scheduler", |
| ":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", |
| |
| # Don't emit extra code making static initializers thread-safe (which in turn |
| # requires additional library support for locks the kernel doesn't provide). |
| "//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/zircon: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", |
| ] |
| |
| 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-dummy" ] |
| } |
| |
| # 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"), |
| ] |
| |
| 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_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. |
| if (!is_gcc) { |
| cflags += [ "-mgeneral-regs-only" ] |
| } else { |
| # TODO(mcgrathr): To work around a GCC bug we have to sneak |
| # -mgeneral-regs-only in through an arcane mechanism. See |
| # //zircon/kernel/lib/arch/arm64/include/lib/arch/intrin.h for details. |
| cflags += [ |
| "-include", |
| rebase_path("arch/arm64/general-regs-only.h", root_build_dir), |
| ] |
| } |
| } |
| |
| 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" ] |
| } |
| |
| if (!is_gcc) { |
| # Disable the implicit addition of toolchain-provided libraries to |
| # the link by the compiler driver. No toolchain-provided library is |
| # compatible with the kernel's internal ABI. |
| # |
| # TODO(fxbug.dev/27356): Clang doesn't have a single straightforward switch to |
| # disable all such libraries, though it certainly should. It |
| # provides separate switches to disable the profiling/coverage |
| # runtime and to disable all the flavors of runtime implied by |
| # -fsanitize=... switches (including any such defaults). It will |
| # still provide other incompatible libraries to the link, but they |
| # won't have any effect since they don't define any symbols the link |
| # needs. However, this is a fragile situation that could easily |
| # break. |
| ldflags += [ |
| "-noprofilelib", |
| "-fno-sanitize-link-runtime", |
| ] |
| } |
| |
| configs = [ "//build/config/zircon:no_exceptions" ] |
| } |
| |
| config("warnings") { |
| cflags = [ |
| "-Wformat=2", |
| "-Wvla", |
| ] |
| |
| # GCC supports `-Wformat-signedness` but Clang currently does not. |
| if (is_gcc) { |
| cflags += [ "-Wformat-signedness" ] |
| } |
| |
| cflags_c = [ "-Wmissing-prototypes" ] |
| } |
| |
| config("lock_dep") { |
| visibility = [ ":*" ] |
| defines = [] |
| if (enable_lock_dep) { |
| defines += [ |
| "WITH_LOCK_DEP=1", |
| "LOCK_DEP_ENABLE_VALIDATION=1", |
| "CUSTOM_DEFAULT_STACK_SIZE=16384", |
| ] |
| } |
| 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", |
| ] |
| } |
| |
| code_patching_hermetic_embedding("code-patches") { |
| deps = [ "top" ] |
| } |
| |
| # This is the kernel proper, an ELF executable with full symbols. |
| # It's the file to use with a debugger, for example. |
| executable("zircon") { |
| visibility = [ ":*" ] |
| |
| # //zircon/scripts/zircon.elf-gdb.py expects kernel symbols in "zircon.elf". |
| output_extension = "elf" |
| |
| ldflags = [ |
| "-Wl,-T," + rebase_path("kernel.ld", root_build_dir), |
| "-Wl,--emit-relocs", |
| ] |
| if (toolchain.tags + [ "lto" ] - [ "lto" ] != toolchain.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 = [ |
| ":code-patches", |
| ":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" ] |
| 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" ] |
| 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", |
| ":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), |
| ] |
| } |
| |
| # 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", |
| ":zircon.elf.rsp", |
| "//zircon/kernel/lib/version:headers", |
| ] |
| 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.tags + [ "instrumented" ] - [ "instrumented" ] == |
| toolchain.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" ] |
| } |
| |
| # Finally, extract the raw image (no ELF headers), which includes its own |
| # embedded headers to make it a ZBI. |
| image_binary("kernel") { |
| visibility = [ ":*" ] |
| |
| deps = [ ":image" ] |
| |
| output_dir = root_out_dir |
| output_extension = "zbi" |
| if (!use_physboot) { |
| 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 |
| }, |
| ] |
| |
| # Used by the zbi() template. This metadata makes the kernel act as a zbi_input() |
| # target so it can be a dependency of a zbi() target to get into the image. |
| zbi_input_barrier = [] |
| zbi_input_args = [ |
| "--type=container", |
| output_path, |
| ] |
| } |
| } |
| } |
| |
| zbi_input("vmzircon") { |
| type = "kernel" |
| sources = get_target_outputs(":kernel") |
| deps = [ ":kernel" ] |
| |
| # The kernel image is small enough that maximum compression is quick enough |
| # at build time, so always get the smallest size available. |
| args = [ "--compressed=max" ] |
| } |
| |
| zbi("physzircon") { |
| deps = [ |
| ":vmzircon", |
| "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") { |
| if (use_physboot) { |
| kernel = ":physzircon" |
| } else { |
| kernel = ":kernel" |
| } |
| public_deps = [ "$kernel(//zircon/kernel:kernel_$target_cpu)" ] |
| } |
| |
| group("tests") { |
| testonly = true |
| deps = [ |
| "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", |
| ] |
| |
| if (current_cpu == "x64") { |
| deps += [ |
| "arch/x86/user-copy:tests", |
| "lib/libc/string/arch/x86:tests", |
| ] |
| } |
| |
| # Don't let any test ZBIs roll up into the overall product image. |
| metadata = { |
| zbi_input_barrier = [] |
| } |
| } |
| } |