Linux kernel arm entry point

Some Details on Arm Linux Boot

The bs=512 seek=2 specification should be from the NXP/Freescale reference manual for the SoC (e.g. the «Expansion Device: SD, eSD and SDXC» section of the System Boot chapter).

When configured to boot from an SDcard, the ROM boot program (of the SoC) will look for a program image (e.g. U-Boot) at byte offset 0x400 (or 2 * 512 = 1024), which is the third 512-byte sector.

The first sector is presumed to be the MBR, and the second sector is reserved for an optional Secondary Image Table (using terminology from NXP document).

Allwinner SoCs use a similar booting scheme for SDcard (i.e. the U-Boot image is at a fixed location in raw sectors not part of a partition), but the image starts at the 17th sector.

Instead of loading raw sectors, some SoCs (e.g. Atmel) boot from SDcard by loading a file from a FAT partition.

Please also explain what Load address/entry point address mean?

These values are specified to the mkimage utility so that they can be installed in the uImage header. U-Boot will then use these values when the uImage is loaded and unpacked.

The load address specifies to U-Boot the required memory address to locate the image. The image is copied to that memory address.

The entry point specifies to U-Boot the memory address to jump/branch to in order to execute the image. This value is typically the same address as the load address .

For an ARM Linux kernel the recommended load and entry-point addresses are 0x8000 from the start of physical memory, according to (Vincent Sanders’) Booting ARM Linux.

See Building kernel uImage using LOADADDR for more details.

What is the booting process for ARM?

Currently, there are two exception models in the ARM architecture (reset is considered a kind of exception):

The classic model, used in pre-Cortex chip and current Cortex-A/R chips. In it, the memory at 0 contains several exception handlers:

 Offset Handler 
===============
00 Reset
04 Undefined Instruction
08 Supervisor Call (SVC)
0C Prefetch Abort
10 Data Abort
14 (Reserved)
18 Interrupt (IRQ)
1C Fast Interrupt (FIQ)

When the exception happens, the processor just starts execution from a specific offset, so usually this table contains single-instruction branches to the complete handlers further in the code. A typical classic vector table looks like following:

00000000 LDR PC, =Reset
00000004 LDR PC, =Undef
00000008 LDR PC, =SVC
0000000C LDR PC, =PrefAbort
00000010 LDR PC, =DataAbort
00000014 NOP
00000018 LDR PC, =IRQ
0000001C LDR PC, =FIQ

At runtime, the vector table can be relocated to 0xFFFF0000, which is often implemented as a tightly-coupled memory range for the fastest exception handling. However, the power-on reset usually begins at 0x00000000 (but in some chips can be set to 0xFFFF0000 by a processor pin).

Читайте также:  Linux dentries and inodes

The new microcontroller model is used in the Cortex-M line of chips. There, the vector table at 0 is actually a table of vectors (pointers), not instructions. The first entry contains the start-up value for the SP register, the second is the reset vector. This allows writing the reset handler directly in C, since the processor sets up the stack. Again, the table can be relocated at runtime. The typical vector table for Cortex-M begins like this:

__Vectors DCD __initial_sp ; Top of Stack 
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
DCD MemManage_Handler ; MPU Fault Handler
DCD BusFault_Handler ; Bus Fault Handler
DCD UsageFault_Handler ; Usage Fault Handler
[. more vectors. ]

Note that in the modern complex chips such as OMAP3 or Apple’s A4 the first piece of code which is executed is usually not user code but the on-chip Boot ROM. It might check various conditions to determine where to load the user code from and whether to load it at all (e.g. it could require a valid digital signature). In such cases, the user code might have to conform to different start-up conventions.

How do I find ARM Linux entry point when it fails to uncompress?

We seem to have ported Das U-Boot successfully.

There’s evidence that that is a faulty assumption.

Just before calling the kernel, the pointer theKernel is 10008000 and not 10800000.

Which version of U-Boot are you using?

