I tried to read signature row of atmega32 with boot_signature_byte_get(0); but i get this error : 'SIGRD' undeclared (first use in this function) and it seems that it's because of we only can read the signature with the AVRs that have SIGRD bit in SPMCR (i suppose!).
Is this right? and so then how can i read signature row another way?
Back in 2007, there was no function in any header to read the signature byte, then it was introduced in the same year. But as it seems, it still has some problems with that.
The datasheet says:
All Atmel microcontrollers have a three-byte signature code which identifies the device.
This code can be read in both serial and parallel mode, also when the device is locked.
The three bytes reside in a separate address space.
Meaning that the ATMEGA32 has read-access to this byte. Also in the datasheet, it specifies how to read this byte. And in most of their MCUs, it's read in the same way, but for some reason, the SIGRD definition number is missing in some header files, including the ATMEGA32 one.
But, as a workaround, we can define SIGRD manually. We just have to known its value. When I do some findstr (or grep) in the header files searching this definition, unanimously, its value is 5.
So, the workaround would be:
#define SIGRD 5
#include <avr/boot.h>
I compiled this successfully, but I've just tried to test the program on a simulation software, as I don't have the ATMEGA32 with me right now. It returns a byte, so now it's up to you to known if this is the correct byte...
Regarding the SPMCR thing you said, it seems that the signature row is in another address space (by my understanding, I don't know if this is right I can't confirm), and the way to get it, is using some instructions, similarly when we upload some program to the MCU.
I'd like to enhance a bit and illustrate Flávio's answer as I faced the same problem tonight.
so then how can i read signature row another way?
Flávio's workaround does the job well:
#define SIGRD 5
#include <avr/boot.h>
void read_signature_row(uint8_t sr[3])
{
sr[0] = boot_signature_byte_get(0x0000);
sr[1] = boot_signature_byte_get(0x0002);
sr[2] = boot_signature_byte_get(0x0004);
}
void setup()
{
Serial.begin(9600);
delay(100);
uint8_t sr[3] = {0xcc, 0xcc, 0xcc};
read_signature_row(sr);
char buff[100];
snprintf(buff, sizeof buff, "Device signature: %02X %02X %02X", sr[0], sr[1], sr[2]);
Serial.println(buff);
}
void loop()
{}
I don't have ATmega32, although this method retrieves correct signatures for my ATmega328P (1E 95 0F) and ATmega168PA (1E 94 0B).
it seems that it's because of we only can read the signature with the AVRs that have SIGRD bit in SPMCR (i suppose!).
Is this right?
No, it actually sets SIGRD to SPMCR prior to call LPM as seen at avr/boot.h:
#define __BOOT_SIGROW_READ (_BV(__SPM_ENABLE) | _BV(SIGRD))
#define boot_signature_byte_get(addr) \
(__extension__({ \
uint8_t __result; \
__asm__ __volatile__ \
( \
"sts %1, %2\n\t" \
"lpm %0, Z" "\n\t" \
: "=r" (__result) \
: "i" (_SFR_MEM_ADDR(__SPM_REG)), \
"r" ((uint8_t)(__BOOT_SIGROW_READ)), \
"z" ((uint16_t)(addr)) \
); \
__result; \
}))
I believe that the atmega32 does not support reading the signature bytes within the user program.
The atmega32 Datasheet reveales that this device has no bit called SIGRD in its register SPMCR, the fifth bit of this register is reserved and marked as read only.
The atmega640/1280/2560 processors have a similar register called spmcSr that has this bit SIGRD (Bit 5). But the datasheet explains that this Bit should not be used since this operation is reserved for future use (Nonetheless, the procedure for reading the signature is explained there).
Your compiler works correctly since the atmega32 does not have this SIGRD bit.
The SIGRD exists and can be used in the ATmega164A/164PA/324A/324PA/644A/644PA/1284/1284P processors. The "reserved for future use" remark only applies to SPM instruction for anything else than the five lowest bits of SPMCSR, see page 285, section 26.9.1.
Related
I am working on a small program to learn how to mix C and Assembly in Atmel Studio GCC. Basically I am writing a C program to initiate the stack pointer in assembly. When I build it I keep getting an error "Operand out of range". I've initiated stack pointers many times in programs but can't get it to work in the ".s" file of this program. I've gotten the program to work with different register in the ".s" file so I don't think it's the connection between the two files. Any help would be appreciated.
Main.c:
#define F_CPU 16000000
#include <avr/io.h>
#include <util/delay.h>
extern void setStackPointer(void);
int main(void)
{
DDRB |= 0x20;
setStackPointer();
while (1)
{
PORTB = 0x20;
_delay_ms(500);
PORTB = 0x00;
_delay_ms(500);
}
}
Assembler1.s:
#define _SFR_ASM_COMPAT 1
#define _SFR_OFFSET 0
#include <avr/io.h>
.global setStackPointer
setStackPointer:
ldi r18, lo8(RAMEND-0x20)
out SPL, R18
ldi R18, hi8(RAMEND-0x20)
out SPH, R18
ret
There are several issues here.
First, the comment by Sir Jo Black is right: there is no reason to
subtract 0x20 from RAMEND. I mean, unless you want to set apart 32
bytes at the end of the RAM...
Second, there is no point in setting the stack pointer yourself. On most
recent AVRs, including the ATmega328P, SP is automatically initialized
by the hardware with RAMEND. C.f. the datasheet. If that weren't
enough, it is initialized again by the C runtime, which gets normally
linked into your program (even a 100% assembly program) if you compile
it with gcc.
Third, from the avr-libc documentation:
For more backwards compatibility, insert the following at the start of
your old assembler source file:
#define __SFR_OFFSET 0
This automatically subtracts 0x20 from I/O space addresses, but it's a
hack, so it is recommended to change your source: wrap such addresses
in macros defined here, as shown below. After this is done, the
__SFR_OFFSET definition is no longer necessary and can be removed.
The recommended way to write that code is then:
setStackPointer:
ldi r18, lo8(RAMEND)
out _SFR_IO_ADDR(SPL), r18
ldi r18, hi8(RAMEND)
out _SFR_IO_ADDR(SPH), r18
ret
If you really want to use the old hack, write
#define __SFR_OFFSET 0
at the beginning of your program. And pay attention to the double
underscore at the beginning of the macro name.
I want to branch to a particular address(NOT a label) using ARM assembly, without modifying the LR register. So I go with B instead of BL or BX.
I want this to be done in GCC inline asm.
Here is the documentation, and here is what I 've tried:
#define JMP(addr) \
__asm__("b %0" \
: /*output*/ \
: /*input*/ \
"r" (addr) \
);
It is a C macro, that can be called with an address. When I run it I get the following error:
error: undefined reference to 'r3'
The error is because of the usage of "r". I looked into it a bit, and I've found that it could be a bug on gcc 4.9.* version.
BTW, I am using Android/Linux Gcc 4.9 cross compiler, on an OSX.
Also, I don't know wether I should have loaded something on Rm.
Cheers!
Edit:
I changed the macro to this, and I still get undefined reference to r3 and r4:
#define JMP(addr) \
__asm__("LDR r5,=%0\n\t" \
"LDR r4,[r5]\n\t"\
"ADD r4,#1\n\t" \
"B r4" \
: /*output*/ \
: /*input*/ \
"r" (addr) \
: /*clobbered*/ \
"r4" ,"r5" \
);
Explanation:
load the address of the variable to r5, then load the value of that address to r4. Then add 1 to LSB (emm required by ARM specification?). And finally Branch to that address.
Since you are programming in C, you could just use a plain C approach without any assembly at all: just cast the variable, that holds the pointer to address to which you want to jump, to a function pointer and call it right away:
((void (*)(void)) addr)();
just an explanation to this jungle of brackets:
with this code you are casting addr to a pointer (signified by the star (*)) to a function that takes no argument (the second void means there are no arguments) and that also returns nothing (first void). finally the last two brackets are the actual invocation of that function.
Google for "C function pointer" for more information about that approach.
But if that doesn't work for you and you still want to go with the assembly approach, the instruction that you are in looking for is in fact BX (not sure why you excluded that initially. but I can guess that the name "Branch and Exchange" mislead you to believe that the register argument is swapped (and thereby changed) with the program counter, which is NOT the case, but it confused me in the beginning, too).
For that just a simple recap of the instructions:
B would take a label as an argument. Actually the jump will be encoded as an offset from the current position, which tells the processor to jump that many instruction forwards or backwards (normally the compiler, assembler or linker will take care of calculating that offset for you). During execution, control flow will simply be transferred to that position without changing any register (this means also the link register LR will stay unchanged)
BX R0 will take the absolute (so not an offset) address from a register, in this case R0, and continue execution at that address. This is also done without changing any other register.
BL and BLX R0 are the corresponding counterparts to the previous two instruction. They will do the same thing control flow wise, but on top of that save the current program counter in the link register LR. This is needed if the called function is supposed to return later on.
so in essence, what you would need to do is:
asm("BX %0" : : "r"(addr));
instructing the compiler to make sure the variable addr is in a register (r), which you are promising to only read and not to change. on top of that, upon return you won't have changed (clobbered) any other register.
See here
https://gcc.gnu.org/onlinedocs/gcc/Constraints.html
for more information about inline assembly constraints.
To help you understand why there are also other solutions floating around, here some things about the ARM architecture:
the program counter PC is for many instruction accessible as a regular register R15. It's just an alias for that exact register number.
this means that almost all arithmetic and register altering instructions can take it as an argument. However, for many of them it is highly deprecated.
if you are looking at the disassembly of a program compiled to ARM code, any function will end with one of three things:
BX LR which does exactly what you want to do: take the content of the link register (LR is an alias for R14) and jump to that location, effectively returning to the caller
POP {R4-R11, PC} restoring the caller saved register and jumping back to the caller. This will almost certainly have counterpart of PUSH {R4-R11, LR} in the beginning of the function: your are pushing the content of the link register (the return address) onto the stack but store it back into the program counter effectively returning to the caller in the end
B branch to a different function, if this function ends with a tail call and leaving it up to that function to return to the original caller.
Hope that helps,
Martin
You can't branch to a register, you can only branch to a label. If you want to jump to address in a register you need to move it into the PC register (r15).
#define JMP(addr) \
__asm__("mov pc,%0" \
: /*output*/ \
: /*input*/ \
"r" (addr) \
);
I'm writing a kernel space component for a research project which requires me to intercept and checkpoint a user space process at different points in its execution (specific instructions.) For various reasons I cannot modify the user-space program or ptrace that process.
To accomplish this goal I'm attempting to insert an breakpoint (INT 3 instruction) in the user-space process at the point I need to checkpoint it, and then intercept the SIGTRAP in kernel space. Unfortunately, I can't seem to figure out how to properly modify the read-only text section of the user-space code from the kernel space of that process. I'm currently attempting to use the get_user_pages API to force the pages writable, and modify them, but the text data doesn't seem to change. The relevant portions of the code I'm attempting to use are below. user_addr is the user-space address to insert a breakpoint at (unsigned long); page is a struct page *.
char *addr;
unsigned long aligned_user_addr = user_addr & ~((unsigned long)PAGE_SIZE - 1);
down_read(¤t->mm->mmap_sem);
rc = get_user_pages(current, current->mm, aligned_user_addr,
1, 1, 1, &page, &vma);
up_read(¤t->mm->mmap_sem);
BUG_ON(rc != 1);
addr = kmap(page);
BUG_ON(!addr);
offs = user_addr % PAGE_SIZE;
/* NOTE: INT3_INSTR is defined to be 0xCC */
addr[offs] = INT3_INSTR;
BUG_ON(addr[offs] != INT3_INSTR); // Assertion fails
set_page_dirty(page);
kunmap(page);
page_cache_release(page);
I'm hoping someone with more kernel knowledge and experience will be able to tell me what I'm doing wrong, or the proper way to go about accomplishing my task.
Thank you for your help.
It turns out that my issue was actually with C sign extension. INT3_INSTR was defined as:
#define INT3_INSTR 0xCC
Which makes it an integer, and the line:
BUG_ON(addr[offs] != INT3_INSTR);
evaluated addr[offs] to be a signed char. In c when a signed char is compared to an int its type is elevated to that of int, and since its signed it will be signed extended if its MSB is 1. As 0xCC's MSB is always 1 the comparison always evaluated to:
BUG_ON(0xFFFFFFCC != 0xCC);
Which evaluated as false. Changing addr to a unsigned char * resolves the issue. and then the above code works.
I was looking through an Intel provided reference implementation of RDRAND instruction. The page is Intel Digital Random Number Generator (DRNG) Software Implementation Guide, and the code came from Intel Digital Random Number Generator software code examples.
The following is the relevant portion from Intel. It reads a random value and places it in val, and it sets the carry flag on success.
char rc;
unsigned int val;
__asm__ volatile(
"rdrand %0 ; setc %1"
: "=r" (val), "=qm" (rc)
);
// 1 = success, 0 = underflow
if(rc) {
// use val
...
}
Soory to have to ask. I don't think it was covered in GNU Extended Assembler, and searching for "=qm" is producing spurious hits.
What does the "=qm" mean in the extended assembler?
What you're looking at is an inline assembler constraint. The GCC documentation is at 6.47.3.1 Simple Constraints and 6.47.3.4 Constraints for Particular Machines under x86 family section. This one (=qm) combines three flags which indicate:
=: The operand is write-only - its previous value is not relevant.
q: The operand must be in register a, b, c, or d (it cannot be in esi, for instance).
m: The operand may be placed in memory.
qm probably means 1 byte 8 bit mem
=qm will be valid constraint for storing 1 byte result
See what setc wants
http://web.itu.edu.tr/~aydineb/index_files/instr/setc.html
reg8 and mem8
as we know only eax , ebx edx ecx .. a,b,c,d registers that q refer can be used cause they can accessed with low byte al dl cl ...With combining qm we are getting mem8 . m meant memory. Thats what I meant
Wow that stumped me at first but I searched around a bit and found out that it is a reference to the model of the processor this peice of code is meant for.
Spicically I read that it is for the i7 Quadcore.
Is that where you got this code from?
It is a simple value indicator for a variable syntax.
I haven't done assembly since school (eons ago) and have never done any x86, but I have found a pesky bug in old existing code where somebody isn't doing an atomic op where they should be. The person who wrote the code is long gone and nobody around here knows the answer to my question. What I need to do is create an atomic copy for 128-bit values. The code I currently have is as follows:
void atomic_copy128(volatile void* dest,volatile const void* source) {
#if PLATFORM_BITS == 64
#ifdef __INTEL_COMPILER
//For IA64 platform using intel compiler
*((__int64*)source)=__load128((__int64*)dest,((__int64*)source)+1);
#else
//For x86_64 compiled with gcc
__asm__ __volatile__("lock ; movq %0,%1"
: "=r"(*((volatile long *)(source)))
: "r"(*((volatile long *)(dest)))
: "memory");
#endif
#else
#error "128 bit operations not supported on this platform."
#endif
}
This isn't the code that I originally tried, since I've messed with it quite a bit while trying get it to compile and run. When I make it a totally invalid instruction, it does not compile. When I run this, it executes until it hits this line and then generates a "Illegal instruction" error message. I'd appreciate any help.
As far as I know, "movq" supports at most one memory operand, and its arguments are of 64-bit size anyway, so even if two memory operands were supported, it still wouldn't give you that atomic 128-bit copy you're looking for.
For windows:
::memset( dst, -1, 16 );
_InterlockedCompareExchange128(source, -1, -1, dst );
(but const must be deleted)
For other use cmpxchg16b instruction