Assembly code within C (ARMv8 architecture) - c

i am working on a Cortex-A72 (Armv8) and i need to implement this pseudocode:
put addr1 into X9
put addr2 into X10
for i := 0 to N − 1 do
STR X0, [X9]
STR X0, [X10]
DC CVAC, X9
DC CVAC, X10
Following my C code:
int main(){
unsigned char temp = 0xff;
unsigned char *mem;
mem = mmap(NULL, BUF_SIZE, PROT_READ | PROT_WRITE,MAP_ANON | MAP_PRIVATE, -1, 0);
if (mem == MAP_FAILED){
perror("mmap()");
return 1;
}
memset(mem,0xff,BUF_SIZE);
/* Select two random addresses within memory pool*/
size_t offset1 = (rand()<<12)%BUF_SIZE;
size_t offset2 = (rand()<<12)%BUF_SIZE;
unsigned char *addr1 = (unsigned char*) (mem+offset1);
unsigned char *addr2= (unsigned char*) (mem+offset2)
for (int i = 0; i < 10000; i++){
asm volatile("str %x1, %x0" : "=m"(*addr1) : "r"(temp));
asm volatile("str %x1, %x0" : "=m"(*addr2) : "r"(temp));
__asm__ __volatile__("dc cvac, %0\n\t" : : "r" (addr1));
__asm__ __volatile__("dc cvac, %0\n\t" : : "r" (addr2));
}
.
.
.
}
I just want to know if I am using the assembly code in the right way.
The goal is to access data directly in physical memory, bypassing cache.

Related

devmem2 is not writing to the address specified

I am trying to change a variable's content of a program with devmem2. Simply:
static inline __attribute__ ((always_inline))
void clflush(volatile void *p)
{
asm volatile ("clflush (%0)\n"::"r" (p):"memory");
}
uint64_t get_pfn(uint64_t entry) {
return ((entry) & 0x3fffffffffffff);
}
uint64_t get_phys_addr(uint64_t v_addr)
{
uint64_t entry;
uint64_t offset = (v_addr/4096) * sizeof(entry);
uint64_t pfn;
int fd = open("/proc/self/pagemap", O_RDONLY);
assert(fd >= 0);
int bytes_read = pread(fd, &entry, sizeof(entry), offset);
close(fd);
assert(bytes_read == 8);
assert(entry & (1ULL << 63));
pfn = get_pfn(entry);
assert(pfn != 0);
return (pfn*4096) | (v_addr & 4095);
}
int main(){
int a = 44;
uint64_t v_addr = (uint64_t) &a;
printf("%lx\n",v_addr);
uint64_t p_addr = get_phys_addr(v_addr);
printf("%lx\n",p_addr);
clflush(&a);
sleep(20);
int b = a;
printf("value of b: %d\n",b);
printf("value of b: %d\n",a);
uint64_t v_addr2 = (uint64_t) &b;
printf("%lx\n",v_addr2);
uint64_t p_addr2 = get_phys_addr(v_addr2);
printf("%lx\n",p_addr2);
}
I am trying to change the content of variable "a" while sleeping with devmem2. Consider that my program prints "17580fab8" as the physical address of "a". I am executing devmem2 in another tab like this:
sudo devmem2 -x17580fab8 w 0x1235
So, after sleeping 20 seconds, I would expect the value of a and b to 0x1235. However, they do not change. The output when I run devmem2 while my program sleeps is this:
/dev/mem opened.
Memory mapped at address 0x7f464d50c000.
Value at address 0x0 (0x7f464d50c000): 0x1235
Written 0x1235; readback 0x1235
devmem2 claims to "read/write from/to any location in memory". Is it wrong? Because how can it write to my program's pages?

Enabling paging causes PAGE_FAULT immeditially after setting flag on cr0