In both 2012.10 and 2013.04 versions of U-Boot, the variable theKernel is only declared and used by code for arches like AVR32 and MIPS.

There is no ARM code that should be using theKernel .

u-boot-2012.10$ find . -print | xargs grep theKernel
./arch/avr32/lib/bootm.c: void (*theKernel)(int magic, void *tagtable);
./arch/avr32/lib/bootm.c: theKernel = (void *)images->ep;
./arch/avr32/lib/bootm.c: theKernel, params_start);
./arch/avr32/lib/bootm.c: theKernel(ATAG_MAGIC, params_start);
./arch/microblaze/lib/bootm.c: void (*theKernel) (char *, ulong, ulong);
./arch/microblaze/lib/bootm.c: theKernel = (void (*)(char *, ulong, ulong))images->ep;
./arch/microblaze/lib/bootm.c: (ulong) theKernel, rd_data_start, (ulong) of_flat_tree);
./arch/microblaze/lib/bootm.c: theKernel (commandline, rd_data_start, (ulong) of_flat_tree);
./arch/mips/lib/bootm.c: void (*theKernel) (int, char **, char **, int *);
./arch/mips/lib/bootm.c: theKernel = (void (*)(int, char **, char **, int *))images->ep;
./arch/mips/lib/bootm.c: (ulong) theKernel);
./arch/mips/lib/bootm.c: theKernel(linux_argc, linux_argv, linux_env, 0);
./arch/mips/lib/bootm_qemu_mips.c: void (*theKernel) (int, char **, char **, int *);
./arch/mips/lib/bootm_qemu_mips.c: theKernel = (void (*)(int, char **, char **, int *))images->ep;
./arch/mips/lib/bootm_qemu_mips.c: (ulong) theKernel);
./arch/mips/lib/bootm_qemu_mips.c: theKernel(0, NULL, NULL, 0);
./arch/nds32/lib/bootm.c: void (*theKernel)(int zero, int arch, uint params);
./arch/nds32/lib/bootm.c: theKernel = (void (*)(int, int, uint))images->ep;
./arch/nds32/lib/bootm.c: (ulong)theKernel);
./arch/nds32/lib/bootm.c: theKernel(0, machid, bd->bi_boot_params);
u-boot-2012.10$


Please explain how you are able to trace a variable that should not be defined or assigned on an ARM processor.

The next output after U-Boot prints «Starting kernel . » should be «Uncompressing Linux. «.

For the Freescale arch this text output is dependent on the proper passing of the machine type number (aka arch_id ) by U-Boot to the kernel.

You need to verify that this machine type number is properly defined in U-Boot.

What does your configuration file for U-Boot look like?

I tried adding some register manipulation code to signal GPIOs in compressed/head.S with no response.

Did you sanity check this code to ensure that it works as you expect?

Читайте также:  Как завершить приложение linux

Did you try out the GPIO operations from the U-Boot command line?

My question is, how can I make sure U-Boot is calling the correct entry point?

For the ARM arch, it is a jump to the address specified in the bootm command.

Since the uImage load address and the bootm specify the same 0x10800000 address, that should be good (assuming that U-Boot is correctly configured and built for ARM).

Just before calling the kernel, the pointer theKernel is 10008000 and not 10800000. Does this mean U-boot is jumping at the wrong location?

YES.
If you check the source code (for AVR32 or MIPS), you’d find that theKernel is assigned from the image header, specifically the entry point value. U-Boot would then jump to this location.

But the real problem is that your ARM Cortex A9 should not be using this code or this variable.
It seems as if that U-Boot is not configured for the proper arch and/or the machine type may not be correctly defined.

CORRECTIONS:

As the OP pointed out, older versions of U-Boot did use the variable theKernel even for the ARM arch.

indicates that U-Boot has (successfully) copied the kernel image (without the image information header) from the bootm address of 0x10800000 (plus offset 0x40 for the header length) to the load address of 0x10008000. This memory move operation is performed by the procedure bootm_load_os() in common/cmd_bootm.c.

