Thursday, 10 January 2013

Setup JTAG ICE for Linux kernel debugging

1) Setup PEEDI JTAG ICE for MPC8315 for Linux kernel debugging

The information of PEEDI JTAG ICE is available at:
http://www.ronetix.at/peedi.html

The steps to be performed are:
  • Download insight 6.8-1 to linux host
  • Build insight on linux host
configure  –target=powerpc-linux  –enable-sim-powerpc  –enable-sim-hostendian=little  --prefix=/home/vic/insight
  • In mpc8315.cfg
Set CORE0_BREAKMODE to hard
Comment out CORE0_INIT parameter
  • Download the cfg file from linux host to PEEDI
On PEEDI prompt, do the transfer from linux host to a SD card
tftp://192.168.xxx.xxx/mpc8315.cfg eep://mpc8315.cfg
  • In linux "make menuconfig" on linux host
Enable Kernel hacking->Compile the kernel with debug info
           Kernel hacking->Kernel debugging
  • Start “insight vmlinux” in linux build directory on linux host
In the insight window, do the following:
  • File ->Target Settings
  • Run->Connect to Target
  • Setup the breakpoint
(gdb) hb start_kernel  ; hardware breakpoint is used because the MMU is still not active


  •        set the program counter
(gdb) set $pc = 0x208000
  •  to run the linux kernel
(gdb) c

After this, you can step through the linux kernel.


A sample mpc8315.cfg
;--------------------------------------------------------------------------
;
; PEEDI target configuration file for PowerPC MPC8315 processor
;
; Ronetix
;
; Supported devices : MPC8315
;
; Board : MPC8315 board
;
; Revision : 1.0
;
; The file is delivered "AS IS" without warranty or condition of any
; kind, either express, implied or statutory. This includes without
; limitation any warranty or condition with respect to merchantability or
; fitness for any particular purpose, or against the infringements of
; intellectual property rights of others.
;
;--------------------------------------------------------------------------

[DEBUGGER]
PROTOCOL        = gdb_remote
REMOTE_PORT = 2000 ; TCP/IP port
DROP_ON_RESET = NO

[TARGET]
PLATFORM     = MPC8300 ; platform is Freescale MPC8300

[PLATFORM_MPC8300]
JTAG_CHAIN  = 8 ; list of IR lenght of all TAP controller in JTAG chain
JTAG_CLOCK = 20000 ; JTAG Clock in kHz - 10kHz jtag clock for init operations and 20MHz for normal work
JTAG_TDO_DELAY = 0
TRST_TYPE              = PUSHPULL ; type of TRST output: OPENDRAIN or PUSHPULL
RESET_TIME            = 500 ; lenght of RESET pulse in ms; 0 means no RESET
WAKEUP_TIME       = 500

CORE0 = MPC8313 ; TAP is PowerPC CPU
;CORE0_INIT = INIT_MPC8315
;CORE0_INIT = INIT_MPC8315_LINUX
CORE0_STARTUP_MODE = reset
CORE0_BOOT_ADDR = 0xFFF00100

; Reset Configuration words: Hi, Lo registers
; The bit order differs from the Freescale's User Manual:
; arg1 bit31 - PCIHOST
; arg1 bit30 - PCI64
; arg1 bit29 - PCIARB
; ....................
; arg2 bit31 - LBCM
; arg2 bit30 - DDRCM
; ..................
;
; If you want to set RCWHR bit-0 (PCIHOST) and RCWLR bit-1 (DDRCM) regarding
; the Freescale's User Manual, you should use: CORE0_RCW = 0x80000000, 0x40000000
;
CORE0_RCW = 0xA4146C00, 0x42060000 ;override reset configuration words
;CORE0_MMU_PTBASE = 0x000000F0
CORE0_BREAKMODE = hard ; breakpoint mode:
;CORE0_BREAKMODE = soft ; breakpoint mode:
CORE0_ENDIAN = big ; core is little endian
CORE0_FLASH0 = FLASH_NAND
CORE0_WORKSPACE = 0, 0x11000 ; workspace for flash programmer

; Default path to be used if only a file name (without the full path) is
; provided to a PEEDI command or for the FILE parameter in the Flash sections
; Examples:
; In a console:
; "flash prog tftp://192.168.3.1/image.elf"
; is equal to
; "flash prog image.elf"
;
; In a Flash Profile:
; FILE="tftp://192.168.3.1/image.bin", BIN, 0
; is equal to
; FILE="image.bin", BIN, 0
;
CORE0_PATH = "tftp://192.168.3.1"
;CORE0_PATH = "card://"
CORE0_FILE = "myfile.bin", BIN, 0x2000000    ; default file

[INIT_MPC8315_LINUX]
break add hard 0xC01AE4C8 ; hardware breakpoint at start_kernel()
go ; start U-boot which eventually will start Linux
wait 30000 stop ; wait until the kernel halts on the break
beep 3000 100 ; beep to signal we are ready for debug

