boot/efi: introduce arch_dtb

Change-Id: Iff9e4198aca706097889faf51e9559fe551126ad
Reviewed-on: https://review.haiku-os.org/c/haiku/+/4782
Tested-by: Commit checker robot <[email protected]>
Reviewed-by: Alex von Gluck IV <[email protected]>
This commit is contained in:
David Karoly
2022-01-05 16:22:35 +00:00
committed by Alex von Gluck IV
parent f4df72a061
commit 023a36024d
7 changed files with 259 additions and 151 deletions
@@ -0,0 +1,16 @@
/*
* Copyright 2021 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef KERNEL_BOOT_PLATFORM_EFI_ARCH_DTB_H
#define KERNEL_BOOT_PLATFORM_EFI_ARCH_DTB_H
#include <SupportDefs.h>
void arch_handle_fdt(const void* fdt, int node, uint32 addressCells, uint32 sizeCells);
void arch_dtb_set_kernel_args(void);
#endif /* KERNEL_BOOT_PLATFORM_EFI_ARCH_DTB_H */
@@ -2,6 +2,8 @@ SubDir HAIKU_TOP src system boot platform efi arch arm ;
SubDirHdrs $(HAIKU_TOP) src system boot platform efi ; SubDirHdrs $(HAIKU_TOP) src system boot platform efi ;
UseLibraryHeaders [ FDirName libfdt ] ;
UsePrivateHeaders [ FDirName kernel platform ] ; UsePrivateHeaders [ FDirName kernel platform ] ;
UsePrivateHeaders [ FDirName kernel boot platform efi ] ; UsePrivateHeaders [ FDirName kernel boot platform efi ] ;
@@ -19,6 +21,7 @@ for platform in [ MultiBootSubDirSetup efi ] {
crt0-efi-$(TARGET_ARCH).S crt0-efi-$(TARGET_ARCH).S
entry.S entry.S
relocation_func.cpp relocation_func.cpp
arch_dtb.cpp
arch_mmu.cpp arch_mmu.cpp
arch_smp.cpp arch_smp.cpp
arch_start.cpp arch_start.cpp
@@ -0,0 +1,95 @@
/*
* Copyright 2019-2021 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Alexander von Gluck IV <[email protected]>
*/
#include <arch_cpu_defs.h>
#include <arch_dtb.h>
#include <arch_smp.h>
#include <boot/platform.h>
#include <boot/stage2.h>
extern "C" {
#include <libfdt.h>
}
#include "dtb.h"
const struct supported_interrupt_controllers {
const char* dtb_compat;
const char* kind;
} kSupportedInterruptControllers[] = {
{ "arm,cortex-a9-gic", INTC_KIND_GICV1 },
{ "arm,cortex-a15-gic", INTC_KIND_GICV2 },
{ "ti,omap3-intc", INTC_KIND_OMAP3 },
{ "marvell,pxa-intc", INTC_KIND_PXA },
};
void
arch_handle_fdt(const void* fdt, int node, uint32 addressCells, uint32 sizeCells)
{
const char* deviceType = (const char*)fdt_getprop(fdt, node,
"device_type", NULL);
if (deviceType != NULL) {
if (strcmp(deviceType, "cpu") == 0) {
platform_cpu_info* info;
arch_smp_register_cpu(&info);
if (info == NULL)
return;
info->id = fdt32_to_cpu(*(uint32*)fdt_getprop(fdt, node,
"reg", NULL));
dprintf("cpu\n");
dprintf(" id: %" B_PRIu32 "\n", info->id);
}
}
int compatibleLen;
const char* compatible = (const char*)fdt_getprop(fdt, node,
"compatible", &compatibleLen);
if (compatible == NULL)
return;
intc_info &interrupt_controller = gKernelArgs.arch_args.interrupt_controller;
if (interrupt_controller.kind[0] == 0) {
for (uint32 i = 0; i < B_COUNT_OF(kSupportedInterruptControllers); i++) {
if (dtb_has_fdt_string(compatible, compatibleLen,
kSupportedInterruptControllers[i].dtb_compat)) {
memcpy(interrupt_controller.kind, kSupportedInterruptControllers[i].kind,
sizeof(interrupt_controller.kind));
dtb_get_reg(fdt, node, addressCells, sizeCells, 0,
interrupt_controller.regs1);
dtb_get_reg(fdt, node, addressCells, sizeCells, 1,
interrupt_controller.regs2);
}
}
}
}
void
arch_dtb_set_kernel_args(void)
{
intc_info &interrupt_controller = gKernelArgs.arch_args.interrupt_controller;
dprintf("Chosen interrupt controller:\n");
if (interrupt_controller.kind[0] == 0) {
dprintf("kind: None!\n");
} else {
dprintf(" kind: %s\n", interrupt_controller.kind);
dprintf(" regs: %#" B_PRIx64 ", %#" B_PRIx64 "\n",
interrupt_controller.regs1.start,
interrupt_controller.regs1.size);
dprintf(" %#" B_PRIx64 ", %#" B_PRIx64 "\n",
interrupt_controller.regs2.start,
interrupt_controller.regs2.size);
}
}
@@ -2,6 +2,8 @@ SubDir HAIKU_TOP src system boot platform efi arch riscv64 ;
SubDirHdrs $(HAIKU_TOP) src system boot platform efi ; SubDirHdrs $(HAIKU_TOP) src system boot platform efi ;
UseLibraryHeaders [ FDirName libfdt ] ;
UsePrivateHeaders [ FDirName kernel platform ] ; UsePrivateHeaders [ FDirName kernel platform ] ;
UsePrivateHeaders [ FDirName kernel boot platform efi ] ; UsePrivateHeaders [ FDirName kernel boot platform efi ] ;
@@ -13,9 +15,10 @@ for platform in [ MultiBootSubDirSetup efi ] {
crt0-efi-$(TARGET_ARCH).S crt0-efi-$(TARGET_ARCH).S
entry.S entry.S
relocation_func.cpp relocation_func.cpp
arch_start.cpp arch_dtb.cpp
arch_smp.cpp
arch_mmu.cpp arch_mmu.cpp
arch_smp.cpp
arch_start.cpp
arch_timer.cpp arch_timer.cpp
arch_traps.cpp arch_traps.cpp
arch_traps_asm.S arch_traps_asm.S
@@ -0,0 +1,117 @@
/*
* Copyright 2019-2021 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Alexander von Gluck IV <[email protected]>
*/
#include <arch_cpu_defs.h>
#include <arch_dtb.h>
#include <arch_smp.h>
#include <boot/platform.h>
#include <boot/stage2.h>
extern "C" {
#include <libfdt.h>
}
#include "dtb.h"
uint32 gBootHart = 0;
static uint64 sTimerFrequency = 10000000;
static addr_range sPlic = {0};
static addr_range sClint = {0};
void
arch_handle_fdt(const void* fdt, int node, uint32 addressCells, uint32 sizeCells)
{
const char* deviceType = (const char*)fdt_getprop(fdt, node,
"device_type", NULL);
const char* name = fdt_get_name(fdt, node, NULL);
if (strcmp(name, "chosen") == 0) {
if (uint32* prop = (uint32*)fdt_getprop(fdt, node, "boot-hartid", NULL))
gBootHart = fdt32_to_cpu(*prop);
} else if (strcmp(name, "cpus") == 0) {
if (uint32* prop = (uint32*)fdt_getprop(fdt, node, "timebase-frequency", NULL))
sTimerFrequency = fdt32_to_cpu(*prop);
}
if (deviceType != NULL) {
if (strcmp(deviceType, "cpu") == 0) {
// TODO: improve incompatible CPU detection
if (!(fdt_getprop(fdt, node, "mmu-type", NULL) != NULL))
return;
platform_cpu_info* info;
arch_smp_register_cpu(&info);
if (info == NULL)
return;
info->id = fdt32_to_cpu(*(uint32*)fdt_getprop(fdt, node,
"reg", NULL));
dprintf("cpu\n");
dprintf(" id: %" B_PRIu32 "\n", info->id);
int subNode = fdt_subnode_offset(fdt, node, "interrupt-controller");
if (subNode < 0) {
dprintf(" [!] no interrupt controller\n");
} else {
info->phandle = fdt_get_phandle(fdt, subNode);
dprintf(" phandle: %" B_PRIu32 "\n", info->phandle);
}
}
}
int compatibleLen;
const char* compatible = (const char*)fdt_getprop(fdt, node,
"compatible", &compatibleLen);
if (compatible == NULL)
return;
if (dtb_has_fdt_string(compatible, compatibleLen, "riscv,clint0")) {
dtb_get_reg(fdt, node, addressCells, sizeCells, 0, sClint);
return;
}
if (dtb_has_fdt_string(compatible, compatibleLen, "riscv,plic0")
|| dtb_has_fdt_string(compatible, compatibleLen, "sifive,plic-1.0.0")) {
dtb_get_reg(fdt, node, addressCells, sizeCells, 0, sPlic);
int propSize;
if (uint32* prop = (uint32*)fdt_getprop(fdt, node, "interrupts-extended", &propSize)) {
dprintf("PLIC contexts\n");
uint32 contextId = 0;
for (uint32 *it = prop; (uint8_t*)it - (uint8_t*)prop < propSize; it += 2) {
uint32 phandle = fdt32_to_cpu(*it);
uint32 interrupt = fdt32_to_cpu(*(it + 1));
if (interrupt == sExternInt) {
platform_cpu_info* cpuInfo = arch_smp_find_cpu(phandle);
dprintf(" context %" B_PRIu32 ": %" B_PRIu32 "\n", contextId, phandle);
if (cpuInfo != NULL) {
cpuInfo->plicContext = contextId;
dprintf(" cpu id: %" B_PRIu32 "\n", cpuInfo->id);
}
}
contextId++;
}
}
return;
}
}
void
arch_dtb_set_kernel_args(void)
{
dprintf("bootHart: %" B_PRIu32 "\n", gBootHart);
dprintf("timerFrequency: %" B_PRIu64 "\n", sTimerFrequency);
gKernelArgs.arch_args.timerFrequency = sTimerFrequency;
// gKernelArgs.arch_args.htif = {.start = 0x40008000, .size = 0x10};
gKernelArgs.arch_args.htif = {.start = 0, .size = 0};
gKernelArgs.arch_args.plic = sPlic;
gKernelArgs.arch_args.clint = sClint;
}
+20 -149
View File
@@ -10,6 +10,7 @@
// TODO: split arch-depending code to per-arch source // TODO: split arch-depending code to per-arch source
#include <arch_cpu_defs.h> #include <arch_cpu_defs.h>
#include <arch_dtb.h>
#include <arch_smp.h> #include <arch_smp.h>
#include <arch/generic/debug_uart_8250.h> #include <arch/generic/debug_uart_8250.h>
#if defined(__riscv) #if defined(__riscv)
@@ -41,14 +42,6 @@ extern "C" {
static void* sDtbTable = NULL; static void* sDtbTable = NULL;
static uint32 sDtbSize = 0; static uint32 sDtbSize = 0;
// TODO: gBootHart is riscy, move
uint32 gBootHart = 0;
static uint64 sTimerFrequency = 10000000;
static addr_range sPlic = {0};
static addr_range sClint = {0};
static void WriteString(const char *str) {dprintf("%s", str);} static void WriteString(const char *str) {dprintf("%s", str);}
static void WriteLn() {dprintf("\n");} static void WriteLn() {dprintf("\n");}
static void WriteHex(uint64_t val, int n) {dprintf("%08" B_PRIx64, val);} static void WriteHex(uint64_t val, int n) {dprintf("%08" B_PRIx64, val);}
@@ -77,19 +70,6 @@ const struct supported_uarts {
}; };
#ifdef __ARM__
const struct supported_interrupt_controllers {
const char* dtb_compat;
const char* kind;
} kSupportedInterruptControllers[] = {
{ "arm,cortex-a9-gic", INTC_KIND_GICV1 },
{ "arm,cortex-a15-gic", INTC_KIND_GICV2 },
{ "ti,omap3-intc", INTC_KIND_OMAP3 },
{ "marvell,pxa-intc", INTC_KIND_PXA },
};
#endif
static void WriteStringList(const char* prop, size_t size) static void WriteStringList(const char* prop, size_t size)
{ {
bool first = true; bool first = true;
@@ -295,8 +275,8 @@ static void DumpFdt(const void *fdt)
static bool bool
HasFdtString(const char* prop, int size, const char* pattern) dtb_has_fdt_string(const char* prop, int size, const char* pattern)
{ {
int patternLen = strlen(pattern); int patternLen = strlen(pattern);
const char* propEnd = prop + size; const char* propEnd = prop + size;
@@ -310,8 +290,8 @@ HasFdtString(const char* prop, int size, const char* pattern)
} }
static bool bool
GetReg(const void* fdt, int node, uint32 addressCells, uint32 sizeCells, size_t idx, addr_range& range) dtb_get_reg(const void* fdt, int node, uint32 addressCells, uint32 sizeCells, size_t idx, addr_range& range)
{ {
int propSize; int propSize;
const uint8* prop = (const uint8*)fdt_getprop(fdt, node, "reg", &propSize); const uint8* prop = (const uint8*)fdt_getprop(fdt, node, "reg", &propSize);
@@ -339,7 +319,7 @@ GetReg(const void* fdt, int node, uint32 addressCells, uint32 sizeCells, size_t
static uint32 static uint32
GetInterruptParent(const void* fdt, int node) dtb_get_interrupt_parent(const void* fdt, int node)
{ {
while (node >= 0) { while (node >= 0) {
uint32* prop; uint32* prop;
@@ -357,9 +337,9 @@ GetInterruptParent(const void* fdt, int node)
static uint32 static uint32
GetInterruptCells(const void* fdt, int node) dtb_get_interrupt_cells(const void* fdt, int node)
{ {
uint32 intc_node = GetInterruptParent(fdt, node); uint32 intc_node = dtb_get_interrupt_parent(fdt, node);
if (intc_node > 0) { if (intc_node > 0) {
uint32* prop = (uint32*)fdt_getprop(fdt, intc_node, "#interrupt-cells", NULL); uint32* prop = (uint32*)fdt_getprop(fdt, intc_node, "#interrupt-cells", NULL);
if (prop != NULL) { if (prop != NULL) {
@@ -372,9 +352,9 @@ GetInterruptCells(const void* fdt, int node)
static uint32 static uint32
GetInterrupt(const void* fdt, int node) dtb_get_interrupt(const void* fdt, int node)
{ {
uint32 interruptCells = GetInterruptCells(fdt, node); uint32 interruptCells = dtb_get_interrupt_cells(fdt, node);
if (uint32* prop = (uint32*)fdt_getprop(fdt, node, "interrupts-extended", NULL)) { if (uint32* prop = (uint32*)fdt_getprop(fdt, node, "interrupts-extended", NULL)) {
return fdt32_to_cpu(*(prop + 1)); return fdt32_to_cpu(*(prop + 1));
@@ -392,7 +372,7 @@ GetInterrupt(const void* fdt, int node)
static int64 static int64
GetClockFrequency(const void* fdt, int node) dtb_get_clock_frequency(const void* fdt, int node)
{ {
uint32* prop; uint32* prop;
int len = 0; int len = 0;
@@ -419,43 +399,9 @@ GetClockFrequency(const void* fdt, int node)
static void static void
HandleFdt(const void* fdt, int node, uint32 addressCells, uint32 sizeCells) dtb_handle_fdt(const void* fdt, int node, uint32 addressCells, uint32 sizeCells)
{ {
const char* name = fdt_get_name(fdt, node, NULL); arch_handle_fdt(fdt, node, addressCells, sizeCells);
if (strcmp(name, "chosen") == 0) {
if (uint32* prop = (uint32*)fdt_getprop(fdt, node, "boot-hartid", NULL))
gBootHart = fdt32_to_cpu(*prop);
} else if (strcmp(name, "cpus") == 0) {
if (uint32* prop = (uint32*)fdt_getprop(fdt, node, "timebase-frequency", NULL))
sTimerFrequency = fdt32_to_cpu(*prop);
}
const char* deviceType = (const char*)fdt_getprop(fdt, node,
"device_type", NULL);
if (deviceType != NULL) {
if (strcmp(deviceType, "cpu") == 0) {
// TODO: improve incompatible CPU detection
if (!(fdt_getprop(fdt, node, "mmu-type", NULL) != NULL))
return;
platform_cpu_info* info;
arch_smp_register_cpu(&info);
if (info == NULL)
return;
info->id = fdt32_to_cpu(*(uint32*)fdt_getprop(fdt, node,
"reg", NULL));
dprintf("cpu\n");
dprintf(" id: %" B_PRIu32 "\n", info->id);
int subNode = fdt_subnode_offset(fdt, node, "interrupt-controller");
if (subNode < 0) {
dprintf(" [!] no interrupt controller\n");
} else {
info->phandle = fdt_get_phandle(fdt, subNode);
dprintf(" phandle: %" B_PRIu32 "\n", info->phandle);
}
}
}
int compatibleLen; int compatibleLen;
const char* compatible = (const char*)fdt_getprop(fdt, node, const char* compatible = (const char*)fdt_getprop(fdt, node,
@@ -464,50 +410,21 @@ HandleFdt(const void* fdt, int node, uint32 addressCells, uint32 sizeCells)
if (compatible == NULL) if (compatible == NULL)
return; return;
if (HasFdtString(compatible, compatibleLen, "riscv,clint0")) {
GetReg(fdt, node, addressCells, sizeCells, 0, sClint);
return;
}
if (HasFdtString(compatible, compatibleLen, "riscv,plic0")
|| HasFdtString(compatible, compatibleLen, "sifive,plic-1.0.0")) {
GetReg(fdt, node, addressCells, sizeCells, 0, sPlic);
int propSize;
if (uint32* prop = (uint32*)fdt_getprop(fdt, node, "interrupts-extended", &propSize)) {
dprintf("PLIC contexts\n");
uint32 contextId = 0;
for (uint32 *it = prop; (uint8_t*)it - (uint8_t*)prop < propSize; it += 2) {
uint32 phandle = fdt32_to_cpu(*it);
uint32 interrupt = fdt32_to_cpu(*(it + 1));
if (interrupt == sExternInt) {
platform_cpu_info* cpuInfo = arch_smp_find_cpu(phandle);
dprintf(" context %" B_PRIu32 ": %" B_PRIu32 "\n", contextId, phandle);
if (cpuInfo != NULL) {
cpuInfo->plicContext = contextId;
dprintf(" cpu id: %" B_PRIu32 "\n", cpuInfo->id);
}
}
contextId++;
}
}
return;
}
// TODO: We should check for the "chosen" uart and prioritize that one // TODO: We should check for the "chosen" uart and prioritize that one
// check for a uart if we don't have one // check for a uart if we don't have one
uart_info &uart = gKernelArgs.arch_args.uart; uart_info &uart = gKernelArgs.arch_args.uart;
if (uart.kind[0] == 0) { if (uart.kind[0] == 0) {
for (uint32 i = 0; i < B_COUNT_OF(kSupportedUarts); i++) { for (uint32 i = 0; i < B_COUNT_OF(kSupportedUarts); i++) {
if (HasFdtString(compatible, compatibleLen, if (dtb_has_fdt_string(compatible, compatibleLen,
kSupportedUarts[i].dtb_compat)) { kSupportedUarts[i].dtb_compat)) {
memcpy(uart.kind, kSupportedUarts[i].kind, memcpy(uart.kind, kSupportedUarts[i].kind,
sizeof(uart.kind)); sizeof(uart.kind));
GetReg(fdt, node, addressCells, sizeCells, 0, uart.regs); dtb_get_reg(fdt, node, addressCells, sizeCells, 0, uart.regs);
uart.irq = GetInterrupt(fdt, node); uart.irq = dtb_get_interrupt(fdt, node);
uart.clock = GetClockFrequency(fdt, node); uart.clock = dtb_get_clock_frequency(fdt, node);
gUART = kSupportedUarts[i].uart_driver_init(uart.regs.start, gUART = kSupportedUarts[i].uart_driver_init(uart.regs.start,
uart.clock); uart.clock);
@@ -517,25 +434,6 @@ HandleFdt(const void* fdt, int node, uint32 addressCells, uint32 sizeCells)
if (gUART != NULL) if (gUART != NULL)
gUART->InitEarly(); gUART->InitEarly();
} }
#if defined(__ARM__)
intc_info &interrupt_controller = gKernelArgs.arch_args.interrupt_controller;
if (interrupt_controller.kind[0] == 0) {
for (uint32 i = 0; i < B_COUNT_OF(kSupportedInterruptControllers); i++) {
if (HasFdtString(compatible, compatibleLen,
kSupportedInterruptControllers[i].dtb_compat)) {
memcpy(interrupt_controller.kind, kSupportedInterruptControllers[i].kind,
sizeof(interrupt_controller.kind));
GetReg(fdt, node, addressCells, sizeCells, 0,
interrupt_controller.regs1);
GetReg(fdt, node, addressCells, sizeCells, 1,
interrupt_controller.regs2);
}
}
}
#endif
} }
@@ -574,7 +472,7 @@ dtb_init()
int node = -1; int node = -1;
int depth = -1; int depth = -1;
while ((node = fdt_next_node(sDtbTable, node, &depth)) >= 0 && depth >= 0) { while ((node = fdt_next_node(sDtbTable, node, &depth)) >= 0 && depth >= 0) {
HandleFdt(sDtbTable, node, 2, 2); dtb_handle_fdt(sDtbTable, node, 2, 2);
} }
break; break;
} }
@@ -586,7 +484,6 @@ dtb_set_kernel_args()
{ {
// pack into proper location if the architecture cares // pack into proper location if the architecture cares
if (sDtbTable != NULL) { if (sDtbTable != NULL) {
#if defined(__ARM__) || defined(__riscv)
// libfdt requires 8-byte alignment // libfdt requires 8-byte alignment
gKernelArgs.arch_args.fdt = (void*)(addr_t)kernel_args_malloc(sDtbSize, 8); gKernelArgs.arch_args.fdt = (void*)(addr_t)kernel_args_malloc(sDtbSize, 8);
@@ -594,20 +491,8 @@ dtb_set_kernel_args()
memcpy(gKernelArgs.arch_args.fdt, sDtbTable, sDtbSize); memcpy(gKernelArgs.arch_args.fdt, sDtbTable, sDtbSize);
else else
ERROR("unable to malloc for fdt!\n"); ERROR("unable to malloc for fdt!\n");
#endif
} }
#ifdef __riscv
dprintf("bootHart: %" B_PRIu32 "\n", gBootHart);
dprintf("timerFrequency: %" B_PRIu64 "\n", sTimerFrequency);
gKernelArgs.arch_args.timerFrequency = sTimerFrequency;
// gKernelArgs.arch_args.htif = {.start = 0x40008000, .size = 0x10};
gKernelArgs.arch_args.htif = {.start = 0, .size = 0};
gKernelArgs.arch_args.plic = sPlic;
gKernelArgs.arch_args.clint = sClint;
#endif
#if defined(__ARM__) || defined(__riscv)
uart_info &uart = gKernelArgs.arch_args.uart; uart_info &uart = gKernelArgs.arch_args.uart;
dprintf("Chosen UART:\n"); dprintf("Chosen UART:\n");
if (uart.kind[0] == 0) { if (uart.kind[0] == 0) {
@@ -619,20 +504,6 @@ dtb_set_kernel_args()
dprintf(" irq: %" B_PRIu32 "\n", uart.irq); dprintf(" irq: %" B_PRIu32 "\n", uart.irq);
dprintf(" clock: %" B_PRIu64 "\n", uart.clock); dprintf(" clock: %" B_PRIu64 "\n", uart.clock);
} }
#endif
#if defined(__ARM__) arch_dtb_set_kernel_args();
intc_info &interrupt_controller = gKernelArgs.arch_args.interrupt_controller;
dprintf("Chosen interrupt controller:\n");
if (interrupt_controller.kind[0] == 0) {
dprintf("kind: None!\n");
} else {
dprintf(" kind: %s\n", interrupt_controller.kind);
dprintf(" regs: %#" B_PRIx64 ", %#" B_PRIx64 "\n",
interrupt_controller.regs1.start,
interrupt_controller.regs1.size);
dprintf(" %#" B_PRIx64 ", %#" B_PRIx64 "\n",
interrupt_controller.regs2.start,
interrupt_controller.regs2.size);
}
#endif
} }
+3
View File
@@ -15,6 +15,9 @@
extern void dtb_init(); extern void dtb_init();
extern void dtb_set_kernel_args(); extern void dtb_set_kernel_args();
bool dtb_get_reg(const void* fdt, int node, uint32 addressCells, uint32 sizeCells, size_t idx, addr_range& range);
bool dtb_has_fdt_string(const char* prop, int size, const char* pattern);
#endif /* !_ASSEMBLER */ #endif /* !_ASSEMBLER */