EDK2 coding style requires to use 2 spaces instead of tabulation in source code files. Contributed-under: TianoCore Contribution Agreement 1.0 Signed-off-by: Olivier Martin <olivier.martin@arm.com> git-svn-id: https://svn.code.sf.net/p/edk2/code/trunk/edk2@17094 6f19259b-4bc3-4df7-8a09-765794883524
		
			
				
	
	
		
			238 lines
		
	
	
		
			5.5 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
			
		
		
	
	
			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
 |