[INIT_MPC8315]

set SPR 311 0xFF400000

; change internal MMR base from 0xff400000 (reset value) to 0xe0000000
mem write 0xff400000 0xe0000000 ; IMMRBAR = 0xe0000000

;setMMRBaseAddr 0xe0000000
set SPR 311 0xe0000000

mem write 0xe0000204 0xffff0000 ;SWCRR: disable watchdog
mem write 0xe0000110 0x00400000 ;SPCR : enable timebase unit
mem write 0xe00050d4 0x00010002 ;LCRR : CLKDIV = 4

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; System Configuration - Local Access Windows
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; Local Bus Local Access Windows
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; WINDOW 0 - NAND Flash
mem write 0xe0000020 0xf8000000 ; LBLAWBAR1 - begining at 0xf8000000
mem write 0xe0000024 0x80000018 ; LBLAWAR1 - enable, size = 32MB

;; DDR Local Access Windows
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; WINDOW 0 - 1st DDR SODIMM
mem write 0xe00000a0 0x00000000 ; DDRLAWBAR0 - begining at 0x00000000
mem write 0xe00000a4 0x8000001a ; DDRLAWAR0 - enable, size = 128MB

mem write 0xe00000e0 0x08000000 ; DDRLAWBAR0 - begining at 0x08000000
mem write 0xe00000e4 0x8000001a ; DDRLAWAR0 - enable, size = 128MB

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; ; DDR Controllers Configuration
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;DDRCDR
mem write 0xE0000128 0x73000002

;;*********************************
;; DDR2 Controller Registers
;;*********************************

;; DDR_SDRAM_CLK_CNTL
; CLK_ADJST = b'010' ; 2 Clocks
mem write 0xE0002130 0x02000000

; CS0_BNDS
; SA0 = b'000000000000'
; EA0 = b'000000000111'

mem write 0xE0002000 0x00000007 ;; 128MB

; CS0_CONFIG
; CS_0_EN = b'1'
; AP_0_EN = b'1'
; ODT_RD_CFG = b'0'
; ODT_WR_CFG = b'1'
; BA_BITS_CS_0 = b'00'
; ROW_BITS_CS_0 = b'001' ; 13 row bits
; COL_BITS_CS_0 = b'010' ; 10 columns bits

mem write 0xE0002080 0x80840102

; TIMING_CFG_3
; EXT_REFREC = b'000' ; 0 Clocks

mem write 0xE0002100 0x00000000

; TIMING_CONFIG_1

; bit 1-3 = 2 - PRETOACT precharge activate interval 2 clock cycles
; bit 4-7 = 6 - ACTTOPRE activate to precharge interval 6 clock cycles
; bit 9-11 = 2 = ACTTORW activate to r/w interval 2 clock cycles
; bit 13 - 15 = 5 - CASLAT CAS latency 3 clock cycles
; bit 16 - 19 = 6 - REFREC refresh recovery time 14 clock cycles
; bit 21 - 23 = 2 - WRREC data to precharge interval 2 clock cycles
; bit 25 - 27 = 2 - ACTTOACT activate to activate interval 2 clock cycles
; bit 29 - 31 = 2 - WRTORD write data to read command interval 2 clock cycles

mem write 0xe0002108 0x26256222

; TIMING_CONFIG_2
; bit 19-21 = b010 - WR_DATA_DELAY - 1/2 DRAM clock delay

mem write 0xe000210C 0x0f9028c7

; TIMING_CFG_0
; RWT = b'00' ; 0 Clocks
; WRT = b'00' ; 0 Clocks
; RRT = b'00' ; 0 Clocks
; WWT = b'00' ; 0 Clocks
; ACT_PD_EXIT = b'010' ; 2 Clocks
; PRE_PD_EXIT = b'010' ; 2 Clocks
; ODT_PD_EXIT = b'1000' ; 8 Clocks
; MRS_CYC = b'0010' ; 2 Clocks

mem write 0xE0002104 0x00220802

; DDR_SDRAM_CFG
; MEM_EN = b'0'
; SREN = b'1'
; RD_EN = b'0'
; SDRAM_TYPE = b'011'
; DYN_PWR = b'0'
; 32_BE = b'1'
; 8_BE = b'0'
; NCAP = b'0'
; 2T_EN = b'0'
; x32_EN = b'0'
; PCHB8 = b'0'
; HSE = b'0'
; MEM_HALT = b'0'
; BI = b'0'

mem write 0xE0002110 0x43080000

; DDR_SDRAM_CFG_2
; FRC_SR = b'0'
; DQS_CFG = b'00'
; ODT_CFG = b'10'
; NUM_PR = b'0001'
; D_INIT = b'0'

mem write 0xE0002114 0x00401000

; DDR_SDRAM_MODE
; Extended Mode Register: Outputs=0 or 1?
; Mode Register

mem write 0xE0002118 0x44400232

