audk/ArmVirtPkg/Library/ArmXenRelocatablePlatformLib/AARCH64/MemnodeParser.S

238 lines
5.5 KiB
ArmAsm

/*
* Copyright (c) 2014, Linaro Ltd. All rights reserved.
*
* This program and the accompanying materials
* are licensed and made available under the terms and conditions of the BSD License
* which accompanies this distribution. The full text of the license may be found at
* http://opensource.org/licenses/bsd-license.php
*
* THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
* WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
*/
/*
* Theory of operation
* -------------------
*
* This code parses a Flattened Device Tree binary (DTB) to find the base of
* system RAM. It is written in assembly so that it can be executed before a
* stack has been set up.
*
* To find the base of system RAM, we have to traverse the FDT to find a memory
* node. In the context of this implementation, the first node that has a
* device_type property with the value 'memory' and a 'reg' property is
* acceptable, and the name of the node (memory[@xxx]) is ignored, as are any
* other nodes that match the above constraints.
*
* In pseudo code, this implementation does the following:
*
* for each node {
* have_device_type = false
* have_reg = false
*
* for each property {
* if property value == 'memory' {
* if property name == 'device_type' {
* have_device_type = true
* }
* } else {
* if property name == 'reg' {
* have_reg = true
* membase = property value[0]
* memsize = property value[1]
* }
* }
* }
* if have_device_type and have_reg {
* return membase and memsize
* }
* }
* return NOT_FOUND
*/
#define FDT_MAGIC 0xedfe0dd0
#define FDT_BEGIN_NODE 0x1
#define FDT_END_NODE 0x2
#define FDT_PROP 0x3
#define FDT_END 0x9
xMEMSIZE .req x0 // recorded system RAM size
xMEMBASE .req x1 // recorded system RAM base
xLR .req x8 // our preserved link register
xDTP .req x9 // pointer to traverse the DT structure
xSTRTAB .req x10 // pointer to the DTB string table
xMEMNODE .req x11 // bit field to record found properties
#define HAVE_REG 0x1
#define HAVE_DEVICE_TYPE 0x2
.text
.align 3
_memory:
.asciz "memory"
_reg:
.asciz "reg"
_device_type:
.asciz "device_type"
/*
* Compare strings in x4 and x5, return in w7
*/
.align 3
strcmp:
ldrb w2, [x4], #1
ldrb w3, [x5], #1
subs w7, w2, w3
cbz w2, 0f
cbz w3, 0f
beq strcmp
0: ret
.globl find_memnode
find_memnode:
// preserve link register
mov xLR, x30
mov xDTP, x0
/*
* Check the DTB magic at offset 0
*/
movz w4, #:abs_g0_nc:FDT_MAGIC
movk w4, #:abs_g1:FDT_MAGIC
ldr w5, [xDTP]
cmp w4, w5
bne err_invalid_magic
/*
* Read the string offset and store it for later use
*/
ldr w4, [xDTP, #12]
rev w4, w4
add xSTRTAB, xDTP, x4
/*
* Read the struct offset and add it to the DT pointer
*/
ldr w5, [xDTP, #8]
rev w5, w5
add xDTP, xDTP, x5
/*
* Check current tag for FDT_BEGIN_NODE
*/
ldr w5, [xDTP]
rev w5, w5
cmp w5, #FDT_BEGIN_NODE
bne err_unexpected_begin_tag
begin_node:
mov xMEMNODE, #0
add xDTP, xDTP, #4
/*
* Advance xDTP past NULL terminated string
*/
0: ldrb w4, [xDTP], #1
cbnz w4, 0b
next_tag:
/*
* Align the DT pointer xDTP to the next 32-bit boundary
*/
add xDTP, xDTP, #3
and xDTP, xDTP, #~3
/*
* Read the next tag, could be BEGIN_NODE, END_NODE, PROP, END
*/
ldr w5, [xDTP]
rev w5, w5
cmp w5, #FDT_BEGIN_NODE
beq begin_node
cmp w5, #FDT_END_NODE
beq end_node
cmp w5, #FDT_PROP
beq prop_node
cmp w5, #FDT_END
beq err_end_of_fdt
b err_unexpected_tag
prop_node:
/*
* If propname == 'reg', record as membase and memsize
* If propname == 'device_type' and value == 'memory',
* set the 'is_memnode' flag for this node
*/
ldr w6, [xDTP, #4]
add xDTP, xDTP, #12
rev w6, w6
mov x5, xDTP
adr x4, _memory
bl strcmp
/*
* Get handle to property name
*/
ldr w5, [xDTP, #-4]
rev w5, w5
add x5, xSTRTAB, x5
cbz w7, check_device_type
/*
* Check for 'reg' property
*/
adr x4, _reg
bl strcmp
cbnz w7, inc_and_next_tag
/*
* Extract two 64-bit quantities from the 'reg' property. These values
* will only be used if the node also turns out to have a device_type
* property with a value of 'memory'.
*
* NOTE: xDTP is only guaranteed to be 32 bit aligned, and we are most
* likely executing with the MMU off, so we cannot use 64 bit
* wide accesses here.
*/
ldp w4, w5, [xDTP]
orr xMEMBASE, x4, x5, lsl #32
ldp w4, w5, [xDTP, #8]
orr xMEMSIZE, x4, x5, lsl #32
rev xMEMBASE, xMEMBASE
rev xMEMSIZE, xMEMSIZE
orr xMEMNODE, xMEMNODE, #HAVE_REG
b inc_and_next_tag
check_device_type:
/*
* Check whether the current property's name is 'device_type'
*/
adr x4, _device_type
bl strcmp
cbnz w7, inc_and_next_tag
orr xMEMNODE, xMEMNODE, #HAVE_DEVICE_TYPE
inc_and_next_tag:
add xDTP, xDTP, x6
b next_tag
end_node:
/*
* Check for device_type = memory and reg = xxxx
* If we have both, we are done
*/
add xDTP, xDTP, #4
cmp xMEMNODE, #(HAVE_REG | HAVE_DEVICE_TYPE)
bne next_tag
ret xLR
err_invalid_magic:
err_unexpected_begin_tag:
err_unexpected_tag:
err_end_of_fdt:
wfi