diff --git a/.github/actions/toolchain/action.yml b/.github/actions/toolchain/action.yml new file mode 100644 index 00000000..9787c5ac --- /dev/null +++ b/.github/actions/toolchain/action.yml @@ -0,0 +1,46 @@ +name: Platform toolchain +description: Build the platform toolchain, with GCC cached between runs + +inputs: + arch: + description: The architecture to build for + required: true + toolchain: + description: gcc or clang + required: true + lookup-only: + description: Don't restore a cached toolchain, only build a missing one + default: "false" + +runs: + using: composite + steps: + - if: ${{ inputs.toolchain == 'gcc' }} + id: version + name: Get the toolchain version + shell: bash + run: | + python3 -c 'import scripts.build_utils.toolchain_builder as tb + print(f"key=gcc-{tb.GCC_VERSION}-binutils-{tb.BINUTILS_VERSION}")' \ + >> "$GITHUB_OUTPUT" + + - if: ${{ inputs.toolchain == 'gcc' }} + id: cache + name: Restore the toolchain cache + uses: actions/cache/restore@v4.2.2 + with: + path: "${{ github.workspace }}/toolchain/tools-${{ inputs.toolchain }}-${{ inputs.arch }}" + key: "${{ runner.os }}-${{ runner.arch }}-${{ inputs.arch }}-${{ steps.version.outputs.key }}" + lookup-only: ${{ inputs.lookup-only }} + + - if: ${{ inputs.lookup-only != 'true' || steps.cache.outputs.cache-hit != 'true' }} + name: Build the platform toolchain + shell: bash + run: "${{ github.workspace }}/build.py --arch ${{ inputs.arch }} --toolchain ${{ inputs.toolchain }} --no-tune-native --toolchain-only" + + - if: ${{ inputs.toolchain == 'gcc' && steps.cache.outputs.cache-hit != 'true' }} + name: Save the toolchain cache + uses: actions/cache/save@v4.2.2 + with: + path: "${{ github.workspace }}/toolchain/tools-${{ inputs.toolchain }}-${{ inputs.arch }}" + key: ${{ steps.cache.outputs.cache-primary-key }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3418745c..0877c6cd 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -42,10 +42,30 @@ jobs: - name: Build the platform toolchain run: "${{ github.workspace }}/build.py --toolchain clang --toolchain-only" - - name: Check the coding conventions - run: "${{ github.workspace }}/build.py --toolchain clang --lint" + - name: Check the coding conventions of every preset + run: | + for preset in configs/*.config; do + "${{ github.workspace }}/build.py" --toolchain clang \ + --config "$(basename "$preset" .config)" --lint + done + + toolchain: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + with: + submodules: true - build: + - name: Build the GCC toolchain unless it is cached + uses: ./.github/actions/toolchain + with: + arch: x86_64 + toolchain: gcc + lookup-only: true + + build-and-boot: + needs: toolchain runs-on: ${{ matrix.os }} strategy: @@ -66,42 +86,61 @@ jobs: with: submodules: true - - if: ${{ matrix.toolchain == 'gcc' }} - id: toolchain-version - name: Get the toolchain version - run: | - python3 -c 'import scripts.build_utils.toolchain_builder as tb - print(f"key=gcc-{tb.GCC_VERSION}-binutils-{tb.BINUTILS_VERSION}")' \ - >> "$GITHUB_OUTPUT" - - - if: ${{ matrix.toolchain == 'gcc' }} - id: toolchain-cache - name: Restore the toolchain cache - uses: actions/cache/restore@v4.2.2 - with: - path: "${{ github.workspace }}/toolchain/tools-${{ matrix.toolchain }}-${{ matrix.arch }}" - key: "${{ runner.os }}-${{ runner.arch }}-${{ matrix.arch }}-${{ steps.toolchain-version.outputs.key }}" - - name: Build the platform toolchain - run: "${{ github.workspace }}/build.py --arch ${{ matrix.arch }} --toolchain ${{ matrix.toolchain }} --no-tune-native --toolchain-only" - - - if: ${{ matrix.toolchain == 'gcc' && steps.toolchain-cache.outputs.cache-hit != 'true' }} - name: Save the toolchain cache - uses: actions/cache/save@v4.2.2 + uses: ./.github/actions/toolchain with: - path: "${{ github.workspace }}/toolchain/tools-${{ matrix.toolchain }}-${{ matrix.arch }}" - key: ${{ steps.toolchain-cache.outputs.cache-primary-key }} + arch: ${{ matrix.arch }} + toolchain: ${{ matrix.toolchain }} - - name: Build the kernel - run: "${{ github.workspace }}/build.py --arch ${{ matrix.arch }} --config ${{ matrix.preset }} --toolchain ${{ matrix.toolchain }} --no-tune-native" + - name: Build and boot the kernel + run: "${{ github.workspace }}/build.py --arch ${{ matrix.arch }} --config ${{ matrix.preset }} --toolchain ${{ matrix.toolchain }} --no-tune-native --boot-test" - unit-tests: + ktest: + needs: toolchain runs-on: ${{ matrix.os }} strategy: fail-fast: true matrix: - os: [ubuntu-latest, macos-latest] + include: + - os: ubuntu-latest + arch: x86_64 + toolchain: clang + accel: kvm + overrides: --optimization-level 3 --lto + - os: ubuntu-latest + arch: x86_64 + toolchain: gcc + accel: tcg + overrides: --optimization-level 2 + - os: macos-latest + arch: x86_64 + toolchain: clang + accel: tcg + + steps: + - uses: actions/checkout@v3 + with: + submodules: true + + - name: Build the platform toolchain + uses: ./.github/actions/toolchain + with: + arch: ${{ matrix.arch }} + toolchain: ${{ matrix.toolchain }} + + - if: ${{ matrix.accel == 'kvm' }} + name: Give the runner access to KVM + run: | + echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules + sudo udevadm control --reload-rules + sudo udevadm trigger --name-match=kvm + + - name: Run the in-kernel tests + run: "${{ github.workspace }}/build.py --arch ${{ matrix.arch }} --toolchain ${{ matrix.toolchain }} --no-tune-native --ktest ${{ matrix.accel == 'kvm' && '--kvm' || '' }} ${{ matrix.overrides }}" + + unit-tests: + runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 diff --git a/build.py b/build.py index 1028a309..c26da1eb 100755 --- a/build.py +++ b/build.py @@ -7,11 +7,10 @@ import selectors import shutil import time -import urllib.request import signal import sys from contextlib import contextmanager -from typing import Optional, List, Callable, Iterator, Tuple +from typing import Optional, Dict, List, Callable, Iterator, Tuple try: import scripts.kconfiglib.kconfiglib as kc @@ -24,6 +23,7 @@ import scripts.build_utils.package_manager as pm import scripts.build_utils.toolchain_builder as tb import scripts.build_utils.toolchain_args as ta + import scripts.build_utils.download as dl import scripts.image_utils.ultra as ultr import scripts.image_utils.uefi as uefi import scripts.image_utils.path_guesser as pg @@ -235,7 +235,7 @@ def build_ultra( QEMU_HYPER_OPTIONS = """ -cmdline = "log-level=debug earlycon=e9,colored" +cmdline = "log-level=debug earlycon=e9,colored{cmdline_extra}" # Hardcode a resolution that's good enough for local testing, # we don't need a huge QEMU window. @@ -250,7 +250,15 @@ def build_ultra( } -def make_hyper_config(arch: str, target: str) -> str: +# The QEMU image gets a fixed command line, extra arguments are appended +def image_target_options(target: str, extra_cmdline: str) -> str: + if extra_cmdline: + extra_cmdline = " " + extra_cmdline + + return IMAGE_TARGETS[target].replace("{cmdline_extra}", extra_cmdline) + + +def make_hyper_config(arch: str, target: str, extra_cmdline: str) -> str: return \ f""" default-entry = ultra-{arch} @@ -266,7 +274,7 @@ def make_hyper_config(arch: str, target: str) -> str: page-table: levels = 5 constraint = maximum -{IMAGE_TARGETS[target]} +{image_target_options(target, extra_cmdline)} [ultra-aarch64] protocol = ultra higher-half-exclusive = true @@ -290,7 +298,8 @@ def get_kernel_path(execution_node: str, build_dir: str) -> str: def make_hyper_image( br_type: str, fs_type: str, arch: str, target: str, build_dir: str, hyper_installer: Optional[str], hyper_iso_br: Optional[str], - hyper_uefi_binaries: List[str], image_path: str, force_regenerate: bool + hyper_uefi_binaries: List[str], image_path: str, force_regenerate: bool, + extra_cmdline: str ) -> Optional[ultr.DiskImage]: kernel_path = get_kernel_path(arch, build_dir) image_root_path = os.path.join(build_dir, "image-root") @@ -309,7 +318,7 @@ def make_hyper_image( return ultr.DiskImage( image_root_path, br_type, fs_type, - hyper_config=make_hyper_config(arch, target), + hyper_config=make_hyper_config(arch, target, extra_cmdline), hyper_uefi_binary_paths=hyper_uefi_binaries, hyper_iso_br_path=hyper_iso_br, hyper_installer_path=hyper_installer, @@ -330,7 +339,7 @@ def hyper_get_binary(name: str, optional: bool = False) -> Optional[str]: base_url += hyper_version file_url = f"{base_url}/{name}" try: - urllib.request.urlretrieve(file_url, binary_path) + dl.download([file_url], binary_path) except Exception as ex: print(f"Failed to retrieve {name}:", ex) return None @@ -351,12 +360,28 @@ def hyper_get_iso_br() -> str: return ret -def qemu_watch_run_for(qp: subprocess.Popen, run_for: float) -> None: +QEMU_VERDICT_TIMEOUT_SECONDS = 120 +QEMU_DRAIN_SECONDS = 1 + +# The colored console wraps parts of a line in escape sequences +ANSI_ESCAPE = re.compile(rb"\x1b\[[0-9;]*m") + + +# Streams the kernel output, returns the first match of 'until' or None +# when QEMU exited or the wait ran out. With 'until' the run is bounded by +# 'run_for' seconds of total output, otherwise by 'run_for' seconds since +# the first output byte. +def qemu_watch( + qp: subprocess.Popen, run_for: float, + until: Optional[re.Pattern] = None +) -> Optional[re.Match]: assert qp.stdout is not None sel = selectors.DefaultSelector() sel.register(qp.stdout, selectors.EVENT_READ) deadline = None silent_deadline = time.monotonic() + run_for + QEMU_SILENT_BOOT_SECONDS + tail = b"" + match = None while qp.poll() is None: if deadline is not None: @@ -379,13 +404,31 @@ def qemu_watch_run_for(qp: subprocess.Popen, run_for: float) -> None: sys.stdout.buffer.flush() if deadline is None: deadline = time.monotonic() + run_for + if until is not None and match is None: + tail = (tail + data)[-4096:] + match = until.search(ANSI_ESCAPE.sub(b"", tail)) + if match is not None: + deadline = time.monotonic() + QEMU_DRAIN_SECONDS + if match is not None: + sys.stdout.buffer.write(b"\n") + sys.stdout.buffer.flush() qp.wait() + return match + + +# A QEMU run is judged by the same criteria as a baremetal run +def verdict_pattern(criteria_name: Optional[str]) -> re.Pattern: + criteria = bmc.load_criteria(criteria_name) + failures = "|".join(criteria["failures"]) + return re.compile( + f"(?P{criteria['success']})|(?P{failures})".encode() + ) def run_qemu( arch: str, image_path: str, image_type: str, debug: bool, uefi_boot: bool, uefi_firmware: str, kvm: bool, la57: bool, headless: bool, dry: bool, - run_for: Optional[float] = None + run_for: Optional[float] = None, verdict: Optional[re.Pattern] = None ) -> Optional[subprocess.Popen]: extra_args = [] force_uefi = False @@ -440,10 +483,31 @@ def run_qemu( print(" ".join(args)) return None + if verdict is not None: + qp = subprocess.Popen(args, stdout=subprocess.PIPE) + try: + match = qemu_watch(qp, QEMU_VERDICT_TIMEOUT_SECONDS, verdict) + except KeyboardInterrupt: + qp.terminate() + qp.wait() + sys.exit(130) + + if match is None: + print("Run did not finish", file=sys.stderr) + sys.exit(2) + + failure = match.group("failure") + if failure is not None: + print(f"Run failed: {failure.decode(errors='replace')}", + file=sys.stderr) + sys.exit(1) + + sys.exit(0) + if run_for is not None and not debug: qp = subprocess.Popen(args, stdout=subprocess.PIPE) try: - qemu_watch_run_for(qp, run_for) + qemu_watch(qp, run_for) except KeyboardInterrupt: qp.terminate() qp.wait() @@ -503,6 +567,11 @@ def main() -> None: build.add_argument("--reset-config", action="store_true", help="Recreate the build directory's config from " "--config, discarding the changes made to it") + build.add_argument("--optimization-level", type=int, choices=range(4), + help="Override the optimization level of the config") + build.add_argument("--lto", action=argparse.BooleanOptionalAction, + help="Override whether the config uses link-time " + "optimization") build.add_argument("--menuconfig", action="store_true", help="Run menuconfig to edit the current config file") build.add_argument("--guiconfig", action="store_true", @@ -525,6 +594,9 @@ def main() -> None: help="Produce a bootable image after building, " "for QEMU or for real hardware with " "'baremetal' (default: qemu)") + image.add_argument("--cmdline", type=str, default="", + help="Extra kernel command line arguments (implies " + "--make-image unless this is a baremetal run)") image.add_argument("--image-type", choices=["iso", "raw"], default="iso", help="Image type to produce (with --make-image)") image.add_argument("--hyper-installer", type=str, @@ -536,6 +608,22 @@ def main() -> None: help="Paths to the hyper UEFI binaries " "(BOOT{X64,AA64}.EFI)") + boot_tests = parser.add_argument_group( + "Boot tests", + "Boot the kernel headless and exit with the verdict, in QEMU unless " + "--baremetal is given" + ) + boot_tests.add_argument("--boot-test", action="store_true", + help="Check that the kernel boots until it idles " + "(the default for --baremetal)") + boot_tests.add_argument("--ktest", nargs="?", const="all", + metavar="TESTS", + help="Run the in-kernel tests selected by TESTS " + "(default: all)") + boot_tests.add_argument("--criteria", metavar="NAME", + help="Judge the run by criteria-NAME.json " + "instead of criteria.json") + qemu = parser.add_argument_group("QEMU") qemu.add_argument("--run", action="store_true", help="Run in QEMU after building") @@ -584,12 +672,37 @@ def main() -> None: sys.exit(1) if args.unit_tests: + if this_os != "Linux": + print("Unit tests only support Linux hosts") + sys.exit(1) + sys.exit(run_unit_tests(args, this_os.lower())) rc = bmc.early_action(args, hyper_pxe_loaders) if rc is not None: sys.exit(rc) + if args.ktest is not None: + if args.config is not None: + sys.exit("--config cannot be combined with --ktest") + if args.criteria is not None: + sys.exit("--criteria cannot be combined with --ktest") + args.config = "ktest" + args.criteria = "ktest" + + if args.cmdline: + args.cmdline += " " + args.cmdline += f"ktest.run={args.ktest}" + + if args.criteria is not None: + args.boot_test = True + if args.boot_test: + args.headless = True + + # A changed command line only reaches QEMU through a new image + if args.cmdline and args.machines is None and args.make_image is None: + args.make_image = "qemu" + config_layers = cl.build_layers(args.config, args.arch) config_file = config_layers[-1] default_preset = cl.preset_path(cl.DEFAULT_PRESET) @@ -605,8 +718,14 @@ def main() -> None: build_dir = pg.project_root_relative(build_dir_name) os.makedirs(build_dir, exist_ok=True) + overrides: Dict[str, str] = {} + if args.optimization_level is not None: + overrides["OPTIMIZATION_LEVEL"] = str(args.optimization_level) + if args.lto is not None: + overrides["LTO"] = "y" if args.lto else "n" + args.config = os.path.join(build_dir, ".config") - cl.prepare_config(args.config, config_layers, args.toolchain, + cl.prepare_config(args.config, config_layers, overrides, args.toolchain, args.arch, args.reset_config) if args.menuconfig or args.guiconfig: @@ -684,7 +803,7 @@ def main() -> None: make_hyper_image( "MBR", fs_type, args.arch, image_target, build_dir, hyper_installer, hyper_iso_br, hyper_uefi_binary_paths, - image_path, args.make_image is not None + image_path, args.make_image is not None, args.cmdline ) if should_run: @@ -695,9 +814,13 @@ def main() -> None: if args.la57 and args.arch != "x86_64": raise RuntimeError("--la57 is only supported on x86_64") + verdict: Optional[re.Pattern] = None + if args.boot_test: + verdict = verdict_pattern(args.criteria) + qp = run_qemu(args.arch, image_path, args.image_type, is_debug, uefi_boot, args.uefi_firmware_path, args.kvm, args.la57, - args.headless, args.dry, args.run_for) + args.headless, args.dry, args.run_for, verdict) if args.debug: assert qp diff --git a/configs/fireworks.config b/configs/fireworks.config new file mode 100644 index 00000000..1135cf34 --- /dev/null +++ b/configs/fireworks.config @@ -0,0 +1,4 @@ +CONFIG_DEBUG_MM=y +CONFIG_UBSAN=y +# CONFIG_FLANTERM_CONSOLE is not set +CONFIG_FIREWORKS_DEMO=y diff --git a/configs/ktest.config b/configs/ktest.config new file mode 100644 index 00000000..0e0cffc6 --- /dev/null +++ b/configs/ktest.config @@ -0,0 +1,3 @@ +CONFIG_DEBUG_MM=y +CONFIG_UBSAN=y +CONFIG_KTEST=y diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 4e57b995..2026d96c 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -6,6 +6,7 @@ include(target_helpers.cmake) include(module.cmake) include(ld_template.cmake) include(ubsan.cmake) +include(asm_offsets.cmake) # The base kernel executable that contains an empty stub symbol table set( @@ -83,9 +84,9 @@ ultra_compile_definitions( set_property(GLOBAL PROPERTY ULTRA_OBJECT_TARGETS "${ULTRA_KERNEL_OBJECTS}") # Number of times to re-link the kernel to stabilize symbols -set(ULTRA_NUM_RELINKS 1) +set(ULTRA_NUM_RELINKS 2) if (CONFIG_LTO) - set(ULTRA_NUM_RELINKS 2) + set(ULTRA_NUM_RELINKS 3) endif () setup_symbol_table_link_steps(${ULTRA_NUM_RELINKS}) @@ -100,11 +101,10 @@ ultra_sources( entry.c panic.c bug.c + refcount.c console.c log.c symbols.c - spinlock.c - mutex.c unwind.c param.c log_ring.c @@ -131,6 +131,7 @@ ultra_compile_options( -ggdb3 -Wall -Wextra + -Wshadow -Werror -Wstrict-prototypes -Wmissing-prototypes @@ -167,10 +168,14 @@ ultra_link_options( add_subdirectory(arch/${ULTRA_ARCH}) add_subdirectory(common) +add_subdirectory(locking) add_subdirectory(irq) add_subdirectory(memory) +add_subdirectory(sched) add_subdirectory(time) add_subdirectory(drivers) +add_subdirectory(ktest) +add_subdirectory(demo) set(ULTRA_ALL_OBJECTS "") get_property(ULTRA_OBJECT_TARGETS_FINAL GLOBAL PROPERTY ULTRA_OBJECT_TARGETS) diff --git a/kernel/Kconfig b/kernel/Kconfig index 104f64f3..93e594ec 100644 --- a/kernel/Kconfig +++ b/kernel/Kconfig @@ -96,3 +96,5 @@ endmenu rsource "arch/Kconfig" rsource "irq/Kconfig" rsource "drivers/Kconfig" +rsource "ktest/Kconfig" +rsource "demo/Kconfig" diff --git a/kernel/abortable_instructions.c b/kernel/abortable_instructions.c index 5d0207ba..db17f1a2 100644 --- a/kernel/abortable_instructions.c +++ b/kernel/abortable_instructions.c @@ -1,20 +1,18 @@ #include +#include #include #include -extern const struct abortable_instruction -SECTION_ARRAY_BEGIN(ABORTABLE_INSTRUCTIONS_SECTION)[], -SECTION_ARRAY_END(ABORTABLE_INSTRUCTIONS_SECTION)[]; +DECLARE_TYPED_SECTION_MARKERS( + const struct abortable_instruction, ABORTABLE_INSTRUCTIONS_SECTION +); static const struct abortable_instruction *find_abortable_instruction(reg_t ip) { - ssize_t i; const struct abortable_instruction *ai; - for (i = 0; i < SECTION_ARRAY_SIZE(ABORTABLE_INSTRUCTIONS_SECTION); i++) { - ai = &SECTION_ARRAY_BEGIN(ABORTABLE_INSTRUCTIONS_SECTION)[i]; - + for_each_section_array_item(ai, ABORTABLE_INSTRUCTIONS_SECTION) { if (SELF_RELATIVE_TARGET(ai->try_pc_disp) == ip) return ai; } diff --git a/kernel/arch/aarch64/include/arch/private/unwind.h b/kernel/arch/aarch64/include/arch/detail/unwind.h similarity index 100% rename from kernel/arch/aarch64/include/arch/private/unwind.h rename to kernel/arch/aarch64/include/arch/detail/unwind.h diff --git a/kernel/arch/aarch64/unwind.S b/kernel/arch/aarch64/unwind.S index 8c4cb572..3c219ea2 100644 --- a/kernel/arch/aarch64/unwind.S +++ b/kernel/arch/aarch64/unwind.S @@ -1,4 +1,4 @@ -#include +#include .section ".text", "ax", @progbits diff --git a/kernel/arch/aarch64/unwind.c b/kernel/arch/aarch64/unwind.c index 8e726051..ad823171 100644 --- a/kernel/arch/aarch64/unwind.c +++ b/kernel/arch/aarch64/unwind.c @@ -1,5 +1,5 @@ #include -#include +#include void arch_registers_to_dwarf_registers( struct registers *regs, ptr_t *dwarf_regs diff --git a/kernel/arch/x86_64/CMakeLists.txt b/kernel/arch/x86_64/CMakeLists.txt index 88060f93..553010ec 100644 --- a/kernel/arch/x86_64/CMakeLists.txt +++ b/kernel/arch/x86_64/CMakeLists.txt @@ -2,6 +2,7 @@ ultra_sources_no_lto(string.c) ultra_sources( apic.c + apic_timer.c vector_alloc.c lapic_domain.c xapic.c @@ -11,6 +12,7 @@ ultra_sources( entry.c entry.S cpu.c + cpu_match.c smp.c memory.c unwind.c @@ -24,7 +26,11 @@ ultra_sources( earlycon.c pci_type1.c tsc.c + task.c + task.S ) +setup_asm_offsets(asm_offsets.c) + ultra_include_directories(include) check_c_compiler_flag( diff --git a/kernel/arch/x86_64/apic.c b/kernel/arch/x86_64/apic.c index cc508a6d..b24bf6da 100644 --- a/kernel/arch/x86_64/apic.c +++ b/kernel/arch/x86_64/apic.c @@ -14,14 +14,12 @@ #include #include -static UNUSED_DECL u64 s_freq_hz; - struct apic *g_apic = nullptr; enum apic_mode g_apic_mode = APIC_MODE_NONE; -u32 g_bsp_apic_id = APIC_ID_NONE; -u32 g_boot_cpu_apic_id = APIC_ID_NONE; -u32 g_max_apic_id = 0xFF - 1; +apic_id_t g_bsp_apic_id = APIC_ID_NONE; +apic_id_t g_boot_cpu_apic_id = APIC_ID_NONE; +apic_id_t g_max_apic_id = 0xFF - 1; extern struct apic g_xapic; extern struct apic g_x2apic; @@ -59,7 +57,7 @@ void INIT_CODE apic_detect(void) pr_info("using %s mode\n", s_apic_mode_strings[g_apic_mode]); g_apic->setup(); - g_boot_cpu_apic_id = g_apic->read(APIC_REG_ID); + g_boot_cpu_apic_id = apic_read_id(); if (unlikely(!(apic_base_msr & IA32_APIC_BASE_IS_BSP))) { pr_warn("boot CPU (0x%08X) is not the BSP!\n", g_boot_cpu_apic_id); return; @@ -68,22 +66,22 @@ void INIT_CODE apic_detect(void) g_bsp_apic_id = g_boot_cpu_apic_id; } -void apic_set_known_frequency(u64 hz) +void apic_eoi(void) { - s_freq_hz = hz; + g_apic->write(APIC_REG_EOI, 0); } -void apic_eoi(void) +apic_id_t apic_read_id(void) { - g_apic->write(APIC_REG_EOI, 0); + return g_apic->read_id(); } -void apic_icr_write(u32 value, u32 dest_apic_id) +void apic_icr_write(u32 value, apic_id_t dest_apic_id) { g_apic->icr_write(value, dest_apic_id); } -void apic_send_fixed_ipi(u32 dest_apic_id, u8 vector) +void apic_send_fixed_ipi(apic_id_t dest_apic_id, u8 vector) { apic_icr_write(APIC_ICR_DELIVERY_FIXED | vector, dest_apic_id); } diff --git a/kernel/arch/x86_64/apic_timer.c b/kernel/arch/x86_64/apic_timer.c new file mode 100644 index 00000000..b26bbc9f --- /dev/null +++ b/kernel/arch/x86_64/apic_timer.c @@ -0,0 +1,246 @@ +#define MSG_FMT(msg) "apic-timer: " msg + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include