; DDR_SDRAM_MODE_2
; Extended Mode Register 2
; Extended Mode Register 3

mem write 0xE000211C 0x8000c000

; DDR_SDRAM_INTERVAL
; REFINT = 800 Clocks
; BSTOPRE = 100 Clocks

mem write 0xE0002124 0x03200064

; DDR_SDRAM_MD_CNTL (0xE0002120)
; DDR_DATA_INIT (0xE0002128)
; DDR_INIT_ADDRESS (0xE0002148)
; DDR_INIT_EXT_ADDRESS (0xE000214c)

; DDR_IP_REV1 - Read-Only

; md 0xE0002BF8
; DDR_IP_REV2 - Read-Only
; md 0xE0002BFC

;delay before enable
wait 300

;Enable: DDR_SDRAM_CFG
mem write 0xE0002110 0xc3080000

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Local Bus Interface (LBIU) Configuration
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;; CS0 - NAND FLASH
mem write 0xe0005000 0xf8000C21 ; BR1 base address at 0xF8000000, port size 8 bit, FCM, valid
mem write 0xe0005004 0xFFFF8076 ; OR1 32kB

;; LBCR - local bus enable
mem write 0xe00050d0 0x00000000

;; LCRR
;; bit 14 - 15 = 0b11 - EADC - 3 external address delay cycles
;; bit 28 - 31 = 0x0010 - CLKDIV - system clock:memory bus clock = 2
mem write 0xe00050d4 0x00030002

mem write 0xE0000800 0x00000000 ; ACR - Enable Core
;mem write 0xfa000000 0x00000000 ; write board LEDs

;; MRTPR - refresh timer prescaler
mem write 0xe0005084 0x20000000

[FLASH_NAND]
CHIP = NAND_FLASH
CPU = MPC83XX
CMD_BASE = 0xE0000000 ; IMMRBAR
DATA_BASE = 0xF8000000 ; FCM address
ADDR_BASE = 0 ; NAND chip select
OOB_INFO = FF
FILE = "test.bin", 0x0

[SERIAL] ; serial port configuration
BAUD = 115200
STOP_BITS = 1
PARITY = NONE
TCP_PORT = 0

[TELNET]
PROMPT = "mpc8315> " ; telnet prompt
;BACKSPACE = 127 ; comment out for autodetect

2) Setup ARM RealView JTAG ICE for ARM926EJ-S for Linux kernel debugging

Hardware target board has to provide the JTAG connection. The pull-up/pull-down resistors on one particular target board:

  • TDO, TDI, TMS pull-up
  • nTRST pull-up
  • RTCK n/c
  • TCK pull-down


In Linux, the ARM RealView JTAG ICE only supports TCP/IP mode, USB mode is not supported.

Firstly, we need to setup the ARM RealView ICE for TCP/IP mode. We need to run "RVI Config IP" to find the IP address of the ARM RealView. Then we need to run "RVConfig" to configure and save the configuration in a rvc file. Then we need to run the rvigdbconfig from the command line.

$ rvigdbconfig -f ~/.rvdebug/4.0/RVI_0.rvc

Secondly, run gdb from command line:

arm-none-linux-gnueabi-gdb vmlinux

(gdb) target remote 192.168.1.2:4000

the port number 4000 is the TCP port number in ARM Real View ICE

(gdb) info registers -- check the $pc value, it must be 0x0, if not, re-run the rvigdbconfig

(gdb) hb start_kernel

(gdb) x/10i $pc

(gdb) c

At UART terminal of U-Boot, load Image (kernel image) and initramfs.gz (rootfs), and run. Then, observe that the linux loading is stopped. So the Jtag debugging is successful.

Extra gdb commands:

(gdb) file vmlinux

(gdb) list start_kernel

(gdb) p/x $pc

(gdb) set $pc = 208000

(gdb) b *0x10

To do memory dump with gdb:

(gdb) dump binary memory <filename> <mem_start_addr> <mem_ending_addr>

Wednesday, 19 December 2012

Once upon a time in Iran


The travel route (2012 August):
Iran ایران‎ : Tehran (Iranian capital), Tabriz (biggest city in NW Iran), Jolfa (Iranian city neighboring Azerbaijan)  
Armenia Հայաստան : Goris, Yerevan (Armenian capital)
Georgia საქართველო : Tbilisi (Georgian capital), Mtskheta, Stepantsminda, Batumi, back to Tbilisi

The flight:
I took the Air Asia X flight from Kuala Lumpur to Tehran. In the airplane, i only came across 3 non-Iranians, or 3 person of non middle-eastern descent. I believed that 3 person were tourists, but did not get the chance to speak to them. An Iranian man was sitting next to me, he spoke in broken English, claiming he is a economics PhD student at a University around Malaysia. 
Queuing at the Iman Khomeini Airport
Iran

The Islamic Republic of Iran is a country in Western Asia.

