arm: generate memory region tables from dts

This change adds infrastructure to automatically generate the
physBase macro, the avail_p_regs array, and the dev_p_regs array
based on a device tree. Platforms can opt-in to using this
by adding DTS files to the KernelDTSList variable.

The Python script uses the hardware.yml file to determine which
devices in the device tree are of interest to the kernel and should
be hidden from userspace and instead mapped into the kernel. Note that
currently the kernel mappings are not (yet) generated, however most
of the infrastructure needed to make that happen is present.
This commit is contained in:
Simon Shields 2018-12-10 16:08:14 +11:00
parent 463c1cc211
commit 0ac0792339
5 changed files with 813 additions and 0 deletions

View file

@ -22,6 +22,7 @@ Upcoming release: BREAKING
- a new constant in libsel4 that contains the first virtual address unavailable to
user level.
* Support added for Aarch64 hypervisor mode (EL2) for Nvidia TX1 and TX2. This is not verified.
* Support for generating ARM machine header files (memory regions) based on a device tree.
## Upgrade Notes
---

View file

@ -39,6 +39,7 @@ set(PYTHON "python" CACHE INTERNAL "")
RequireTool(CPP_GEN_PATH cpp_gen.sh)
RequireTool(CIRCULAR_INCLUDES circular_includes.py)
RequireTool(BF_GEN_PATH bitfield_gen.py)
RequireTool(HARDWARE_GEN_PATH hardware_gen.py)
RequireTool(INVOCATION_ID_GEN_PATH invocation_header_gen.py)
RequireTool(SYSCALL_ID_GEN_PATH syscall_header_gen.py)
RequireTool(XMLLINT_PATH xmllint.sh)
@ -275,6 +276,48 @@ add_custom_target(xml_headers_target DEPENDS ${xml_headers})
add_dependencies(kernel_headers xml_headers_target)
include_directories("${CMAKE_CURRENT_BINARY_DIR}/gen_headers")
if (DEFINED KernelDTSList)
find_program(DTC_TOOL dtc)
if ("${DTC_TOOL}" STREQUAL "DTC_TOOL-NOTFOUND")
message(FATAL_ERROR "Cannot find 'dtc' program.")
endif()
foreach(entry ${KernelDTSList})
get_absolute_source_or_binary(dts_tmp ${entry})
list(APPEND dts_list ${dts_tmp})
endforeach()
# Generate final DTS based on Linux DTS + seL4 overlay[s]
set(KernelDTSIntermediate "kernel.dts")
add_custom_command(OUTPUT ${KernelDTSIntermediate}
COMMAND cat ${dts_list} > "${KernelDTSIntermediate}"
COMMENT "Generate kernel DTS"
DEPENDS ${dts_list})
# Compile DTS to DTB
set(KernelDTBPath "kernel.dtb")
add_custom_command(OUTPUT ${KernelDTBPath}
COMMAND ${DTC_TOOL} -q -I dts -O dtb -o ${KernelDTBPath} ${KernelDTSIntermediate}
DEPENDS ${KernelDTSIntermediate})
# Generate devices_gen header based on DTB
set(device_dest "gen_headers/plat/machine/devices_gen.h")
set(config_file "${CMAKE_CURRENT_SOURCE_DIR}/tools/hardware.yml")
set(config_schema "${CMAKE_CURRENT_SOURCE_DIR}/tools/hardware_schema.yml")
add_custom_command(OUTPUT ${device_dest}
COMMAND ${PYTHON} "${HARDWARE_GEN_PATH}" --dtb "${KernelDTBPath}" --output "${device_dest}" --config "${config_file}" --schema "${config_schema}"
DEPENDS "${HARDWARE_GEN_PATH}" "${KernelDTBPath}" "${config_file}" "${config_schema}"
COMMENT "Generate hardware description"
VERBATIM
)
# Construct target for the generated hardware header
add_custom_target(hardware_gen_target DEPENDS "${device_dest}")
# Add the hardware header to all the kernel headers
add_dependencies(kernel_headers hardware_gen_target)
list(APPEND gen_files_list "${device_dest}")
endif()
#######################
# Prune list generation
#######################

174
tools/hardware.yml Normal file
View file