I'm working on own OS, it's a lot of fun but i'm stucked with paging, I had wrote simple(very simple) pagination code, but when I turn pagination on, there is page fault. Some details:
I'm using C for kernel
I'm using qemu as VM
I had own crosscompiler
I don't use any external libs
After loading and jumping into kernel code I setup interrupts and remap PIC, It's working and i reckon it's not the issue, after that I'm trying to enable paging
Here is paging.h
#ifndef PAGING_H
#define PAGING_H
#include "../cpu/types.h"
#include "../cpu/isr.h"
#define PAGE_SIZE 4096
typedef struct page{
u32 present :1;
u32 rw :1;
u32 kernel_space :1;
u32 accessed :1;
u32 dirty :1;
u32 unused :7;
u32 frame :20;
} page_t;
typedef struct page_table{
page_t pages[1024];
} page_table_t;
typedef struct page_directory{
u32 page_tables[1024];
} page_dir_t;
typedef struct page_directory_interface{
page_dir_t dir;
page_table_t* page_tables;
u32 page_dir_ph_address;
} page_dir_interface_t;
void init_paging();
void switch_page_dir(page_dir_t* new_dir);
page_t* get_page(u32 addres, page_dir_t* dir, u8 make);
void page_fault(registers_t regs);
#endif
I'm using page_t struct from some tutorial, because I reckon it's quite convinent, other parts, I've writed on my own twice, on both cases code results with PageFault. Here's paging.c:
#include "./paging.h"
#include "../util/panic.h"
#include "./memory.h"
#include "../drivers/screen.h"
page_dir_t* kernel_dir, cur_dir;
//helper function for convinient printing
void pint(u32 a){
char * tmp;
itoa(a, tmp);
kprint(tmp);
kprint("\n");
}
void init_paging(){
kernel_dir = kmalloc(sizeof(page_dir_t));
memset(kernel_dir, 0, 4096);
pint(sizeof(page_dir_t));
pint(&kernel_dir->page_tables[0]);
pint(kernel_dir);
int i;
for(i=0; i<1024; i++){ //setting up page directories for kernel
// read and write and kernel_mode and not present
kernel_dir->page_tables[i] = (u32)kmalloc(sizeof(page_table_t)) | 0x2;
}
map_page_table(kernel_dir->page_tables[0], 1, 1); //map ram to pages;
kernel_dir->page_tables[0] |= 0x3; //set as present
enable_paging();
}
void map_page_table(page_table_t* p_table, int kernel, int rw){
int i;
page_t tmp_page;
tmp_page.present = 1;
tmp_page.rw = (rw) ? 1 : 0;
tmp_page.kernel_space = (kernel) ? 1: 0;
for(i=0; i<1024; i++){
tmp_page.frame |= kmalloc(PAGE_SIZE);
p_table->pages[i] = tmp_page;
}
}
void enable_paging(){
u32 cr0_temp_value;
__asm__ __volatile__("mov %0, %%cr3":: "r"(&kernel_dir->page_tables));
__asm__ ("mov %%cr0, %0": "=r"(cr0_temp_value));
cr0_temp_value |= 0x80000000;
__asm__ __volatile__("mov %0, %%cr0":: "r"(cr0_temp_value));
}
And here is memory.c:
#include "./memory.h"
#include "../cpu/types.h"
#include "../../cathesimc/def.h"
u32 placement_addr = 0x10000;
void memcpy(char* src, char* dst, unsigned int bytes){
int i=0;
for (i=0; i< bytes; i++){
dst[i] = src[i];
}
}
void memset(u8* dest, u8 val, u32 len){
u8* tmp = (u8*)dest;
for(;len != 0; len--) *tmp++ = val;
}
//#TODO poprawic tego biedackiego malloca
u32 kmalloc_intrnl(size_t size, short int align, u32 *phys_addr){
if(align != 0 && (placement_addr & 0xFFFFF000)){
placement_addr &= 0xFFFFF000;
placement_addr += 0x1000;
}
if(phys_addr) *phys_addr = placement_addr;
u32 ret = placement_addr;
placement_addr += size;
return ret;
}
u32 kmalloc(size_t size){
return kmalloc_intrnl(size, 0, NULL);
}
u32 kmalloc_a(size_t size){
return kmalloc_intrnl(size, 1, NULL);
}
u32 kmalloc_p(size_t size, u32* phys_addr){
return kmalloc_intrnl(size, 0, phys_addr);
}
u32 kmalloc_ap(size_t size, u32* phys_addr){
return kmalloc_intrnl(size, 1, phys_addr);
}
My kernel is below 0x10000, it has about 16kb so 0x10000 is safe start of mapped space and memory from that address is definetely not used, I only set up one page table because I want to make paging barebones, and then take care about allocation and freeing, so my only goal for now is to find out what mistake I has making.
============EDIT==============
I have made intensive investigation, and I found that malloc has strange bahaviour, below are snippets of code with explaination
//as we can se from kmalloc() code above, kmalloc starts with
//placement_addr == 0x10000 when kernel starts
void init_paging(){
kernel_dir = kmalloc(sizeof(page_dir_t)); //page_dir_t has size 4096 and that is first malloc in kernel code
memset(kernel_dir, 0, 4096);
pint(sizeof(page_dir_t));
phex(sizeof(page_dir_t));
pint(&kernel_dir->page_tables[0]);
phex(&kernel_dir->page_tables[0]); //prints 0x10000 as expected
int i;
for(i=0; i<1024; i++){ //setting up page directories for kernel
// read and write and kernel_mode and not present
kernel_dir->page_tables[i] = (u32)kmalloc(sizeof(page_table_t)) | 0x2;
}
phex(kernel_dir->page_tables[0]); //0x11002 as expected
phex(kernel_dir->page_tables[1023]); //0x410002 as expected
kprint("pages\n");
map_page_table(kernel_dir->page_tables[0], 1, 1); //map ram to pages;
kernel_dir->page_tables[0] |= 0x3; //set as present
kprint("lol");
enable_paging();
}
void map_page_table(page_table_t* p_table, int kernel, int rw){
int i;
page_t tmp_page;
tmp_page.present = 1;
tmp_page.rw = (rw) ? 1 : 0;
tmp_page.kernel_space = (kernel) ? 1: 0;
//since now troubles starts this forloop below prints adresses:
// iteration0 0x11000
// iteration1 0x12000
// iteration2 0x13000
// iteration3 0x14000
// iteration4 0x15000
// iteration5 0x16000
// iteration6 0x17000
// iteration7 0x18000
for(i=0; i<8; i++){
tmp_page.frame = kmalloc(PAGE_SIZE);
phex(tmp_page.frame);
p_table->pages[i] = tmp_page;
}
}
I think that's definetly might be source of problems but I have no idea why placement_addr changes when i call another function, and why then it changes to 0x11000