Iman Khomeini Airport:
Upon arriving at Iman Khomeini Airport at Tehran, at the immigration booth, the Iranian official, who is a man with moustache, browsed through my passport. Then he said, "ah, Malaysian passport". He gave the passport to another guy, that guy led me to a room. A machine is on the table; in the dimly lit room, he took my thumb print, and he said:
"Welcome to Iran"

Tehran:
So i was in Tehran,  the biggest city in Iran with a population of 12 million people. 

I went to a grocery store to get some food. I don't know why the guy was happy.

In downtown Tehran, i took a picture in front of the Milad Tower. It is the tallest tower in Iran.

The next day, i tried to look for the old American Embassy in Tehran. I saw this sign "Down with USA".


I turned to the other corner, and saw the National Museum of Iran instead.

I kept on walking, and entered a park. Later, i found out it is called "Laleh Park". It is a peaceful and neatly decorated park. Without having much to do, i left the park.

I went to the Grand Bazaar of Tehran.

There were too many people in the grand bazaar. I took a car, and went to a gas station to pump gasoline. I had to stop the car as there were many cars waiting for their turns. In the picture above, this guy took his time to clean the car window.

The car moved on. A traffic police holding a stop sign was walking along the busy road.

A group of Iranian tourists asked me to take a picture for them.

In the evening, the sun was setting. I went back to the Tehran city center.

I went to the landmark of Tehran, the Azadi Tower برج آزادی. It was built in 1971 to commemorate the 2500 years of the Persian Empire. Azadi is Persian word, it means freedom.

 
At night, i went to a mosque, the Imāmzādeh Sāleh mosque. It is located at Tajrish Square in northern Tehran, at 1612 meters above see level.

The next day morning, i went to the Military Museum of Tehran.

At the bus station, it was quiet in the morning.

The bus to Tabriz was waiting.

The highway to Tabriz.

Tabriz:
Tabriz is one of the historical capitals of Iran and the capital of East Azerbaijan Province.
The population is around 1.4 million people.

After 6-7 hours, i arrived at Tabriz.

The Blue Mosque of Tabriz, it is a UNESCO world heritage. It was built in the 15th century.

The hostel i stayed for the night.

The next day, i took a cab and went out of the city. The pictures shows some gray-looking apartments in the desert.
The magic chimney of Kandovan village.

I was at the Kandovan village.

An overall view of the Kandovan village, the young girl in red was trying to communicate to me in Persian. There i wish i could speak Persian.

I took their picture again at Kandovan village.

Outside the Tabriz bazaar, another UNESCO world heritage site.

Jolfa:
Jolfa is located to the north of Tabriz. It borders the country of Azerbaijan.
At Jolfa, the border town, notice that the sign post is written in Persian and Russian languages.

At the immigration border to Azerbaijan, i did not go to Azerbaijan for this trip. For some reasons, Azerbaijan and Armenia have territory disputes and war. Their borders are closed. They are still technically at war.

The next day, i went to visit the Saint Stepanos church, it is an Armenian monastery built in the 9th century. It is part of the UNESCO world heritage, called the Armenian Monastic Ensemble in Iran.

inside the church, the main door is dated from 9th century.

outside the church, i still wore the same shirt from the 1st day.

the Aras river, separating Iran from Azerbaijan.

Now this is the Iran Armenian border, the mountain in the background belongs to Armenia. You can see the Iranian watch tower looking at it.

At the border terminal before going to Armenia.


Crossing the Iranian-Armenian border: The border is separated by Aras river, with a bridge built over it. Photo is prohibited at this border checkpoint. I took this pic somewhat. The picture shows the last Iranian guard house after exiting the immigration building, walking past the guard house, one Iranian soldier sitting on one side of the bridge. I kept walking past the soldier and crossing the bridge, an Armenian soldier asked to see my passport, then he said "welcome to Armenia". I walked further to the immigration compound of Armenia.







Saturday, 4 August 2012

Race Condition

A race condition is defined as the inter-dependency between a few processes. If one process finishes ahead of other processes, undesirable condition could occur. Race condition sometimes only happens in certain hardware combination.

Could race condition happen in the 100 meter sprint competition?

A case study of GPIB EOI Race Condition on LAN/GPIB converter. EOI stands for "end or identify".  EOI line has two purposes - The GPIB Talker uses the EOI line to mark the end of a message string, and the Controller uses the EOI line to tell devices to identify their response in a parallel poll. In our case, the GPIB instrument is the Talker.

PC -------- LAN/GPIB converter -------- GPIB instrument
                                     |
                                     |     <-------        +1
                                     |     <-------       EOI
                                     |
         (normal case, +1 will be forwarded to PC, error case, timeout will occur)   
                                     |
<--------    +1               |
                                     |     ------->      REN
                                     |

The converter is treated as a GPIB card in the PC. The PC test program call itimeout(...) which sets the timeout value, and this timeout value is passed to the converter. In our case, the timeout happens, no matter when timeout value we set.