@ -0,0 +1,174 @@
#
# Copyright 2018, Data61
# Commonwealth Scientific and Industrial Research Organisation (CSIRO)
# ABN 41 687 119 230.
#
# This software may be distributed and modified according to the terms of
# the GNU General Public License version 2. Note that NO WARRANTY is provided.
# See "LICENSE_GPLv2.txt" for details.
#
# @TAG(DATA61_GPL)
#
# Documentation for bindings is relative to Documentation/devicetree/bindings in Linux,
# unless otherwise noted.
buses:
- simple-bus
- qcom,gsbi-v1.0.0
devices:
# ARM GIC (interrupt-controller/arm,gic.txt)
- compatible:
- arm,cortex-a15-gic
- arm,cortex-a7-gic
- arm,cortex-a9-gic
- arm,gic-400
- qcom,msm-qgic2
regions:
# distributor
- executeNever: true
index: 0
kernel: GIC_PL390_DISTRIBUTOR_PPTR
# controller
- executeNever: true
index: 1
kernel: GIC_PL390_CONTROLLER_PPTR
# GICV
- executeNever: false
index: 2
kernel: GIC_PL400_VCPUCTRL_PPTR
macro: CONFIG_ARM_HYPERVISOR_SUPPORT
user: false # never expose to userspace, even if macro is false.
# other regions (i.e. GICH)
- user: true
# Broadcom second level IRQ controller (interrupt-controller/brcm,bcm2835-armctrl-ic.txt),
# TI AM33XX/OMAP3 intc (interrupt-controller/ti,omap-intc-irq.txt)
- compatible:
- brcm,bcm2836-armctrl-ic
- ti,am33xx-intc
- ti,omap3-intc
regions:
- executeNever: true
index: 0
kernel: INTC_PPTR
# Broadcom top level IRQ controller (interrupt-controller/brcm,bcm2836-l1-intc.txt)
- compatible:
- brcm,bcm2836-l1-intc
regions:
- executeNever: true
index: 0
kernel: ARM_LOCAL_PPTR
# i.MX AVIC (no Linux docs, used in arch/arm/mach-imx/avic.c)
- compatible:
- fsl,imx31-avic
regions:
- executeNever: true
index: 0
kernel: AVIC_PPTR
# ARM PL310 L2 cache controller (arm/l2c2x0.txt)
- compatible:
- arm,pl310-cache
regions:
- executeNever: true
index: 0
kernel: L2CC_L2C310_PPTR
# i.MX31 L2CC (seL4 only)
- compatible:
- fsl,imx31-l2cc
regions:
- executeNever: true
index: 0
kernel: L2CC_PPTR
# Exynos multi core timer (timer/samsung,exynos4210-mct.txt)
# Exynos4412 MCT is separate as we use it for the timer IRQ.
# Other Exynos platforms use the ARM architecture timer.
- compatible:
- samsung,exynos4412-mct
regions:
- executeNever: true
index: 0
kernel: EXYNOS_MCT_PPTR
- compatible:
- samsung,exynos4210-mct
regions:
- executeNever: true
index: 0
kernel: EXYNOS_MCT_PPTR
# Tegra SMMU (memory-controllers/nvidia,tegra30-mc.txt)
- compatible:
- nvidia,tegra124-mc
regions:
- executeNever: false
index: 0
kernel: SMMU_PPTR
macro: CONFIG_ARM_SMMU
# ARM per-core timer-watchdog (timer/arm,twd.txt)
- compatible:
- arm,cortex-a9-twd-timer
regions:
- executeNever: true
index: 0
kernel: ARM_MP_PRIV_TIMER_PPTR
# i.MX EPIT (no Linux binding, this is seL4-specific.)
- compatible:
- fsl,imx31-epit
regions:
- executeNever: true
index: 0
kernel: EPIT_PPTR
# QCOM Krait timer (timer/qcom,msm-timer.txt)
- compatible:
- qcom,kpss-timer
regions:
- executeNever: true
index: 0
kernel: TIMER_PPTR
# TI AM335x/OMAP3430 timer
- compatible:
- ti,am335x-timer
- ti,omap3430-timer
regions:
- executeNever: true
index: 0
kernel: TIMER_PPTR
chosen: seL4,timer
# TI prcm (arm/omap/prcm.txt)
- compatible:
- ti,omap4-cm
regions:
- executeNever: true
index: 0
kernel: CMPER_PPTR
user: true
# TI watchdog
- compatible:
- ti,omap3-wdt
regions:
- executeNever: true
index: 0
kernel: WDT1_PPTR
user: true
# various serial consoles (`grep <compatible> serial/*`)
- compatible:
- arm,pl011
- brcm,bcm2835-aux-uart
- fsl,imx31-uart
- fsl,imx6q-uart
- nvidia,tegra124-hsuart
- nvidia,tegra20-uart
- qcom,msm-uartdm
- samsung,exynos4210-uart
- snps,dw-apb-uart
- ti,am3352-uart
- ti,omap3-uart
- xlnx,xuartps
chosen: stdout-path
regions:
- executeNever: true
index: 0
kernel: UART_PPTR
macro: CONFIG_PRINTING
user: true