Why can't i exit from shellcode with a syscall?

I try to make a Programm where you put in some assembled assembly in hex and run it.
With simple instructions like int3 it works, but when I try to exit from the programm with a syscall it doesnt work.
I assembled it with rasm2
mov eax, 1
mov ebx, 12
int 0x80
and then put it as an argument ./Programm b801000000bb0c000000cd80 1
but i get a segfault.
Here is my code:
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
char *base16dec(char *b16str) {
size_t stingrlength = strlen(b16str);
char *decodedstr = malloc(stingrlength / 2);
for (size_t i = 0; i < stingrlength; i += 2) {
u_int8_t num = 0;
char stringIn[3];
stringIn[0] = b16str[i];
stringIn[1] = b16str[i+1];
stringIn[2] = 0;
sscanf(stringIn, "%hhx", &num);
decodedstr[i/2] = (char) num;
}
return decodedstr;
}
this decodes the hex string
int main(int argc, char *argv[]) {
char *dirstr = "XXXXXX";
char dir[7];
strcpy(dir, dirstr);
int fd = mkstemp(dir);
if (fd == -1) {
dirstr = "/tmp/XXXXXX";
char dir[12];
strcpy(dir, dirstr);
fd = mkstemp(dir);
}
unlink(dir);
this creates the tmp file where the assembly is stored
char *stringIn;
if (argc == 2) {
stringIn = malloc(strlen(argv[1]));
strcpy(stringIn, argv[1]);
} else if (argc == 3) {
u_int8_t num = 0;
sscanf(argv[2], "%hhu", &num);
if (num == 1) {
char *done = base16dec(argv[1]);
stringIn = malloc(strlen(done));
strcpy(stringIn, done);
} else {
stringIn = malloc(strlen(argv[1]));
strcpy(stringIn, argv[1]);
}
} else {
stringIn = malloc(1024);
scanf("%s", stringIn);
char *done = base16dec(stringIn);
stringIn = malloc(strlen(done));
strcpy(stringIn, done);
}
this parses and copies the input to stringIn
ftruncate(fd, strlen(stringIn));
u_int8_t *code = mmap(NULL, strlen(stringIn), PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE , fd, 0);
this expands the tmp file and makes it executable and creates a pointer to it named code
for (int i = 0; i < 1024; i++) {
code[i] = (u_int8_t) stringIn[i];
}
this copies the assembly bytes into code
#if __x86_64__
__asm__(
"mov %0, %%rbx\n"
"jmp *%%rbx"
:
: "g" (code)
: "memory"
);
#elif __i386__
__asm__(
"mov %0, %%ebx\n"
"jmp *%%ebx"
:
: "r" (code)
);
#else
#endif
this jumps to the the assembly
return 0;
}
EDIT:
I can't debug the shellcode using gdb
I use 64bit Linux Mint
I tried to copy 0 using strcpy
Since this is a shellcode you can't have null bytes. In your code you have 2 movs with immediates that are padded to 32-bits
mov eax, 1
mov ebx, 12
Which encodes as B801000000BB0C000000, when C hits the null bytes it thinks the string has ended so it only ends up copying part of the instruction and then it executes garbage.
Instead you'll need to use:
xor eax, eax
inc eax
xor ebx, ebx
mov bl, 12
This will provide the values you want for your system call and does not encode as any null bytes.