In the converter firmware, read_buf() function:
       while (buf < bufend) {
          if (re_rdonly_intr(INT0_END)) {
              if (re_rdonly_intr(INT0_BI)) {
                   re_rddes_intr(INT0_BI);
                   buf_eoi = true;  ----> added to fix race condition
                   if (len!=1);
                            modify_cnt = true;
                }
         } else {
              re_rddes_intr(INT0_BI);
         }
      }

  notify_pressed()
     buf_sem.wakeup();    ------>  (1)

  read_buf()
     buf_sem.sleep();       ------->  (2)

(1) wakes up (2), (2) wakes up and check its status, not fulfilled, and (2) goes back to sleep again. and then (2) will timeout eventually.

In details, due to h/w response, when receiving short GPIB msg, INT0_BI is not cleared. Next time INT0_BI is already set, so buf_eoi is not set. It causes read_buf() to wait for a data byte that never arrives, and the semaphore will timeout.

The GPIB instrument is running on Pentium 800 embedded processor, it speeds up GPIB bus response, so the race condition appears.

In debugging race condition, log time should be displayed till milliseconds, and a circular log buffer should be used.

Thursday, 5 July 2012

Bluetooth HCI

Bluetooth HCI (Host Controller Interface)

The Bluetooth specification includes the definition of an interface (the Host Controller Interface) for the hardware of a Bluetooth module. It defines the interface between the HCI Driver (on Micro-controller) and the Host Controller firmware (on the Bluetooth module).

HCI Driver is also known as the HCI Host.

The physical link for transmitting HCI packets between an HCI Driver and Host Controller firmware, via a Host Controller Transport Layer, are:
  • USB (Universal Serial Bus).
  • RS232 (standard serial port), with error detection and recovery.
  • Generic UART, assuming no errors.
HCI Command Packetscommands are issued by the HCI Driver to the Host Controller:
  • Packet indicator (for UART interfaces) of 1.
  • Op-code (16 bits): identifies the command:
    OGF (Op-code Group Field, most significant 6 bits);
    OCF (Op-code Command Field, least significant 10 bits).
  • Parameter length (8-bit): total length of all parameters in bytes.
  • Command parameters: the number of parameters and their length is command specific.
HCI Data Packets for ACL data:
  • Packet indicator (for UART interfaces) of 2.
  • Control information (16 bits):
    Broadcast flag (most significant 2 bits):
    00 = point-to-point packet (no broadcast);
    01 = Active Slave Broadcast;
    10 = Parked Slave Broadcast.
    Packet boundary flag (2 bits):
    01 = continuing packet of a higher level message;
    10 = first packet of a higher level message.
    Connection handle (least significant 12 bits).
  • Data length (16 bits): total length of data in bytes.
  • Data.
HCI Data Packets for SCO data:
  • Packet indicator (for UART interfaces) of 3.
  • Connection handle (12 bits).
  • Unused (4 bits).
  • Data length (8 bits): total length of data in bytes.
  • Data.
HCI Event Packets: the Host Controller notifies the HCI Driver of events:
  • Packet indicator (for UART interfaces) of 4.
  • Event code (8 bits): identifies the event.
  • Parameter length (8-bit): total length of all parameters in bytes.
  • Event parameters: the number of parameters and their length is event specific.
Commands are processed asynchronously, so the completion of a command (and its return data) is reported by a Command Complete event. Commands may be processed in parallel, so a later command may complete before an earlier command.


HCI ERROR CODE DESCRIPTIONS

HCI error code descriptions can be implementation-dependent; whether the error should be returned using a Command Status event or the event associated with the issued command (following a Command Status event with Status=0x00). In these cases, the command can not start executing because of the error, and it is therefore recommended to use the Command Status event. The reason for this suggested course of action is that it is not possible to use the Command Status event in all software architectures.

The Bluetooth module firmware will handle the Link Management Protocol (LMP). If local sends LMP_au_rand and remote returns LM_not_accepted, the Bluetooth module will send a HCI event to inform the HCI Driver, with the error code of 0x05. See below:

AUTHENTICATION FAILURE (0x05)

The’Authentication Failure’ error code is returned by the Host Controller in the Status parameter in a Connection Complete event or Authentication Complete event when pairing or authentication fails due to incorrect results in the pairing/ authentication calculations (because of incorrect PIN code or link key).

"The 'Authentication Failure' error code can also be used as a value for the Reason parameter in the Disconnect command (as a reason code). The error code will then be sent over the air so that it is returned in the Reason parameter of a Disconnection Complete event on the remote side. In the Disconnection Complete event following a Command Status event (where Status=0x00) on the local side on which the Disconnect command has been issued, the Reason parameter will however contain the reason code 'Connection Terminated By Local Host'.

Example of  Authentication Failure in Connection Complete:

> HCI Event: Connect Request (0x04) plen 10
bdaddr 00:0D:44:2F:8C:C0 class 0x20040c type ACL
< HCI Command: Accept Connection Request (0x01|0x0009) plen 7
bdaddr 00:0D:44:2F:8C:C0 role 0x01
Role: Slave
> HCI Event: Command Status (0x0f) plen 4
Accept Connection Request (0x01|0x0009) status 0x00 ncmd 1
> HCI Event: Link Key Request (0x17) plen 6
bdaddr 00:0D:44:2F:8C:C0
< HCI Command: Link Key Request Reply (0x01|0x000b) plen 22
bdaddr 00:0D:44:2F:8C:C0 key C9295D90AF0463360C848B55881C2A11
> HCI Event: Command Complete (0x0e) plen 10
Link Key Request Reply (0x01|0x000b) ncmd 1
status 0x00 bdaddr 00:0D:44:2F:8C:C0
> HCI Event: Connect Complete (0x03) plen 11
status 0x05 handle 1 bdaddr 00:0D:44:2F:8C:C0 type ACL encrypt 0x00
Error: Authentication Failure


Wednesday, 4 July 2012

Bootloader and U-Boot

Bootloader:

Assuming you want to have a bootloader that can boot-up linux kernel.
A very basic bootloader would do a simple memory read/write test, initialise Uart, copy the kernel image from flash to ram, then perform a jump to the kernel starting address.

To expand the basic bootloader, we can add:
- wait for a few seconds for user interruption when booting
- add a loop to accept and process user input
- configure more on-board hardware resources, such as Ethernet controller, Sata controller, Usb controller

ROM Bootloader:

Certain micro-controller contains ROM Bootloader (RBL), which resides in the ROM of the micro-controller. The RBL supports booting from various memory devices (master mode), or from external master (slave mode). When using external master, the PC is used as the UART boot master via UART boot mode. The RBL supports limited number of NAND devices.

Upon reset or power-up, the micro-controller begins executing code from its RBL. The RBL read BOOTCFG register to determine the boot mode. 

Normally, a secondary boot loader, known as User Boot Loader (UBL) would be loaded by RBL. UBL could be used to initialise the memory controller, setting system clock, and download a tertiary bootloader, such as U-Boot.

In NAND boot mode, the RBL requires UBL to exist in the NAND memory, to load the tertiary boot loader from NAND to RAM. In NOR boot mode, the UBL is not required. The RBL locates the UBL image, via magic number in the UBL header. Then RBL copy UBL image to IRAM, then begin executing it.

The UBL will locate the tertiary bootloader image, load it from NAND to RAM, then jump to the entry point address.

U-Boot:

U-Boot is a popular open source bootloader. It has been ported to ARM, PPC, MIPS platform.


  • For PPC porting, modify files in:

cpu/mpc8xxx/
board/freescale/mpc8xxx/
lib_ppc/
include/asm-ppc

  • For ARM porting, modify files in:

cpu/arm926xxx/
board/mv88xxxxx/db88xxxxxx/
lib_arm/
include/asm-arm

important files:
common/main.c - contain main_loop() function
common/cmd_bootm.c - contain bootm command handling
post/tests.c - contain array of functions for post tests
include/configs/MPC8xxx.h - contain u-boot env settings, such as bootargs
include/configs/db88xxxxxx.h

Example: Building U-Boot:
make MPC8xxx_NAND_config    (for PPC)
make

make distclean
make db88xxxxxxxx_config  (for ARM)
make

U-Boot on ARM9 boot up sequence:

cpu/arm926xxx/start.S

  • reset
  • early DRAM init
  • relocate u-Boot to DRAM
  • stack_setup
  • clear_bss
  • call start_armboot


lib_arm/board.c

  • start_armboot()
  • (*init_fnc_ptr)() - looping thru init_sequence[] array
    • cpu_init
    • board_init
    • interrupt_init
    • serial_init
    • console_init_f
    • display_banner
    • dram_init
  • nand_init()
  • devices_init()
  • console_init_r()  - set stdin, stdout, stderr
  • for (;;) main_loop() - parsing user input

DRAM Initialisation:

Set Icache
Twsi Init
Dram Interface Detect
Read from On-board DRAM or DIMMS
Dram Interface Config
Get total size

DRAM Serial Presence Detect (SPD) in U-Boot:

For DRAM on DIMMS, we can read its EEPROM to extract the SPD data
1) Setup the twsi slave address, type, offset
2) Read data from the twsi
3) calculate checksum, compare checksum
4) analyze the SPD data, such as DIMM type, row and col addr, number of banks, data width, cycle time, refresh interval, burst length, CAS latency, DIMM bank density, memory size

Add new commmand:

common/cmd_xxx.c
include/commands.h
   #define U_BOOT_CMD

use U_BOOT_CMD macro to fill in cmd_tbl_t structure

U-Boot boot settings:

For example, a board with 64MB RAM, and booting from network, using ramdisk.

