text: #!/usr/bin/env python
  
  # fw_0x6109.py
  #
  # All firmware specific data such as address offsets are collected
  # in the fw.py file. Later versions of the framework will provide
  # multiple copies of this file in order to target different firmware
  # and chip versions.
  #
  # Copyright (c) 2018 Dennis Mantz. (MIT License)
  #
  # Permission is hereby granted, free of charge, to any person obtaining a copy of
  # this software and associated documentation files (the "Software"), to deal in
  # the Software without restriction, including without limitation the rights to
  # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
  # the Software, and to permit persons to whom the Software is furnished to do so,
  # subject to the following conditions:
  # - The above copyright notice and this permission notice shall be included in
  #   all copies or substantial portions of the Software.
  # - The Software is provided "as is", without warranty of any kind, express or
  #   implied, including but not limited to the warranties of merchantability,
  #   fitness for a particular purpose and noninfringement. In no event shall the
  #   authors or copyright holders be liable for any claim, damages or other
  #   liability, whether in an action of contract, tort or otherwise, arising from,
  #   out of or in connection with the Software or the use or other dealings in the
  #   Software.
  
  from __future__ import absolute_import
  from .fw import MemorySection, FirmwareDefinition
  
  
  class BCM4335C0(FirmwareDefinition):
      # Firmware Infos
      # This runs on Nexus 5, Xperia Z3, Samsung Galaxy Note 3
      FW_NAME = "BCM4335C0"
  
      # Device Infos
      DEVICE_NAME = 0x2178B4  # [type: 1byte] [len: 1byte] [name: len byte]
      BD_ADDR = 0x210C2C
  
      # Memory Sections
      #                          start,    end,      is_rom? is_ram?
      SECTIONS = [
          MemorySection(0x0, 0x90000, True, False),
          MemorySection(0xD0000, 0xD8000, False, True),
          # MemorySection(0xe0000,  0x1f0000, True , False),
          MemorySection(0x200000, 0x228000, False, True),
          MemorySection(0x260000, 0x268000, True, False),
          # MemorySection(0x280000, 0x2a0000, True , False),
          MemorySection(0x318000, 0x320000, False, False),
          MemorySection(0x324000, 0x360000, False, False),
          MemorySection(0x362000, 0x362100, False, False),
          MemorySection(0x363000, 0x363100, False, False),
          MemorySection(0x600000, 0x600800, False, False),
          MemorySection(0x640000, 0x640800, False, False),
          MemorySection(0x650000, 0x650800, False, False),
          # MemorySection(0x680000, 0x800000, False, False)
      ]
  
      # BLOC struct head which points to the first bloc struct (double-linked list)
      BLOC_HEAD = 0x203094
  
      # QUEU struct head which points to the first queue struct (double-linked list)
      QUEUE_HEAD = 0x20307C
      QUEUE_NAMES = [
          "tran_HCIEvent",
          "tran_ACLData",
          "tran_SCOData",
          "tran_UartBridgeNonHCIEvent",
          "tran_DiagData",
          "tran_HIDUsbKBEvt",
          "tran_HIDUsbMSEvt",
          "tran_HIDUsbMSCtrl",
          "tran_HIDUsbKBCtrl",
          "tran_HidAuxData",
          "lm_Cmd",
          "hci_HciCommand",
          "lm_deferredAction",
          "lrmmsm_cmd",
          "liteHostEvent",
          "litehostRcvdL2capData",
      ]
  
      # Connection Structure and Table
      CONNECTION_ARRAY_ADDRESS = 0x002038E8
      CONNECTION_MAX = 11
      CONNECTION_STRUCT_LENGTH = 0x14C
  
      # Patchram
      PATCHRAM_ENABLED_BITMAP_ADDRESS = 0x310204
      PATCHRAM_TARGET_TABLE_ADDRESS = 0x310000
      PATCHRAM_VALUE_TABLE_ADDRESS = 0xD0000
      PATCHRAM_NUMBER_OF_SLOTS = 128
      PATCHRAM_ALIGNED = (
          True  # use readMemAligned, not accessible via ReadRAM HCI command on Nexus 5
      )
  
      # Snippet for sendLcpPacket()
      SENDLCP_CODE_BASE_ADDRESS = 0x00211900
      SENDLCP_ASM_CODE = """
              push {r4,lr}
              
              // we want to call lmulp_sendLcp(conn_index, input, length)
              
              mov r0,  %d     // connection index, starts at 0
              ldr r1, =payload
              mov r2, %d      // length
              bl  0x66760     // lmulp_sendLcp
              
              pop {r4,pc}     // go back
      
              .align          // The payload (LMP packet) must be 4-byte aligend (memcpy needs aligned addresses)
              payload:        // Note: the payload will be appended here by the sendLmpPacket() function
              """
  
      # Snippet for sendLmpPacketLegacy()
      SENDLMP_CODE_BASE_ADDRESS = 0xD7500
      SENDLMP_ASM_CODE = """
              push {r4,lr}
      
              // malloc buffer for LMP packet
              bl 0x3F17E      // malloc_0x20_bloc_buffer_memzero
              mov r4, r0      // store buffer for LMP packet inside r4
      
              // fill buffer
              add r0, 0xC         // The actual LMP packet must start at offset 0xC in the buffer.
                                  // The first 12 bytes are (supposely?) unused and remain zero.
              ldr r1, =payload    // LMP packet is stored at the end of the snippet
              mov r2, 20          // Max. size of an LMP packet is 19 (I guess). The send_LMP_packet
                                  // function will use the LMP opcode to lookup the actual size and
                                  // use it for actually transmitting the correct number of bytes.
              bl  0x2e03c         // memcpy
      
              // load conn struct pointer (needed for determine if we are master or slave)
              mov r0, %d      // connection number is injected by sendLmpPacket()
              bl 0x42c04      // find connection struct from conn nr (r0 will hold pointer to conn struct)
      
              // set tid bit if we are the slave
              ldr r1, [r0, 0x1c]  // Load a bitmap from the connection struct into r1.
              lsr r1, 15          // The 'we are master'-bit is at position 15 of this bitmap
              and r1, 0x1         // isolate the bit to get the correct value for the TID bit
              ldr r2, [r4, 0xC]   // Load the LMP opcode into r2. Note: The opcode was already shifted
                                  // left by 1 bit (done by sendLmpPacket()). The TID bit goes into
                                  // the LSB (least significant bit) of this shifted opcode byte.
              orr r2, r1          // insert the TID bit into the byte
              str r2, [r4, 0xC]   // Store the byte back into the LMP packet buffer
      
      
              // send LMP packet
              mov r1, r4      // load the address of the LMP packet buffer into r1.
                              // r0 still contains the connection number.
              pop {r4,lr}     // restore r4 and the lr
              b 0xf81a        // branch to send_LMP_packet. send_LMP_packet will do the return for us.
      
              .align          // The payload (LMP packet) must be 4-byte aligend (memcpy needs aligned addresses)
              payload:        // Note: the payload will be appended here by the sendLmpPacket() function
              """
  
      # Snippet for fuzzLmp()
      FUZZLMP_HOOK_ADDRESS = 0x1E48C  # execute standard SendLmpPdu HCI to fill parameters
      FUZZLMP_CODE_BASE_ADDRESS = 0xD7500
      FUZZLMP_ASM_CODE = """
              // This hook is put into the end of bthci_cmd_vs_SendLmpPdu_1E45A,
              // so command parsing is still performed as normal. We jump in
              // before bthci_cmd_vs_SendLmpPdu pops and calls DHM_LMPTx.
      
              // put length argument into table_entry
              // payload[5] holds the size argument
              ldr   r7, =table_entry
              add   r7, #4         // length offset within table entry
              ldrb r6, [r4, #5]
              strb r6, [r7]        
      
              // we need to do the original pop...
              pop  {r4-r8, lr}
      
              // now we simply continue like the original DHM_LMPTx function
              push  {r4-r10, lr}   // code at 0xF81A
              mov   r8, r0
              movs  r4, r1
      
              // part of the check if hook_LMP_TxFilter_200D38 is installed
              ldr.w r9, =0x200D80
              sub.w r9, r9, #0x50
              ldr.w r2, [r9, #8]
      
              mov   r1, r4         // code at 0xF840
              mov   r0, r8
              bl    0x50050        // rm_getDHMAclPtr_50050
              cmp   r0, #1
              // skip check if we actually got a ptr 
              // get connection struct, code at 0xF850
              ldr   r0, =0x206eac  // table_for_bt_tx_structs_206EAC
              ldr.w r7, [r8]
              add.w r6, r0, r7, lsl #5
              ldrb  r0, [r6, #0x1f]
              cmp   r0, #1
              // skip check in connection struct
              ldrb  r0, [r4, #0xc] // ptr_to_opcode = buffer_cpy + 12;
              lsrs  r1, r0, #1     // buffer_cpy[12] >> 1
              add.w r0, r4, #0x0c
      
              // enable for debugging: ignore the remaining code and continue like normal opcode
              //b     0xF870
      
              // now we regularly would call the opcode conversion table function
              // however, we do not use lm_getLmpInfoType_3F2D8 but insert our own table here        
              ldr    r1, =table_entry // table_ptr with exactly one entry, so no offsets included here
              ldr    r0, =table_entry
      
              // branch back to DHM_LMPTx position after bl lm_getLmpInfoType
              b     0xF874
      
              .align
              table_entry:
                  .byte 0x35  //nullsub1+1
                  .byte 0xAC 
                  .byte 0x00
                  .byte 0x00
                  .byte 0x20  //length, will be overwritten by us anyways, but can not be longer than one buffer (0x20)  
                  .byte 0x00    
                  .byte 0x00
                  .byte 0x00
              """
  
      # Assembler snippet for the readMemAligned() function
      READ_MEM_ALIGNED_ASM_LOCATION = 0xD7900
      READ_MEM_ALIGNED_ASM_SNIPPET = """
              push {r4, lr}
      
              // malloc HCI event buffer
              mov  r0, 0xff    // event code is 0xff (vendor specific HCI Event)
              mov  r1, %d      // readMemAligned() injects the number of bytes it wants to read here
              add  r1, 6       // + type and length + 'READ'
              bl   0x7AFC      // malloc_hci_event_buffer (will automatically copy event code and length into the buffer)
              mov  r4, r0      // save pointer to the buffer in r4
      
              // append our custom header (the word 'READ') after the event code and event length field
              add  r0, 2            // write after the length field
              ldr  r1, =0x44414552  // 'READ'
              str  r1, [r0]
              add  r0, 4            // advance the pointer. r0 now points to the beginning of our read data
      
              // copy data to buffer
              ldr  r1, =0x%x  // readMemAligned() injects the read_address here. r1 will be used as src pointer in the loop
              mov  r2, %d     // readMemAligned() injects the number of dwords to read here. r2 will be the loop counter
          loop:
              ldr  r3, [r1]   // read 4 bytes from the read_address
              str  r3, [r0]   // store them inside the HCI buffer
              add  r0, 4      // advance the buffer pointer
              add  r1, 4      // advance the read_address
              subs r2, 1      // decrement the loop variable
              bne  loop       // branch if r2 is not zero yet
      
              // send HCI buffer to the host
              mov r0, r4      // r4 still points to the beginning of the HCI buffer
              bl  0x398c1     // send_hci_event_without_free()
      
              // free HCI buffer
              mov r0, r4
              bl  0x3FA36     // free_bloc_buffer_aligned
      
              pop {r4, pc}    // return
          """
  
      # Assembler snippet for tracepoints
      TRACEPOINT_BODY_ASM_LOCATION = 0xD7A00
      TRACEPOINT_HOOKS_LOCATION = 0xD7B00
      TRACEPOINT_HOOK_SIZE = 28
      TRACEPOINT_HOOK_ASM = """
              push {r0-r12, lr}       // save all registers on the stack (except sp and pc)
              ldr  r6, =0x%x          // addTracepoint() injects pc of original tracepoint here
              mov  r7, %d             // addTracepoint() injects the patchram slot of the hook patch
              bl   0x%x               // addTracepoint() injects TRACEPOINT_BODY_ASM_LOCATION here
              pop  {r0-r12, lr}       // restore registers
      
              // branch back to the original instruction
              b 0x%x                  // addTracepoint() injects the address of the tracepoint
      """
      TRACEPOINT_RAM_DUMP_PKT_COUNT = (
          670  # <ramsize> / <packetsize>   where packetsize is 244
      )
      TRACEPOINT_BODY_ASM_SNIPPET = (
          """
              mov   r8, lr     // save link register in r8
      
              // save status register in r5
              mrs  r5, cpsr
      
              // malloc HCI event buffer
              mov  r0, 0xff    // event code is 0xff (vendor specific HCI Event)
              mov  r1, 76      // buffer size: size of registers (68 bytes) + type and length + 'TRACE_'
              bl   0x7AFC      // malloc_hci_event_buffer (will automatically copy event code and length into the buffer)
              mov  r4, r0      // save pointer to the buffer in r4
      
              // append our custom header (the word 'TRACE_') after the event code and event length field
              add  r0, 2            // write after the length field
              ldr  r1, =0x43415254  // 'TRAC'
              str  r1, [r0]
              add  r0, 4            // advance the pointer.
              ldr  r1, =0x5f45      // 'E_'
              strh r1, [r0]
              add  r0, 2            // advance the pointer. r0 now points to the start of the register values
      
              // store pc
              str  r6, [r0]    // r6 still contains the address of the original pc
              add  r0, 4       // advance the pointer.
      
              // store sp
              mov  r1, 56      // 14 saved registers * 4
              add  r1, sp
              str  r1, [r0]
              add  r0, 4       // advance the pointer.
      
              // store status register
              str  r5, [r0]
              add  r0, 4       // advance the pointer.
      
              // store other registers
              mov  r1, sp
              mov  r2, 56
              bl   0x2e03c+1   // memcpy(dst, src, len)
      
              // send HCI buffer to the host
              mov  r0, r4      // r4 still points to the beginning of the HCI buffer
              bl   0x398c1     // send_hci_event_without_free()
      
              // free HCI buffer
              mov  r0, r4
              bl   0x3FA36     // free_bloc_buffer_aligned
      
              mov  r0, r7      // r7 still contains the patchram slot number
              bl   0x311AA     // disable_patchram_slot(slot)
      
              // restore status register
              msr  cpsr_f, r5
      
              // dump ram
              bl   dump_ram
      
              mov  lr, r8      // restore lr from r8
              bx   lr          // return
      
      
      // function to dump the RAM as multiple HCI packets:
      dump_ram:
              push {r4-r6,lr}
      
              // malloc HCI event buffer
              mov  r0, 0xff    // event code is 0xff (vendor specific HCI Event)
              mov  r1, 252     // buffer size
              bl   0x7AFC      // malloc_hci_event_buffer (will automatically copy event code and length into the buffer)
              mov  r4, r0      // save pointer to the buffer in r4
      
              // append our custom header (the word 'RAM___') after the event code and event length field
              add  r0, 2            // write after the length field
              ldr  r1, =0x5f4d4152  // 'RAM_'
              str  r1, [r0]
              add  r0, 4            // advance the pointer.
              ldr  r1, =0x5f5f      // '__'
              strh r1, [r0]
              add  r0, 2            // advance the pointer. r0 now points to the start of the actual payload
      
              mov  r5, 0x200000     // start of ram
              ldr  r6, =%d          // number of ramdump packets to be sent
      
              dump_ram_loop:
                  // Set r0 to point to the beginning of the payload in the hci buffer
                  mov  r0, r4
                  add  r0, 8
      
                  // store current address
                  str  r5, [r0]    // r5 contains the address in RAM which is send next
                  add  r0, 4       // advance the pointer.
      
                  // copy ram to hci buffer
                  mov  r1, r5
                  mov  r2, 244
                  bl   0x2e03c     // memcpy
      
                  // send HCI buffer to the host
                  mov  r0, r4      // r4 still points to the beginning of the HCI buffer
                  bl   0x398c1     // send_hci_event_without_free()
      
                  // delay loop; Workaround: without the delay, a lot of packets are not actually sent
                  // through HCI.
                  mov  r0, 0x1000
                  delay_loop:
                      subs r0, 1
                      bne delay_loop
      
                  // increment the RAM pointer; decrement the counter
                  add  r5, 244
                  subs r6, 1
      
                  bne  dump_ram_loop
      
              // free HCI buffer
              mov  r0, r4
              bl   0x3FA36     // free_bloc_buffer_aligned
      
              pop  {r4-r6,pc}
      """
          % TRACEPOINT_RAM_DUMP_PKT_COUNT
      )
  