Converting hexadecimal to ASCII in c

I'm working on an Android rom for a mobile phone and I want to make the kernel load the wifi MAC address from the device's NV partition. My code looks like this:
#include <linux/kernel.h>
#include <linux/random.h>
#include <linux/syscalls.h>
#define ETHER_ADDR_LEN 6
#define FILE_WIFI_MACADDR "/dev/block/mmcblk0p7"
static int bcm_wifi_get_mac_addr(unsigned char *buf)
{
int ret = 0;
mm_segment_t oldfs;
int i;
int fp;
int macbyte;
int readlen = 0;
uint rand_mac;
static unsigned char mymac[ETHER_ADDR_LEN] = {0,};
const unsigned char nullmac[ETHER_ADDR_LEN] = {0,};
const unsigned char bcastmac[] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF};
if (buf == NULL)
return -EAGAIN;
memset(buf, 0x00, ETHER_ADDR_LEN);
oldfs = get_fs();
set_fs(get_ds());
fp = sys_open(FILE_WIFI_MACADDR, O_RDONLY, 0);
if (fp < 0) {
pr_err("%s: Failed to read error %d for %s\n",
__FUNCTION__, fp, FILE_WIFI_MACADDR);
goto random_mac;
}
for (i = 0; i<12; i++) {
macbyte=0;
sys_lseek( fp,i+7680,SEEK_SET);
readlen = sys_read(fp,&macbyte,1);
if (i)
sprintf(macaddr,"%s%c",macaddr,macbyte);
else
sprintf(macaddr,"%c",macbyte);
}
if (readlen > 0) {
unsigned char* macbin;
macbin = (unsigned char*)macaddr;
pr_info("%s: READ MAC ADDRESS %02X:%02X:%02X:%02X:%02X:%02X\n",
__FUNCTION__,
macbin[0], macbin[1], macbin[2],
macbin[3], macbin[4], macbin[5]);
if (memcmp(macbin, nullmac, ETHER_ADDR_LEN) == 0 ||
memcmp(macbin, bcastmac, ETHER_ADDR_LEN) == 0) {
sys_close(fp);
goto random_mac;
}
memcpy(buf, macbin, ETHER_ADDR_LEN);
} else {
sys_close(fp);
goto random_mac;
}
sys_close(fp);
return ret;
random_mac:
set_fs(oldfs);
pr_debug("%s: %p\n", __func__, buf);
if (memcmp( mymac, nullmac, ETHER_ADDR_LEN) != 0) {
/* Mac displayed from UI is never updated..
So, mac obtained on initial time is used */
memcpy(buf, mymac, ETHER_ADDR_LEN);
return 0;
}
srandom32((uint)jiffies);
rand_mac = random32();
buf[0] = 0x00;
buf[1] = 0x90;
buf[2] = 0x4c;
buf[3] = (unsigned char)rand_mac;
buf[4] = (unsigned char)(rand_mac >> 8);
buf[5] = (unsigned char)(rand_mac >> 16);
memcpy(mymac, buf, 6);
pr_info("[%s] Exiting. MAC %02X:%02X:%02X:%02X:%02X:%02X\n",
__FUNCTION__,
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5] );
return 0;
}
The idea is to load the nv parition, located at /dev/block/mmcblk0p7, then read the mac address, which is located at offset 7680 on the nv. The problem is that the MAC address is written in hex, so I'm trying to print it to an ASCII string using sprintf().
for (i = 0; i<12; i++) {
macbyte=0;
sys_lseek( fp,i+7680,SEEK_SET);
readlen = sys_read(fp,&macbyte,1);
if (i)
sprintf(macaddr,"%s%c",macaddr,macbyte);
else
sprintf(macaddr,"%c",macbyte);
}
In the nv the MAC looks something like this: 34 30 42 30 46 41 36 35 39 33 34 39, which in ASCII is 40B0FA659349. But instead the resulting MAC is 34:30:42:30:46:41, which tells me that the hex values are not getting converted at all.
What would be the proper way to export the hex values into an ASCII string? I'm new to programming and i was hoping someone could give me some tips.
Thanks in advance.
In your loop you are reading single bytes and converting them to hex strings, while what you actually need to do is read the hex string and convert it byte values. Unless you actually want a hex string, in which case no conversion is necessary.
You have 12 hex characters representing 6 bytes so:
#define MAC_LEN 6
uint8_t macbytes[MAC_LEN] ;
for( i = 0; i < MAC_LEN; i++ )
{
char hex_str[3] ;
unsigned byte_val ;
sys_lseek( fp, (i*2) + 7680, SEEK_SET ) ;
readlen = sys_read( fp, hex_str, 2 ) ;
sscanf( hex_str, "%2X", &byte_val ) ;
macbytes[i] = (uint8_t)byte_val ) ;
}
The data in NV is already ASCII coded hexadecimal; for example 0x34 is the ASCII code for the hex digit '4', and 0x30 that for '0', together the ASCII character pair "40" represent the single 8 bit integer value 0x40. So the conversion you need is ASCII to byte array, not "hex to ASCII" (which makes no semantic sense).
I think this is OP's stubbing block: forming a string version of the MAC address.
I'll make this wiki for anyone to modify, borrow or steal.
sys_lseek( fp,i+7680,SEEK_SET);
char macaddr[100];
char *p = macaddr;
const char *sep = "";
for (i = 0; i < 12; i++) {
unsigned char macbyte;
int readlen = sys_read(fp, &macbyte, 1);
if (readlen != 1) Handle_Error();
p += sprintf(p, "%s%02X", sep, macbyte);
sep = ":";
}
puts(macaddr);