bootargs=root=/dev/ram0 rw initrd=0xc2000000,16M console=ttyS2,115200n8
ramboot=tftp 0xc2000000 rootfs.ext2.gz;tftp 0xc0700000 uImage;bootm 0xc0700000
bootcmd=tftp 0xc2000000 rootfs.ext2.gz;tftp 0xc0700000 uImage;bootm 0xc0700000




Tuesday, 3 July 2012

Linux Network Driver Interrupt Mitigation


Introduction:
NAPI ("New API") is a modification to the Linux driver packet processing framework, which is designed to improve the performance of high-speed networking. NAPI works through:
Interrupt mitigation

High-speed networking can create thousands of interrupts per second, all of which tell the system something it already knew: it has lots of packets to process. NAPI allows drivers to run with (some) interrupts disabled during times of high traffic, with a corresponding decrease in system load.

Packet throttling
When the system is overwhelmed and must drop packets, it's better if those packets are disposed of before much effort goes into processing them. NAPI-compliant drivers can often cause packets to be dropped in the network adaptor itself, before the kernel sees them at all.
NAPI was first incorporated in the 2.5/2.6 kernel. The use of NAPI is entirely optional.
Real World Example of NAPI network driver:
In driver init function:
  netif_napi_add(xxx, napi, napi_poll_func, weight)
In driver open function:
  setup the irq routine
  napi_enable(napi)
In irq routine:
  napi_schedule(napi)
In napi_poll_func:
  do the actual packet reception
  call netif_receive_skb(skb) to send packets to linux kernel for further processing. If the packet received is IP packet, it will be forwarded to linux's packet filtering framework.
  if (all packets are received)
    napi_complete(napi)

napi polling function will be called when there is sufficient packets being received on the network card. The network card must have enough RAM or DMA ring to store the packets.
  

Monday, 2 July 2012

A Tale of Two Linux Routers

By default, administrators just define a single, default router (which is eth0). So for a Linux system with two network interface cards —  eth0 and eth1, and if we receive traffic (i.e., ICMP pings) on eth1, the return traffic will go out on eth0.

In short, this post will explain how to ensure traffic going into eth1 goes out only on eth1, as well as enforce all traffic going into eth0 goes out only on eth0.
Assuming we have the following network setup:
  • eth0 - 10.10.1.10 netmask 255.255.255.0
  • eth0's gateway is: 10.10.1.254
  • eth1 - 192.168.7.7 netmask 255.255.255.0
  • eth1's gateway is: 192.168.7.1
First, we need to make sure the Linux kernel has support for “policy routing” enabled.
During the kernel compilation process, we need to:
cd /usr/src/linux
make menuconfig
Select "Networking --->"
Select "Networking options --->"
[*] TCP/IP networking
[*] IP: advanced router
Choose IP: FIB lookup algorithm (FIB_HASH)
[*] IP: policy routing
[*] IP: use netfilter MARK value as routing key
Next, we need to download, compile, and install the iproute2 utilities. (Most Linux distributions have binary packages for this utility.) Once installed, typing ip route show should show the system’s routing table. 
To check the system’s initial route configuration:
# netstat -anr
Kernel IP routing table
Destination Gateway Genmask Flags MSS Window irtt Iface
192.168.7.0  0.0.0.0  255.255.255.0 U 0 0 0 eth1
10.10.1.0  0.0.0.0  255.255.255.0 U 0 0 0 eth0
0.0.0.0  192.168.7.1  0.0.0.0 UG 0 0 0 eth1
So, basically, the system is using eth1 as the default route. If anyone pings 192.168.7.7, then the response packets will properly go out eth1 to the upstream gateway of 192.168.7.1. But what about pinging 10.10.1.10? The incoming ICMP packets will properly arrive on eth0but the outgoing response packets will be sent out via eth1! That is not good.
To fix this issue, we need to create a new policy routing table entry within the /etc/iproute2/rt_tables. We call it table #1, named “admin” (for routing administrative traffic onto eth0).
# echo "1 admin" >> /etc/iproute2/rt_tables
Then, we are going to add a few new entries within this “admin” table. Specifically, we provide information about eth0‘s local /24 subnet, along with eth0‘s default gateway.
ip route add 10.10.1.0/24 dev eth0 src 10.10.1.10 table admin
ip route add default via 10.10.1.254 dev eth0 table admin
At this point, we have created a new, isolated routing table named “admin” that is not used by the OS just yet. Because we still need to create a rule referencing how the OS should use this table. For starters, type ip rule show to see your current policy routing ruleset. Here’s what an empty ruleset looks like:
0: from all lookup local
32766: from all lookup main
32767: from all lookup default
Without going into all the boring details, each rule entry is evaluated in ascending order. The main gist is that the normal main routing table appears as entry 32766 in this list. (This would be the normal route table we see when we type netstat -anr.)
We are now going to create two new rule entries, that will be evaluated before the main rule entry.
ip rule add from 10.10.1.10/32 table admin
ip rule add to 10.10.1.10/32 table admin
Typing ip rule show now shows the following policy routing rulesets:
0: from all lookup local
32764: from all to 10.10.1.10 lookup admin
32765: from 10.10.1.10 lookup admin
32766: from all lookup main
32767: from all lookup default
Rule 32764 specifies that for all traffic going to eth0‘s IP, make sure to use the “admin” routing table, instead of the “main” one. Likewise, rule 32765 indicates that for all traffic originating from eth0‘s IP, make sure to use the “admin” routing table as well. For all other packets, use the “main” routing table. In order to commit these changes, it’s a good idea to type ip route flush cache.
So the system should now be able to properly route traffic to these two different default gateways. 
Update: Here are some additional resources, that I have found useful.
http://lartc.org/howto/lartc.rpdb.multiple-links.html
http://linux-ip.net/html/routing-tables.html