So the value of 0x10008000 that you reported is correct for theKernel .

There is no indication that U-Boot is jumping to the wrong location.

As already mentioned, you should verify that the machine type is correctly defined. The value would be used in __arch_decomp_setup() in arch/arm/plat-mxc/include/mach/uncompress.h so that text could be output during the decompression prior to the kernel booting.

memory location for linux kernel for u-boot

So what I’ve done is loaded a copy of linux compiled for ARM devices onto a sd-card .

Unlike x86, there is no one build of a Linux kernel that executes on all «ARM devices«.

Older ARM Linux kernels (i.e. pre-Device Tree) were configured and built specifically for each SoC/board variation.

Modern ARM Linux kernels can be built for one or more SoCs, and the kernel is booted with a Device Tree blob to describe the board in use.

You haven’t provided any details to indicate if the kernel you have is sufficient to boot your «ARM device«.

The problem now is what memory location do I tell U-boot to boot up the vmlinuz linux kernel?

ARM Linux should boot using a zImage file, not a vmlinuz file.

What is the start address of physical RAM on your device?

The convention is that the ARM Linux kernel will execute at the base of physical RAM plus an offset of 0x8000 (32K). See this answer for more details.

The zImage file is self-extracting, and can be loaded (by U-Boot from your SD card) to any suitable memory address above the kernel’s load/execute address (while leaving room for the DT blob and not clobber U-Boot).

Or pointing out some resources would be great as well.

Try Russel King’s Booting ARM Linux and Vincent Sanders’ Booting ARM Linux.

Читайте также:  Linux просмотр установленных сертификатов

Источник

kernel entry points on ARM

Inside each of these vector_* jump tables, there is exactly 1 DWORD with the address of a label with a _usr suffix.

This is correct. The table in indexed by the current mode. For instance, irq only has three entries; irq_usr , irq_svc , and irq_invalid . Irq’s should be disabled during data aborts, FIQ and other modes. Linux will always transfer to svc mode after this brief ‘vector stub’ code. It is accomplished with,

@ @ Prepare for SVC32 mode. IRQs remain disabled. @ mrs r0, cpsr eor r0, r0, #(\mode ^ SVC_MODE | PSR_ISETSTATE) msr spsr_cxsf, r0 @@@ . other unrelated code movs pc, lr @ branch to handler in SVC mode 

This is why irq_invalid is used for all other modes. Exceptions should never happen when this vector stub code is executing.

Does this mean that labels with the _usr suffix are executed, only if the interrupt arises when the kernel thread executing on that CPU is in userspace context? For instance, irq_usr is executed if the interrupt occurs when the kernel thread is in userspace context, dabt_usr is executed if the interrupt occurs when the kernel thread is in userspace context, and so on.

Yes, the spsr is the interrupted mode and the table indexes by these mode bits.

If 1 is true, then which kernel threads are responsible for handling, say irqs, with a different suffix such as irq_svc. I am assuming that this is the handler for an interrupt request that happens in SVC mode. If so, which kernel thread handles this? The kernel thread currently in SVC mode, on whichever CPU receives the interrupt?

I think you have some misunderstanding here. There is a ‘kernel thread’ for user space processes. The irq_usr is responsible for storing the user mode registers as a reschedule might take place. The context is different for irq_svc as a kernel stack was in use and it is the same one the IRQ code will use. What happens when a user task calls read() ? It uses a system call and code executes in a kernel context. Each process has both a user and svc/kernel stack (and thread info). A kernel thread is a process without any user space stack.

If 2 is true, then at what point does the kernel thread finish processing the second interrupt, and return to where it had left off(also in SVC mode)? Is it ret_from_intr ?

Generally Linux returns to the kernel thread that was interrupted so it can finish it’s work. However, there is a configuration option for pre-empting svc threads/contexts. If the interrupt resulted in a reschedule event, then a process/context switch may result if CONFIG_PREEMPT is active. See svc_preempt for this code.

Источник

Оцените статью
Adblock
detector