conditional statements for linker command language LD - ld

Are there conditional statements for the GNU LD linker command language?
Context: I am developing firmware for an arm cortex m0+, which consists of a bootloader and an application. Both are compiled and flashed to target in separate projects, but I use a framework with symbolic links to the drivers, makefile and loader scripts so that I can reuse those for every app I make without copying these files for each app.
Currently I have two loader files, for bootloader and application (makefile automatically specifies the appropriate one), with memory assigment as follows:
bootloader
MEMORY {
flash (rx) : ORIGIN = 0x00000000, LENGTH = 16K
ram (rwx) : ORIGIN = 0x1FFFF000, LENGTH = 16K
}
app
MEMORY {
flash (rx) : ORIGIN = 0x00004000, LENGTH = 112K
ram (rwx) : ORIGIN = 0x1FFFF000, LENGTH = 16K
}
Like the makefile, I want to merge these to something like this (using C expressions to clarify)
MEMORY {
#ifdef(bootloaderSymbol)
flash (rx) : ORIGIN = 0x00000000, LENGTH = 16K
#else
flash (rx) : ORIGIN = 0x00004000, LENGTH = 112K
#endif
ram (rwx) : ORIGIN = 0x1FFFF000, LENGTH = 16K
}

Although its not its primary purpose, you can always run the C preprocessor
(cpp) on your linker scripts:
#if defined(MACHINE1)
# define TARGET_ADDRESS 0x80000000
# define SDRAM_START xxx
# define SDRAM_SIZE yyy
# define ROMFLAGS rx
#elif defined(MACHINE2)
# define TARGET_ADDRESS 0x40000000
# define SDRAM_START zzz
# define SDRAM_SIZE aaa
# define ROMFLAGS rwx
#else
# error unknown machine
#endif
MEMORY
{
rom (ROMFLAGS) : ORIGIN = TARGET_ADDRESS, LENGTH = 0x00100000
ram (WX) : ORIGIN = SDRAM_START + SDRAM_SIZE - 0x00200000, LENGTH = 0x00100000
driver_ram (WX) : ORIGIN = SDRAM_START + SDRAM_SIZE - 0x00100000, LENGTH = 0x00100000
}
...
You just need to make sure your macros don't collide with linker script syntax. Then save your linker script as xxx.lk.in (instead of xxx.lk) and add a recipe to your Makefile:
xxx.lk: xxx.lk.in
$(CPP) -P $(INCLUDE) -D$(MACHINE) $< $#
All that's left to do is to add xxx.lk as dependency to your final executables build recipe. I'm using similar processes on many of my projects successfully.

I think you can try "DEFINED(symbol)" according to https://sourceware.org/binutils/docs/ld/Builtin-Functions.html
Also, please don't forget to pass "--defsym=bootloaderSymbol=1" to ld.
MEMORY {
flash (rx) : ORIGIN = DEFINED(bootloaderSymbol) ? 0x00000000 : 0x00004000, LENGTH = DEFINED(bootloaderSymbol) ? 112K : 16K
ram (rwx) : ORIGIN = 0x1FFFF000, LENGTH = 16K
}

I've gone down this same path before, and later found out there's an ld command line argument to specify segment origin, which alleviates the need to figure it out in the linker script. From man page:
-Tbss=org
-Tdata=org
-Ttext=org
Same as --section-start, with ".bss", ".data" or ".text" as the section name.
So in your case, you would have -Ttext=0 (bootloader) or -Ttext=0x00004000 (app)

Related

How to force my section in the very end of flash?

