diff options
Diffstat (limited to 'ArmVirtPkg/Library/ArmXenRelocatablePlatformLib/AARCH64/MemnodeParser.S')
-rw-r--r-- | ArmVirtPkg/Library/ArmXenRelocatablePlatformLib/AARCH64/MemnodeParser.S | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/ArmVirtPkg/Library/ArmXenRelocatablePlatformLib/AARCH64/MemnodeParser.S b/ArmVirtPkg/Library/ArmXenRelocatablePlatformLib/AARCH64/MemnodeParser.S new file mode 100644 index 0000000000..2bdaa3c951 --- /dev/null +++ b/ArmVirtPkg/Library/ArmXenRelocatablePlatformLib/AARCH64/MemnodeParser.S @@ -0,0 +1,237 @@ +/*
+ * 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
|