488
tools/hardware_gen.py Executable file
View file

@ -0,0 +1,488 @@
#!/usr/bin/env python
#
# Copyright 2018, Data61
# Commonwealth Scientific and Industrial Research Organisation (CSIRO)
# ABN 41 687 119 230.
#
# This software may be distributed and modified according to the terms of
# the GNU General Public License version 2. Note that NO WARRANTY is provided.
# See "LICENSE_GPLv2.txt" for details.
#
# @TAG(DATA61_GPL)
#
from __future__ import print_function, division
import sys
import os.path
import argparse
import logging
import pyfdt.pyfdt
import yaml
from jinja2 import Environment, BaseLoader
try:
from jsonschema import validate
except ImportError:
print("Skipping hardware YAML validation, `pip install jsonschema` to validate")
def validate(*args, **kwargs):
pass
def align_down(addr, boundary):
return addr & ~(boundary - 1)
def align_up(addr, boundary):
return (addr + (boundary - 1)) & ~(boundary - 1)
def make_number(cells, array):
""" make a number with "cells" cells from the given list """
ret = 0
for i in range(cells):
ret = (ret << 32)
ret |= array.pop(0)
return ret
class Region:
def __init__(self, start, size, name, index=0):
self.start = start
self.size = size
if isinstance(name, Region):
other = name
self.names = set(other.names)
self.index = other.index
self.kaddr = other.kaddr
self.kernel_var = other.kernel_var
self.kernel_macro = other.kernel_macro
self.user_macro = other.kernel_macro
else:
self.names = set()
self.names.add(name)
self.index = index
self.kaddr = 0
self.kernel_var = ""
self.kernel_macro = ""
self.user_macro = False
def __eq__(self, other):
if other.start == self.start and other.size == self.size:
return True
return False
def __hash__(self):
return hash((self.start, self.size))
def __str__(self):
return '%s(0x%x-0x%x)'%('+'.join(sorted(self.names)), self.start, self.start + self.size - 1)
def __repr__(self):
return str(self)
def isempty(self):
return self.size <= 0
def overlaps(self, other):
sstart = self.start
send = self.start + self.size
ostart = other.start
oend = other.start + other.size
return (sstart <= ostart and send > ostart) or \
(ostart <= sstart and oend > sstart)
def remove_subregions(self, subregions):
new = []
for i in range(len(subregions)):
reg = subregions[i]
base = reg['address']
end = reg['size'] + base
if self.start <= base < (self.start + self.size):
newsize = base - self.start
newstart = end
if newstart < (self.start + self.size):
newreg = Region(newstart, (self.start + self.size - newstart), self)
new += newreg.remove_subregions(subregions[i+1:])
self.size = newsize
new.append(self)
return filter(lambda a: not a.isempty(), new)
class Device:
def __init__(self, node, parent, name):
self.props = {}
self.node = node
self.parent = parent
self.name = name
for o in node:
if isinstance(o, pyfdt.pyfdt.FdtNop):
continue
self.props[o.get_name()] = o
def get_addr_cells(self):
if '#address-cells' in self.props:
return self.props['#address-cells'].words[0]
return 2
def get_size_cells(self):
if '#size-cells' in self.props:
return self.props['#size-cells'].words[0]
return 1
def translate_child_address(self, addr):
if self.parent is None:
# the root node does not need to do any translation of addresses
return addr
if 'ranges' not in self.props or not isinstance(self.props['ranges'], pyfdt.pyfdt.FdtPropertyWords):
return self.parent.translate_child_address(addr)
# ranges is a list with the following format:
# <child-bus-address> <parent-bus-address> <length>
# child-bus-address is self.get_addr_cells() cells long
# parent-bus-address is self.parent.get_addr_cells() cells long
# length is self.get_size_cells() cells long
child_addr_cells = self.get_addr_cells()
parent_addr_cells = self.parent.get_addr_cells()
size_cells = self.get_size_cells()
# We assume that a child's region
# will stay within one "ranges" entry.
data = list(self.props['ranges'].words)
while len(data) > 0:
cbase = make_number(child_addr_cells, data)
pbase = make_number(parent_addr_cells, data)
length = make_number(size_cells, data)
if cbase <= addr < (cbase + length):
return self.parent.translate_child_address(addr - cbase + pbase)
# if we get here, the device tree is probably wrong - print out a warning
# but continue as though the address was identity-mapped.
logging.warning("Untranslatable address 0x%x in %s, not translating"%(addr, self.name))
return self.parent.translate_child_address(addr)
def is_memory(self):
return 'device_type' in self.props and self.props['device_type'].strings[0] == 'memory'
def regions(self, config, by_phandle):
addr_cells = self.parent.get_addr_cells()
size_cells = self.parent.get_size_cells()
if addr_cells == 0 or size_cells == 0 or 'reg' not in self:
return (set(), set())
regions = []
prop = self['reg']
data = list(prop.words)
while len(data) > 0:
base = make_number(addr_cells, data)
length = make_number(size_cells, data)
regions.append(Region(self.parent.translate_child_address(base), length, self.node.get_name()))
if not self.is_memory() and 'compatible' in self.props:
(user, kernel) = config.split_regions(self, regions, by_phandle)
else:
user = set(regions)
kernel = set()
return (user, kernel)
def __getitem__(self, val):
return self.props[val]
def __contains__(self, key):
return key in self.props
class Config:
def __init__(self, blob):
self.devices = {}
self.blob = blob
self.chosen = None
self.aliases = None
self.buses = blob['buses']
# wrangle the json a little so it's easier
# to figure out which rules apply to a given device
for dev in blob['devices']:
for compat in dev['compatible']:
if compat in self.devices:
self.devices[compat].append(dev)
else:
self.devices[compat] = [dev]
def set_chosen(self, chosen):
self.chosen = chosen
def set_aliases(self, aliases):
self.aliases = aliases
def _apply_rule(self, region, rule):
if 'kernel' in rule and rule['kernel'] != False:
region.kernel_var = rule['kernel']
region.kernel_macro = rule.get('macro', None)
region.executeNever = rule['executeNever']
region.user_macro = (not rule.get('user', False)) and region.kernel_macro is not None
return region
def _lookup_alias(self, name):
if self.aliases is None:
return ''
if name not in self.aliases:
return ''
return self.aliases[name].strings[0]
def _is_chosen(self, device, rule, by_phandle):
if 'chosen' not in rule:
return True
if self.chosen is None:
return False
prop = rule['chosen']
if prop not in self.chosen:
return False
val = self.chosen[prop]
# a "chosen" field will either have a phandle, or
# a path or an alias, then a ":", then other data.
# the path/alias may not contain a ":".
if isinstance(val, pyfdt.pyfdt.FdtPropertyWords):
return 'phandle' in device and device['phandle'].words[0] == val.words[0]
val = val.strings[0].split(':')[0]
# path starts with '/'.
if val[0] == '/':
return device.name == val
else:
return device.name == self._lookup_alias(val)
def split_regions(self, device, regions, by_phandle):
compat = device['compatible']
for compatible in compat.strings:
if compatible not in self.devices:
continue
user = set()
kernel = set()
default_user = True
for rule in self.devices[compatible]:
regs = []
if not self._is_chosen(device, rule, by_phandle):
continue
if 'regions' in rule:
for reg_rule in rule['regions']:
if 'index' not in reg_rule:
default_user = reg_rule['user']
continue
if reg_rule['index'] >= len(regions):
continue
reg = self._apply_rule(regions[reg_rule['index']], reg_rule)
if reg.user_macro or ('user' in reg_rule and reg_rule['user'] == True):
user.add(reg)
regs.append(reg)
kernel.update(set(regs))
if default_user:
user = user.union(set(regions).difference(kernel))
return (user, kernel)
return (set(regions), set())
def is_compatible(node, compatibles):
""" returns True if node matches a compatible in the given list """
try:
prop = node.index("compatible")
except ValueError:
return False
for c in compatibles:
if c in node[prop].strings:
return True
return False
def should_parse_regions(root, node, buses):
""" returns True if we should parse regions found in this node. """
parent = node.get_parent_node()
return parent == root or is_compatible(parent, buses)
def find_devices(dtb, cfg):
devices = {}
nodes = {}
by_phandle = {}
chosen = None
aliases = None
root = dtb.get_rootnode()
devices[root] = Device(root, None, '/')
nodes[root] = devices[root]
for child in root.walk(): # walk every node in the whole tree
name = child[0]
child = child[1]
if not isinstance(child, pyfdt.pyfdt.FdtNode):
continue
if name == '/chosen':
cfg.set_chosen(Device(child, None, name))
elif name == '/aliases':
cfg.set_aliases(Device(child, None, name))
if should_parse_regions(root, child, cfg.buses):
devices[child] = Device(child, devices[child.get_parent_node()], name)
nodes[child] = devices[child]
else:
nodes[child] = Device(child, nodes[child.get_parent_node()], name)
try:
idx = child.index('phandle')
except ValueError:
continue
prop = child[idx]
by_phandle[prop.words[0]] = nodes[child]
# the root node is not actually a device, so remove it.
del devices[root]
return {d.name: d for d in devices.values()}, by_phandle
def fixup_device_regions(regions, pagesz, merge=False):
""" page align all regions and check for overlapping regions """
ret = list()
for r in regions:
r.start = align_down(r.start, pagesz)
r.size = align_up(r.size, pagesz)
# we abuse the fact that regions will be
# "equal" if they have different names but the same range.
if r in ret:
ret[ret.index(r)].names.update(r.names)
else:
ret.append(r)
if merge:
ret = sorted(ret, key=lambda a: a.start)
i = 1
while i < len(ret):
if ret[i].start == ret[i-1].start + ret[i-1].size:
ret[i-1].size += ret[i].size
ret[i-1].names.update(ret[i].names)
del ret[i]
else:
i += 1
return set(ret)
HEADER_TEMPLATE = """
/*
* Copyright 2018, Data61
* Commonwealth Scientific and Industrial Research Organisation (CSIRO)
* ABN 41 687 119 230.
*
* This software may be distributed and modified according to the terms of
* the GNU General Public License version 2. Note that NO WARRANTY is provided.
* See "LICENSE_GPLv2.txt" for details.
*
* @TAG(DATA61_GPL)
*/
/*
* This file is autogenerated by kernel/tools/hardware_gen.py from
* {{args.dtb.name}}.
*/
#ifndef __PLAT_DEVICES_GEN_H
#define __PLAT_DEVICES_GEN_H
#define physBase {{ "0x%x" % physBase }}
#if 0
static const kernel_frame_t BOOT_RODATA kernel_devices[] = {
{%- for reg in kernel %}
{%- if reg.kernel_macro %}
#ifdef {{ reg.kernel_macro }}
{%- endif %}
#define {{ reg.kernel_var }} {{ "0x%x" % reg.kaddr }}
{ /* {{ ' '.join(sorted(reg.names)) }} */
{{ "0x%x" % reg.start }},
{{ reg.kernel_var }},
{{ "true" if reg.executeNever else "false" }} /* armExecuteNever */
},
{%- if reg.kernel_macro %}
#endif /* {{ reg.kernel_macro }} */
{%- endif %}
{%- endfor %}
};
#endif
static const p_region_t BOOT_RODATA avail_p_regs[] = {
{%- for reg in memory %}
{ {{ "0x%x" % reg.start }}, {{ "0x%x" % (reg.start + reg.size) }} }, /* {{ ' '.join(sorted(reg.names)) }} */
{%- endfor %}
};
static const p_region_t BOOT_RODATA dev_p_regs[] = {
{%- for reg in devices %}
{%- if reg.user_macro %}
#ifndef {{ reg.kernel_macro }}
{%- endif %}
{ {{ "0x%x" % reg.start }}, {{ "0x%x" % (reg.start + reg.size) }} }, /* {{ ' '.join(sorted(reg.names)) }} */
{%- if reg.user_macro %}
#endif /* {{ reg.kernel_macro }} */
{%- endif %}
{%- endfor %}
};
#endif /* __PLAT_DEVICES_GEN_H */
"""
def output_regions(args, devices, memory, kernel, fp):
""" generate the device list for the C header file """
memory = sorted(memory, key=lambda a: a.start)
kernel = sorted(kernel, key=lambda a: a.start)
addr = 0x0 # args.kernel_base
for reg in kernel:
reg.kaddr = addr
addr += align_up(reg.size, 1 << args.page_bits)
# make sure physBase is at least 16MB (supersection) aligned for the ELF loader's sake.
# TODO: this may need to be larger for aarch64. It seems to work OK on currently supported platforms though.
paddr = align_up(memory[0].start, 1 << args.phys_align)
memory[0].size -= paddr - memory[0].start
memory[0].start = paddr
template = Environment(loader=BaseLoader, trim_blocks=False, lstrip_blocks=False).from_string(HEADER_TEMPLATE)
data = template.render({'args': args, 'devices': sorted(devices, key=lambda a: a.start), 'sorted': sorted,
'memory': memory, 'physBase': paddr, 'kernel': kernel})
fp.write(data)
def main(args):
with open(args.schema) as fp:
schema = yaml.load(fp)
with open(args.config) as blob:
kernel_devices = yaml.load(blob)
validate(kernel_devices, schema)
cfg = Config(kernel_devices)
memory, user, kernel = set(), set(), set()
fdt = pyfdt.pyfdt.FdtBlobParse(args.dtb).to_fdt()
devices, by_phandle = find_devices(fdt, cfg)
for d in devices.values():
if d.is_memory():
m, _ = d.regions(cfg, by_phandle) # second set is always empty for memory
res = set()
for e in m:
res.update(set(e.remove_subregions(fdt.reserve_entries)))
memory.update(res)
else:
(u, k) = d.regions(cfg, by_phandle)
user.update(u)
kernel.update(k)
user = fixup_device_regions(user, 1 << args.page_bits, merge=True)
kernel = fixup_device_regions(kernel, 1 << args.page_bits)
output_regions(args, user, memory, kernel, args.output)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--dtb', help='device tree blob to use for generation', required=True, type=argparse.FileType())
parser.add_argument('--output', help='output file for generated header', required=True, type=argparse.FileType('w'))
parser.add_argument('--page-bits', help='number of bits per page', default=12, type=int)
parser.add_argument('--phys-align', help='alignment in bits of the base address of the kernel', default=24, type=int)
#parser.add_argument('--kernel-base', help='first address to use for kernel device mappings', type=lambda a: int(a, 0), required=True)
parser.add_argument('--config', help='kernel device configuration', required=True)
parser.add_argument('--schema', help='config file schema for validation', required=True)
args = parser.parse_args()
main(args)