Friday, 22 June 2012

TCP Keep Alive

How to know if remote TCP device is down?

By default, TCP blocking socket will block when remote TCP device is down. We can use TCP KEEPALIVE to check:

setsockopt(SOL_SOCKET, SO_KEEPALIVE,...);

and set the TCP keep alive settings:

sysctl -w net.ipv4.tcp_keepalive_intvl = 10
sysctl -w net.ipv4.tcp_keepalive_time = 30
sysctl -w net.ipv4.tcp_keepalive_probes = 2

Then, the socket recv() function will return -1 when remote device is down.

Thursday, 10 May 2012

Shut it down

Graceful shutdown of Linux system to prevent hard disk ext2 file system corruption.

"/usr/local/bin/msp_int_handler" will call "init 0" to do graceful shutdown.

311 /************************************************************************
312
313 Previous
314 Implementation : Previously MSP was not generating the interrupt, and switching off
315                  the power modules when Power button is switched off.
316
317 Description    : Handling of the interrupt from MSP. When the power button is
318                  off, then MSP generates an interrupt to MPC for gracefully shutdown.
319                  The interrupt handler calls an userspace program called 'msp_int_handler'.
320                              
321            
322
323 ************************************************************************/
324
325 static irqreturn_t msp_interrupt_handler(int gpio_irq,void *ignored);
326
327 static irqreturn_t msp_interrupt_handler(int gpio_irq,void *ignored) {
328  
329     wake_up_interruptible(&msp_event);
330     msp_flag = 1;  
331
332     return 1;
333 }
334
335 static int msp_interrupt_thread(void *ptr) {
336
337   char *argv[] = {"/usr/local/bin/gpio_test", "2", NULL };
338   char *envp[] = {"HOME=/", "PATH=/sbin:/usr/sbin:/bin:/usr/local/bin:", NULL };
339   int ret;
340
341   wait_event_interruptible(msp_event, msp_flag == 1);
342
343   printk("<msp-interrupt> got interrupt from MSP\n");
344
345   ret = call_usermodehelper("/usr/local/bin/msp_int_handler", argv, envp, UMH_WAIT_EXEC);
346
347   if (ret) {
348      printk("<msp-interrupt> call_usermodehelper is not successful \n");
349   } else {
350      printk("<msp-interrupt> call_usermodehelper is successful \n");
351   }
352
353   return 0;
354 }
355
356 static int msp_interrupt_thread_init(void)
357 {
358        int ret=0;    
359        int virq;
360        #define SICRL_OFFSET 0x0114     /* SICR offset in IMMRBAR space */
361        __be32 __iomem *psicr = ioremap(get_immrbase() + SICRL_OFFSET, 4);    
362        u32 sicr;
363        #define IRQ5_INTERRUPT_NUMBER 21
364
365        sicr = in_be32(psicr);
366
367        printk(" Before SICRL Register value : 0x%x \n",sicr);
368
369        sicr &= 0xFE7FFFFF;   //clearing the bits 7 & 8 for IRQ5
370
371        out_be32(psicr, sicr);
372
373        sicr = in_be32(psicr);
374
375        printk(" After SICRL Register value : 0x%x \n",sicr);
376
377        // interrupt line from MSP to MPC for the power failure or power button press
378        virq = vinetic_map_irq(IRQ5_INTERRUPT_NUMBER);
379
380        if (request_irq(virq, &msp_interrupt_handler, 0, "MSP_IRQ", (void *)0))
381           printk(KERN_ERR "pmu: can't get irq %d" " (MSP to MPC)\n", virq);
382        else
383           printk("request_irq for IRQ5 is success with virtual IRQ  %d",virq);
384  
385        /* Wait Q initialization */
386        
387        init_waitqueue_head(&msp_event);
388        printk(" msp interrupt thread init \n\n");
389        msp_interrupt_task = kthread_run(msp_interrupt_thread, NULL, "msp_interrupt_thread");
390      
391        return ret;
392 }
393
394
395
396 static void __exit msp_interrupt_exit(void)
397 {
398        printk(" fact default module exit \n ");      
399 }
400
401 module_init(msp_interrupt_thread_init);
402 module_exit(msp_interrupt_exit);