I have my linker script with such memory layout
MEMORY{
ram : ORIGIN = 0x0, LENGTH = 0x1000 /* 4KB of RAM starting at address 0x0 */
flash : ORIGIN = 0x20000000, LENGTH = 0xC000 /* 48KB of flash starting at address 0x20000000 */
}
and I want to add two section in the very end of my flash(let's say .section1 and .section2)
but I don't know the length of my section unless I compile it
In my expectation, my flash layout will look like this:
start address
section
0x20000000
.text
0x20000000+.text size
.data
0x20000000+.text size + .data size
.mdata
0x20000000+.text size + .data size + .mdata
(empty flash)
0x2000C000-.section2 size - section1 size
.section1
0x2000C000-.section2 size
.section2
How should I do in my linker script?
I don't know how to wrote it for now I just force my address like this. but its not what I want.
.section1 :{
*(.section1)
} >flash :AT (0x2000A000)
.section2 :{
*(.section2)
} >flash :AT (0x2000B000)

getting illegal instructions when vectorized code writes to PCI

I am writing a program that writes to a device's range of HW registers. I am using mmap to map the HW addresses to virtual address (user space). I tested the result from the mmap and it is OK. I implemented a copy of a buffer into the device:
void bufferCopy(void *dest, void *src, const size_t size) {
uint8_t *pdest = static_cast<uint8_t *>(dest);
uint8_t *psrc = static_cast<uint8_t *>(src);
size_t iters = 0, tailBytes = 0;
/* iterate 64bit */
iters = (size / sizeof(uint64_t));
for (size_t index = 0; index < iters; ++index) {
*(reinterpret_cast<uint64_t *>(pdest)) =
*(reinterpret_cast<uint64_t *>(psrc));
pdest += sizeof(uint64_t);
psrc += sizeof(uint64_t);
}
.
.
.
but when running it on QEMU I get illegal instruction exception. When I debugged got it crashes on the next instruction (below is the asm of the main loop):
movdqu (%rsi,%rax,1),%xmm0
movups %xmm0,(%rdi,%rax,1) <----- this instruction crashes ...
add $0x10,%rax
cmp %rax,%r9
jne 0x7ffff7eca1e0 <_ZN12_GLOBAL__N_110bufferCopyEPvS0_m+64>
any ideas why ? my guess that you can write to PCI only 32/64 bit.
The compile doesn’t know my limitations, so it optimize my code and create vectorized loop (each iteration loads 128 bit and saves 128 bit). Is is making sense ?? can I write to PCI with vectorized instructions ?
Also, whether it is a missing feature in QEMU or a bug or just a recommendation, how can I prevent from the compiler to generate those vector instructions ?

How to change a mac address using the command line parameters in the linux kernel

I want to change a mac address at the u-boot level like the following.
# setenv bootargs 'console=ttyAMA0,115200n8 root=/dev/ram0 rw initrd=0x40000000 ethaddr=${ethaddr}'
# setenv ethaddr 11:22:33:44:55:66
# saveenv
And at the driver,
static unsigned char my_ethaddr[MAX_ADDR_LEN];
/* need to get the ether addr from armboot */
static int __init ethaddr_setup(char *line)
{
char *ep;
int i;
printk("command line : %s\n", line);
memset(my_ethaddr, 0, MAX_ADDR_LEN);
/* there should really be routines to do this stuff */
for (i = 0; i < 6; i++)
{
my_ethaddr[i] = line ? simple_strtoul(line, &ep, 16) : 0;
if (line)
line = (*ep) ? ep+1 : ep;
printk("mac[%d] = 0x%02Xn", i, my_ethaddr[i]);
}
return 0;
}
__setup("ethaddr=", ethaddr_setup);
When booting, the log message is like following.
[ 0.000000] Kernel command line: console=ttyAMA0,115200n8 root=/dev/ram0 rw initrd=0x40000000 ethaddr=${ethaddr}
[ 0.000000] command line : ${ethaddr}
[ 0.000000] mac[0] = 0x00, mac[1] = 0x00, mac[2] = 0x0E, mac[3] = 0x00, mac[4] = 0xDD, mac[5] = 0x00
The command line message is ${ethaddr}, is it right?
The mac address isn't correct.
How can I fix it?
You are using single quotes in:
setenv bootargs '... ethaddr=${ethaddr}'
so ${ethaddr} is not expanded and the bootargs variable contains the literal string ethaddr=${ethaddr}, which is then passed into the kernel and is what you see in your debug output. See the U-Boot documentation on How the Command Line Parsing Works for more details.
You could use double quotes, or no quotes at all, in which case ${ethaddr} would be expanded when assigning to bootargs, although you would need to set it first:
# setenv ethaddr 11:22:33:44:55:66
# setenv bootargs console=ttyAMA0,115200n8 root=/dev/ram0 rw initrd=0x40000000 ethaddr=${ethaddr}
# printenv bootargs
bootargs=console=ttyAMA0,115200n8 root=/dev/ram0 rw initrd=0x40000000 ethaddr=11:22:33:44:55:66
Note that in some systems the ethaddr variable is used by U-Boot itself to configure the MAC address of the first network device, and the Linux network driver may continue to use that address, so you don't need to explicitly pass it into the kernel. See the documentation on U-Boot Environment Variables.
In addition U-Boot may be configured to prevent modification of the ethaddr variable, although that's probably not the case here because when it is U-Boot prints the error message:
Can't overwrite "ethaddr"

Cortex-M3 heap-stack organization using keil

Trying to run blinky sample for Atmel sam3s and inspecting the stack pointer...
SP has the value 0x20000238 at the start of main function which is equal too Ram base + RW + ZI for this sample.
The base RAM address for this chip is : 0x20000000
Total ram size is: 0x10000
I expected the sp to be initialized on 0x20010000 and coming down.
Can anyone explain if I am wrong or not?
As Pait said (he/she should have answered the question so I could accept it, I think),
I was wrong to think the stack will be placed at the end of RAM by default. But I think it is wise to make it be placed there.
This is how I do it for my SAM3S micro, by changing the scatter file
LR_IROM1 0x00400000 0x00080000 { ; load region size_region
ER_IROM1 0x00400000 0x00080000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
}
RW_IRAM1 0x20000000 0x00010000 { ; RW data
.ANY (+RW +ZI)
}
RW_STACK 0x2000C000 UNINIT 0x4000 { ; STACK data
*.o (STACK)
}
}
RAM base = 0x20000000
Total RAM = 64 kb
Intended stack size = 16 kb

is Atom-32bit in mode protected after a reset?

I work on Atom-32bit-intel, I have to port MicroC OS II, so there is no code to make any configuration on the Atom (No GDT, no LDT...):
my question is more about the state of the Atom-32bit after a reset, is the Atom in protecte mode or not ? and the most important how do i check which mode is it (which registers have to be checked nad how)?
Remark:
The CR0.PE = 1 (I checked it), is that enough to prove that the Atom is in protected mode ?
************ UPDATE : *****************
/*Read the IDTR*/
sidt (idt_ptr)
/*Read the GDTR*/
sgdt (gdt_ptr)
So I tried just to use IDT's address to link my ISR to the IDT :
fill_interrupt(ISR_Nbr,(unsigned int) isr33, 0x08, 0x8E);
static void fill_interrupt(unsigned char num, unsigned int base, unsigned short sel, unsigned char flags)
{
unsigned short *Interrupt_Address;
/*address = idt_ptr.base + num * 8 byte*/
Interrupt_Address = (unsigned short *)(idt_ptr.base + num*8);
*(Interrupt_Address) = base&0xFFFF;
*(Interrupt_Address+1) = sel;
*(Interrupt_Address+1) = (flags>>8)&0xFF00;
*(Interrupt_Address+1) = (base>>16)&0xFFFF;
}
my ISR a imple one :
isr33:
nop
nop
cli
push %ebp //save the context to swith back
mov %esp,%ebp
pop %ebp //Return to the calling function
sti
ret
Chapter 9 of volume 3 of the Intel Software Developer's Manual says that the reset value of CR0 is 60000010H. As you can see, bit 0, aka PE, is clear.
Regardless, you can setup the descriptor tables in Protected Mode as well as in Real Mode. You just have to be more careful about it.
I suggest you check if the BIOS or OS are setting this bit at a stage before you read it.
Atom is x86 instruction set, and as such, should be starting in real mode for compatibility. I don't have one on hand to test with though.
Resolved, I use N450 Atom board, it has already a BIOS, the BIOS configures the board in Protected Mode.