107
tools/hardware_schema.yml Normal file
View file

@ -0,0 +1,107 @@
#
# Copyright 2018, Data61
# Commonwealth Scientific and Industrial Research Organisation (CSIRO)
# ABN 41 687 119 230.
#
# This software may be distributed and modified according to the terms of
# the GNU General Public License version 2. Note that NO WARRANTY is provided.
# See "LICENSE_GPLv2.txt" for details.
#
# @TAG(DATA61_GPL)
#
---
$schema: http://json-schema.org/draft-07/schema#
description: Schema for seL4 hardware.yml
type: object
additionalProperties: false
properties:
buses:
type: array
uniqueItems: true
description: list of compatibles to treat as MMIO buses
items:
type: string
devices:
type: array
uniqueItems: true
description: top-level list of devices
items:
$ref: '#/definitions/device'
required: [devices]
definitions:
device:
type: object
additionalProperties: false
properties:
compatible:
description: List of compatible strings to which this rule applies
type: array
minItems: 1
uniqueItems: true
regions:
description: Memory regions this device has that the kernel might use
type: array
items:
$ref: '#/definitions/region'
uniqueItems: true
chosen:
description: >
Apply this rule only to the device referenced by the correspondingly named
property in the chosen node. For instance, a dts like this
aliases {
serial0 = &serial_0;
};
chosen {
stdout-path = "serial0";
};
serial_0: serial@13800000 {
compatible = "samsung,exynos4210-uart";
reg = <0x13800000 0x100>;
/* etc */
};
serial_1: serial@13810000 {
compatible = "samsung,exynos4210-uart";
reg = <0x13810000 0x100>;
/* etc */
};
and a device with the property chosen set to stdout-path will only match the serial@13800000
device and not any other device with the samsung,exynos4210-uart compatible.
type: string
required: [compatible]
region:
type: object
additionalProperties: false
properties:
executeNever:
description: should this region be mapped as execute never?
type: boolean
index:
description: Region index this property should apply to
type: integer
minimum: 0
kernel:
description: kernel macro used to access this region. If not present, region will not be mapped.
$ref: '#/definitions/macro'
macro:
description: only map the region to the kernel if this macro is defined
$ref: '#/definitions/macro'
user:
description: >
Whether or not to make a device untyped for userspace.
If true, will always expose this device to userspace.
If there is a macro and the macro is undefined, and user is not present,
the device will be made.
If false, the device will never be made.
type: boolean
dependencies:
kernel: [executeNever, index]
executeNever: [kernel]
macro:
type: string
pattern: '^[A-Za-z_][A-Za-z0-9_]*$'
minLength: 1