C - Memory access with mmap

I have a hex file of 327680 characters which I'm writing to physical address 0x30000000 - 0x3004FFFF on the memory on my ARM linux system.
While reading back from the memory I'm getting a segfault after reading 64170 characters from the start address, ie at 0x3000FAAA.
If I change my starting address to 0x3000FA64, then also I get a segfault after 64170 characters.
How do I ensure data is accessed correctly if Data > 4kB (page size) ?
I'm unable to understand the exact problem, so I'm adding the snippet of my code below:
#define MAX_RANGE 327679
int fd;
FILE* fd_table=NULL;
unsigned long int count = 0 ;
void * mem;
void * aligned_vaddr;
unsigned long aligned_paddr;
uint32_t aligned_size;
unsigned long int addr_phys;
uint8_t *addr;
int g_size = 1;
unsigned long int g_paddr = 0x30000000; //Starting physical address
while((count<MAX_RANGE)){
g_paddr = addr_phys;
g_paddr &= ~(g_size - 1);
aligned_paddr = g_paddr & ~(4096 - 1);
aligned_size = g_paddr - aligned_paddr + (g_count * g_size);
aligned_size = (aligned_size + 4096 - 1) & ~(4096 - 1);
/* Align address to access size */
aligned_vaddr = mmap(NULL, aligned_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, aligned_paddr);
if (aligned_vaddr == NULL) {
printf("Error mapping address\n");
close(fd);
return 1;
}
mem = (void *)((uint32_t)aligned_vaddr + (g_paddr - aligned_paddr));
addr = mem;
fprintf(fd_table, "%02X\n",addr[0]);
addr_phys +=1; //Increment byte address
count++;
}
Note:
1. There is no error in the write process, I have verified by viewing the segfault address with memtool.
2. The address 0x30000000 onwards is not used by the system (I have ensured that in the u-boot).

Resources