blob: c485be3412e35b4bd23772d2fd89646929cd2e64 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2023-24 Rivos Inc.
*/
#include <linux/binfmts.h>
#include <linux/elf.h>
#undef pr_fmt
#define pr_fmt(fmt) "rv-elf-attr: " fmt
#define PT_RISCV_ATTRIBUTES 0x70000003
#define RV_ATTR_TAG_stack_align 4
#define RV_ATTR_TAG_arch 5
#define RV_ATTR_TAG_unaligned_access 6
#define RV_ATTR_SEC_SZ SZ_1K
static void rv_elf_attr_int(u64 tag, u64 val)
{
}
static void rv_elf_attr_str(u64 tag, const char *str)
{
}
static u64
decode_uleb128 (unsigned char **dpp)
{
unsigned char *bp = *dpp;
unsigned char byte;
unsigned shift = 0;
u64 result = 0;
while (1)
{
byte = *bp++;
result |= (byte & 0x7f) << shift;
if ((byte & 0x80) == 0)
break;
shift += 7;
}
*dpp = bp;
return result;
}
/*
* Parse .riscv.attributes elf section to get the various compile time
* attributes such as -march, unaligned access and so no.
*/
static int rv_parse_elf_attributes(struct file *f, const struct elf_phdr *phdr,
struct arch_elf_state *state)
{
unsigned char buf[RV_ATTR_SEC_SZ];
unsigned char *p;
ssize_t n;
int ret=0;
loff_t pos;
pr_info("Section .riscv.attributes found\n");
/* Assume a reasonable size for now */
if (phdr->p_filesz > sizeof(buf))
return -ENOEXEC;
memset(buf, 0, RV_ATTR_SEC_SZ);
pos = phdr->p_offset;
n = kernel_read(f, &buf, phdr->p_filesz, &pos);
if (n < 0)
return -EIO;
p = buf;
p++; /* format-version (1B) */
while ((p - buf) < n) {
unsigned char *vendor_start;
u32 len;
/*
* Organized as "vendor" sub-section(s).
* Current only 1 specified "riscv"
*/
p += 4; /* sub-section length (4B) */
while (*p++ != '\0'); /* vendor name string */
p++; /* Tag_File (1B) */
len = *(u32 *)p; /* data length (4B) */
p += 4;
len -= 5; /* data length includes Tag and self length */
vendor_start = p;
while((p - vendor_start) < len) {
u64 tag = decode_uleb128(&p);
unsigned char *val_str;
u64 val_n;
switch(tag) {
case RV_ATTR_TAG_stack_align:
val_n = decode_uleb128(&p);
break;
case RV_ATTR_TAG_unaligned_access:
val_n = decode_uleb128(&p);
pr_info("Tag_RISCV_unaligned_access =%llu\n", val_n);
rv_elf_attr_int(tag, val_n);
break;
case RV_ATTR_TAG_arch:
val_str = p;
while (*p++ != '\0');
pr_info("Tag_RISCV_arch =[%s]\n", val_str);
rv_elf_attr_str(tag, val_str);
break;
default:
val_n = decode_uleb128(&p);
pr_info("skipping Unrecognized Tag [%llu]=%llu\n", tag, val_n);
break;
}
}
}
return ret;
}
/*
* Hook invoked from common elf loader to parse any arch specific elf segments
*/
int arch_elf_pt_proc(void *_ehdr, void *_phdr, struct file *elf,
bool is_interp, struct arch_elf_state *state)
{
struct elf_phdr *phdr = _phdr;
int ret = 0;
if (phdr->p_type == PT_RISCV_ATTRIBUTES && !is_interp)
ret = rv_parse_elf_attributes(elf, phdr, state);
return ret;
}
int arch_check_elf(void *_ehdr, bool has_interpreter, void *_interp_ehdr,
struct arch_elf_state *state)
{
return 0;
}