aboutsummaryrefslogtreecommitdiff
path: root/linux-user
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2020-10-29 11:40:04 +0000
committerPeter Maydell <peter.maydell@linaro.org>2020-10-29 11:40:04 +0000
commit802427bcdae1ad2eceea8a8877ecad835e3f8fde (patch)
tree3b27c2bb1642d355cb762e9a597f83cb17d299aa /linux-user
parentc0444009147aa935d52d5acfc6b70094bb42b0dd (diff)
parent32bd322a0134ed89db00f2b9b3894982db3dedcb (diff)
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20201027-1' into staging
target-arm queue: * raspi: add model of cprman clock manager * sbsa-ref: add an SBSA generic watchdog device * arm/trace: Fix hex printing * raspi: Add models of Pi 3 model A+, Pi Zero and Pi A+ * hw/arm/smmuv3: Set the restoration priority of the vSMMUv3 explicitly * Nuvoton NPCM7xx: Add USB, RNG, GPIO and watchdog support * hw/arm: fix min_cpus for xlnx-versal-virt platform * hw/arm/highbank: Silence warnings about missing fallthrough statements * linux-user: Support Aarch64 BTI * Armv7M systick: fix corner case bugs by rewriting to use ptimer # gpg: Signature made Tue 27 Oct 2020 11:27:10 GMT # gpg: using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE # gpg: issuer "peter.maydell@linaro.org" # gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@gmail.com>" [ultimate] # gpg: aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [ultimate] # Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83 15CF 3C25 25ED 1436 0CDE * remotes/pmaydell/tags/pull-target-arm-20201027-1: (48 commits) hw/timer/armv7m_systick: Rewrite to use ptimers hw/core/ptimer: Support ptimer being disabled by timer callback hw/arm/sbsa-ref: add SBSA watchdog device hw/watchdog: Implement SBSA watchdog device hw/arm/bcm2835_peripherals: connect the UART clock hw/char/pl011: add a clock input hw/misc/bcm2835_cprman: add sane reset values to the registers hw/misc/bcm2835_cprman: add the DSI0HSCK multiplexer hw/misc/bcm2835_cprman: implement clock mux behaviour hw/misc/bcm2835_cprman: add a clock mux skeleton implementation hw/misc/bcm2835_cprman: implement PLL channels behaviour hw/misc/bcm2835_cprman: add a PLL channel skeleton implementation hw/misc/bcm2835_cprman: implement PLLs behaviour hw/misc/bcm2835_cprman: add a PLL skeleton implementation hw/arm/raspi: add a skeleton implementation of the CPRMAN hw/arm/raspi: fix CPRMAN base address hw/core/clock: trace clock values in Hz instead of ns hw/core/clock: provide the VMSTATE_ARRAY_CLOCK macro arm/trace: Fix hex printing hw/arm/raspi: Add the Raspberry Pi 3 model A+ ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'linux-user')
-rw-r--r--linux-user/aarch64/signal.c10
-rw-r--r--linux-user/elfload.c326
-rw-r--r--linux-user/mmap.c16
-rw-r--r--linux-user/qemu.h4
-rw-r--r--linux-user/syscall_defs.h4
5 files changed, 302 insertions, 58 deletions
diff --git a/linux-user/aarch64/signal.c b/linux-user/aarch64/signal.c
index d50c1ae583..b591790c22 100644
--- a/linux-user/aarch64/signal.c
+++ b/linux-user/aarch64/signal.c
@@ -506,10 +506,16 @@ static void target_setup_frame(int usig, struct target_sigaction *ka,
+ offsetof(struct target_rt_frame_record, tramp);
}
env->xregs[0] = usig;
- env->xregs[31] = frame_addr;
env->xregs[29] = frame_addr + fr_ofs;
- env->pc = ka->_sa_handler;
env->xregs[30] = return_addr;
+ env->xregs[31] = frame_addr;
+ env->pc = ka->_sa_handler;
+
+ /* Invoke the signal handler as if by indirect call. */
+ if (cpu_isar_feature(aa64_bti, env_archcpu(env))) {
+ env->btype = 2;
+ }
+
if (info) {
tswap_siginfo(&frame->info, info);
env->xregs[1] = frame_addr + offsetof(struct target_rt_sigframe, info);
diff --git a/linux-user/elfload.c b/linux-user/elfload.c
index f6022fd704..bf8c1bd253 100644
--- a/linux-user/elfload.c
+++ b/linux-user/elfload.c
@@ -12,6 +12,7 @@
#include "qemu/guest-random.h"
#include "qemu/units.h"
#include "qemu/selfmap.h"
+#include "qapi/error.h"
#ifdef _ARCH_PPC64
#undef ARCH_DLINFO
@@ -1521,6 +1522,39 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs,
#include "elf.h"
+/* We must delay the following stanzas until after "elf.h". */
+#if defined(TARGET_AARCH64)
+
+static bool arch_parse_elf_property(uint32_t pr_type, uint32_t pr_datasz,
+ const uint32_t *data,
+ struct image_info *info,
+ Error **errp)
+{
+ if (pr_type == GNU_PROPERTY_AARCH64_FEATURE_1_AND) {
+ if (pr_datasz != sizeof(uint32_t)) {
+ error_setg(errp, "Ill-formed GNU_PROPERTY_AARCH64_FEATURE_1_AND");
+ return false;
+ }
+ /* We will extract GNU_PROPERTY_AARCH64_FEATURE_1_BTI later. */
+ info->note_flags = *data;
+ }
+ return true;
+}
+#define ARCH_USE_GNU_PROPERTY 1
+
+#else
+
+static bool arch_parse_elf_property(uint32_t pr_type, uint32_t pr_datasz,
+ const uint32_t *data,
+ struct image_info *info,
+ Error **errp)
+{
+ g_assert_not_reached();
+}
+#define ARCH_USE_GNU_PROPERTY 0
+
+#endif
+
struct exec
{
unsigned int a_info; /* Use macros N_MAGIC, etc for access */
@@ -2372,6 +2406,150 @@ void probe_guest_base(const char *image_name, abi_ulong guest_loaddr,
"@ 0x%" PRIx64 "\n", (uint64_t)guest_base);
}
+enum {
+ /* The string "GNU\0" as a magic number. */
+ GNU0_MAGIC = const_le32('G' | 'N' << 8 | 'U' << 16),
+ NOTE_DATA_SZ = 1 * KiB,
+ NOTE_NAME_SZ = 4,
+ ELF_GNU_PROPERTY_ALIGN = ELF_CLASS == ELFCLASS32 ? 4 : 8,
+};
+
+/*
+ * Process a single gnu_property entry.
+ * Return false for error.
+ */
+static bool parse_elf_property(const uint32_t *data, int *off, int datasz,
+ struct image_info *info, bool have_prev_type,
+ uint32_t *prev_type, Error **errp)
+{
+ uint32_t pr_type, pr_datasz, step;
+
+ if (*off > datasz || !QEMU_IS_ALIGNED(*off, ELF_GNU_PROPERTY_ALIGN)) {
+ goto error_data;
+ }
+ datasz -= *off;
+ data += *off / sizeof(uint32_t);
+
+ if (datasz < 2 * sizeof(uint32_t)) {
+ goto error_data;
+ }
+ pr_type = data[0];
+ pr_datasz = data[1];
+ data += 2;
+ datasz -= 2 * sizeof(uint32_t);
+ step = ROUND_UP(pr_datasz, ELF_GNU_PROPERTY_ALIGN);
+ if (step > datasz) {
+ goto error_data;
+ }
+
+ /* Properties are supposed to be unique and sorted on pr_type. */
+ if (have_prev_type && pr_type <= *prev_type) {
+ if (pr_type == *prev_type) {
+ error_setg(errp, "Duplicate property in PT_GNU_PROPERTY");
+ } else {
+ error_setg(errp, "Unsorted property in PT_GNU_PROPERTY");
+ }
+ return false;
+ }
+ *prev_type = pr_type;
+
+ if (!arch_parse_elf_property(pr_type, pr_datasz, data, info, errp)) {
+ return false;
+ }
+
+ *off += 2 * sizeof(uint32_t) + step;
+ return true;
+
+ error_data:
+ error_setg(errp, "Ill-formed property in PT_GNU_PROPERTY");
+ return false;
+}
+
+/* Process NT_GNU_PROPERTY_TYPE_0. */
+static bool parse_elf_properties(int image_fd,
+ struct image_info *info,
+ const struct elf_phdr *phdr,
+ char bprm_buf[BPRM_BUF_SIZE],
+ Error **errp)
+{
+ union {
+ struct elf_note nhdr;
+ uint32_t data[NOTE_DATA_SZ / sizeof(uint32_t)];
+ } note;
+
+ int n, off, datasz;
+ bool have_prev_type;
+ uint32_t prev_type;
+
+ /* Unless the arch requires properties, ignore them. */
+ if (!ARCH_USE_GNU_PROPERTY) {
+ return true;
+ }
+
+ /* If the properties are crazy large, that's too bad. */
+ n = phdr->p_filesz;
+ if (n > sizeof(note)) {
+ error_setg(errp, "PT_GNU_PROPERTY too large");
+ return false;
+ }
+ if (n < sizeof(note.nhdr)) {
+ error_setg(errp, "PT_GNU_PROPERTY too small");
+ return false;
+ }
+
+ if (phdr->p_offset + n <= BPRM_BUF_SIZE) {
+ memcpy(&note, bprm_buf + phdr->p_offset, n);
+ } else {
+ ssize_t len = pread(image_fd, &note, n, phdr->p_offset);
+ if (len != n) {
+ error_setg_errno(errp, errno, "Error reading file header");
+ return false;
+ }
+ }
+
+ /*
+ * The contents of a valid PT_GNU_PROPERTY is a sequence
+ * of uint32_t -- swap them all now.
+ */
+#ifdef BSWAP_NEEDED
+ for (int i = 0; i < n / 4; i++) {
+ bswap32s(note.data + i);
+ }
+#endif
+
+ /*
+ * Note that nhdr is 3 words, and that the "name" described by namesz
+ * immediately follows nhdr and is thus at the 4th word. Further, all
+ * of the inputs to the kernel's round_up are multiples of 4.
+ */
+ if (note.nhdr.n_type != NT_GNU_PROPERTY_TYPE_0 ||
+ note.nhdr.n_namesz != NOTE_NAME_SZ ||
+ note.data[3] != GNU0_MAGIC) {
+ error_setg(errp, "Invalid note in PT_GNU_PROPERTY");
+ return false;
+ }
+ off = sizeof(note.nhdr) + NOTE_NAME_SZ;
+
+ datasz = note.nhdr.n_descsz + off;
+ if (datasz > n) {
+ error_setg(errp, "Invalid note size in PT_GNU_PROPERTY");
+ return false;
+ }
+
+ have_prev_type = false;
+ prev_type = 0;
+ while (1) {
+ if (off == datasz) {
+ return true; /* end, exit ok */
+ }
+ if (!parse_elf_property(note.data, &off, datasz, info,
+ have_prev_type, &prev_type, errp)) {
+ return false;
+ }
+ have_prev_type = true;
+ }
+}
+
/* Load an ELF image into the address space.
IMAGE_NAME is the filename of the image, to use in error messages.
@@ -2391,16 +2569,17 @@ static void load_elf_image(const char *image_name, int image_fd,
struct elfhdr *ehdr = (struct elfhdr *)bprm_buf;
struct elf_phdr *phdr;
abi_ulong load_addr, load_bias, loaddr, hiaddr, error;
- int i, retval;
- const char *errmsg;
+ int i, retval, prot_exec;
+ Error *err = NULL;
/* First of all, some simple consistency checks */
- errmsg = "Invalid ELF image for this architecture";
if (!elf_check_ident(ehdr)) {
+ error_setg(&err, "Invalid ELF image for this architecture");
goto exit_errmsg;
}
bswap_ehdr(ehdr);
if (!elf_check_ehdr(ehdr)) {
+ error_setg(&err, "Invalid ELF image for this architecture");
goto exit_errmsg;
}
@@ -2421,22 +2600,54 @@ static void load_elf_image(const char *image_name, int image_fd,
mmap_lock();
- /* Find the maximum size of the image and allocate an appropriate
- amount of memory to handle that. */
+ /*
+ * Find the maximum size of the image and allocate an appropriate
+ * amount of memory to handle that. Locate the interpreter, if any.
+ */
loaddr = -1, hiaddr = 0;
info->alignment = 0;
for (i = 0; i < ehdr->e_phnum; ++i) {
- if (phdr[i].p_type == PT_LOAD) {
- abi_ulong a = phdr[i].p_vaddr - phdr[i].p_offset;
+ struct elf_phdr *eppnt = phdr + i;
+ if (eppnt->p_type == PT_LOAD) {
+ abi_ulong a = eppnt->p_vaddr - eppnt->p_offset;
if (a < loaddr) {
loaddr = a;
}
- a = phdr[i].p_vaddr + phdr[i].p_memsz;
+ a = eppnt->p_vaddr + eppnt->p_memsz;
if (a > hiaddr) {
hiaddr = a;
}
++info->nsegs;
- info->alignment |= phdr[i].p_align;
+ info->alignment |= eppnt->p_align;
+ } else if (eppnt->p_type == PT_INTERP && pinterp_name) {
+ g_autofree char *interp_name = NULL;
+
+ if (*pinterp_name) {
+ error_setg(&err, "Multiple PT_INTERP entries");
+ goto exit_errmsg;
+ }
+
+ interp_name = g_malloc(eppnt->p_filesz);
+
+ if (eppnt->p_offset + eppnt->p_filesz <= BPRM_BUF_SIZE) {
+ memcpy(interp_name, bprm_buf + eppnt->p_offset,
+ eppnt->p_filesz);
+ } else {
+ retval = pread(image_fd, interp_name, eppnt->p_filesz,
+ eppnt->p_offset);
+ if (retval != eppnt->p_filesz) {
+ goto exit_read;
+ }
+ }
+ if (interp_name[eppnt->p_filesz - 1] != 0) {
+ error_setg(&err, "Invalid PT_INTERP entry");
+ goto exit_errmsg;
+ }
+ *pinterp_name = g_steal_pointer(&interp_name);
+ } else if (eppnt->p_type == PT_GNU_PROPERTY) {
+ if (!parse_elf_properties(image_fd, info, eppnt, bprm_buf, &err)) {
+ goto exit_errmsg;
+ }
}
}
@@ -2490,7 +2701,7 @@ static void load_elf_image(const char *image_name, int image_fd,
(ehdr->e_type == ET_EXEC ? MAP_FIXED : 0),
-1, 0);
if (load_addr == -1) {
- goto exit_perror;
+ goto exit_mmap;
}
load_bias = load_addr - loaddr;
@@ -2525,15 +2736,41 @@ static void load_elf_image(const char *image_name, int image_fd,
info->brk = 0;
info->elf_flags = ehdr->e_flags;
+ prot_exec = PROT_EXEC;
+#ifdef TARGET_AARCH64
+ /*
+ * If the BTI feature is present, this indicates that the executable
+ * pages of the startup binary should be mapped with PROT_BTI, so that
+ * branch targets are enforced.
+ *
+ * The startup binary is either the interpreter or the static executable.
+ * The interpreter is responsible for all pages of a dynamic executable.
+ *
+ * Elf notes are backward compatible to older cpus.
+ * Do not enable BTI unless it is supported.
+ */
+ if ((info->note_flags & GNU_PROPERTY_AARCH64_FEATURE_1_BTI)
+ && (pinterp_name == NULL || *pinterp_name == 0)
+ && cpu_isar_feature(aa64_bti, ARM_CPU(thread_cpu))) {
+ prot_exec |= TARGET_PROT_BTI;
+ }
+#endif
+
for (i = 0; i < ehdr->e_phnum; i++) {
struct elf_phdr *eppnt = phdr + i;
if (eppnt->p_type == PT_LOAD) {
abi_ulong vaddr, vaddr_po, vaddr_ps, vaddr_ef, vaddr_em, vaddr_len;
int elf_prot = 0;
- if (eppnt->p_flags & PF_R) elf_prot = PROT_READ;
- if (eppnt->p_flags & PF_W) elf_prot |= PROT_WRITE;
- if (eppnt->p_flags & PF_X) elf_prot |= PROT_EXEC;
+ if (eppnt->p_flags & PF_R) {
+ elf_prot |= PROT_READ;
+ }
+ if (eppnt->p_flags & PF_W) {
+ elf_prot |= PROT_WRITE;
+ }
+ if (eppnt->p_flags & PF_X) {
+ elf_prot |= prot_exec;
+ }
vaddr = load_bias + eppnt->p_vaddr;
vaddr_po = TARGET_ELF_PAGEOFFSET(vaddr);
@@ -2551,7 +2788,7 @@ static void load_elf_image(const char *image_name, int image_fd,
image_fd, eppnt->p_offset - vaddr_po);
if (error == -1) {
- goto exit_perror;
+ goto exit_mmap;
}
}
@@ -2583,38 +2820,11 @@ static void load_elf_image(const char *image_name, int image_fd,
if (vaddr_em > info->brk) {
info->brk = vaddr_em;
}
- } else if (eppnt->p_type == PT_INTERP && pinterp_name) {
- char *interp_name;
-
- if (*pinterp_name) {
- errmsg = "Multiple PT_INTERP entries";
- goto exit_errmsg;
- }
- interp_name = malloc(eppnt->p_filesz);
- if (!interp_name) {
- goto exit_perror;
- }
-
- if (eppnt->p_offset + eppnt->p_filesz <= BPRM_BUF_SIZE) {
- memcpy(interp_name, bprm_buf + eppnt->p_offset,
- eppnt->p_filesz);
- } else {
- retval = pread(image_fd, interp_name, eppnt->p_filesz,
- eppnt->p_offset);
- if (retval != eppnt->p_filesz) {
- goto exit_perror;
- }
- }
- if (interp_name[eppnt->p_filesz - 1] != 0) {
- errmsg = "Invalid PT_INTERP entry";
- goto exit_errmsg;
- }
- *pinterp_name = interp_name;
#ifdef TARGET_MIPS
} else if (eppnt->p_type == PT_MIPS_ABIFLAGS) {
Mips_elf_abiflags_v0 abiflags;
if (eppnt->p_filesz < sizeof(Mips_elf_abiflags_v0)) {
- errmsg = "Invalid PT_MIPS_ABIFLAGS entry";
+ error_setg(&err, "Invalid PT_MIPS_ABIFLAGS entry");
goto exit_errmsg;
}
if (eppnt->p_offset + eppnt->p_filesz <= BPRM_BUF_SIZE) {
@@ -2624,7 +2834,7 @@ static void load_elf_image(const char *image_name, int image_fd,
retval = pread(image_fd, &abiflags, sizeof(Mips_elf_abiflags_v0),
eppnt->p_offset);
if (retval != sizeof(Mips_elf_abiflags_v0)) {
- goto exit_perror;
+ goto exit_read;
}
}
bswap_mips_abiflags(&abiflags);
@@ -2649,13 +2859,16 @@ static void load_elf_image(const char *image_name, int image_fd,
exit_read:
if (retval >= 0) {
- errmsg = "Incomplete read of file header";
- goto exit_errmsg;
+ error_setg(&err, "Incomplete read of file header");
+ } else {
+ error_setg_errno(&err, errno, "Error reading file header");
}
- exit_perror:
- errmsg = strerror(errno);
+ goto exit_errmsg;
+ exit_mmap:
+ error_setg_errno(&err, errno, "Error mapping file");
+ goto exit_errmsg;
exit_errmsg:
- fprintf(stderr, "%s: %s\n", image_name, errmsg);
+ error_reportf_err(err, "%s: ", image_name);
exit(-1);
}
@@ -2663,26 +2876,27 @@ static void load_elf_interp(const char *filename, struct image_info *info,
char bprm_buf[BPRM_BUF_SIZE])
{
int fd, retval;
+ Error *err = NULL;
fd = open(path(filename), O_RDONLY);
if (fd < 0) {
- goto exit_perror;
+ error_setg_file_open(&err, errno, filename);
+ error_report_err(err);
+ exit(-1);
}
retval = read(fd, bprm_buf, BPRM_BUF_SIZE);
if (retval < 0) {
- goto exit_perror;
+ error_setg_errno(&err, errno, "Error reading file header");
+ error_reportf_err(err, "%s: ", filename);
+ exit(-1);
}
+
if (retval < BPRM_BUF_SIZE) {
memset(bprm_buf + retval, 0, BPRM_BUF_SIZE - retval);
}
load_elf_image(filename, fd, info, NULL, bprm_buf);
- return;
-
- exit_perror:
- fprintf(stderr, "%s: %s\n", filename, strerror(errno));
- exit(-1);
}
static int symfind(const void *s0, const void *s1)
@@ -2961,7 +3175,7 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info)
if (elf_interpreter) {
info->load_bias = interp_info.load_bias;
info->entry = interp_info.entry;
- free(elf_interpreter);
+ g_free(elf_interpreter);
}
#ifdef USE_ELF_CORE_DUMP
diff --git a/linux-user/mmap.c b/linux-user/mmap.c
index f261563420..00c05e6a0f 100644
--- a/linux-user/mmap.c
+++ b/linux-user/mmap.c
@@ -83,6 +83,22 @@ static int validate_prot_to_pageflags(int *host_prot, int prot)
*host_prot = (prot & (PROT_READ | PROT_WRITE))
| (prot & PROT_EXEC ? PROT_READ : 0);
+#ifdef TARGET_AARCH64
+ /*
+ * The PROT_BTI bit is only accepted if the cpu supports the feature.
+ * Since this is the unusual case, don't bother checking unless
+ * the bit has been requested. If set and valid, record the bit
+ * within QEMU's page_flags.
+ */
+ if (prot & TARGET_PROT_BTI) {
+ ARMCPU *cpu = ARM_CPU(thread_cpu);
+ if (cpu_isar_feature(aa64_bti, cpu)) {
+ valid |= TARGET_PROT_BTI;
+ page_flags |= PAGE_BTI;
+ }
+ }
+#endif
+
return prot & ~valid ? 0 : page_flags;
}
diff --git a/linux-user/qemu.h b/linux-user/qemu.h
index 941ca99722..534753ca12 100644
--- a/linux-user/qemu.h
+++ b/linux-user/qemu.h
@@ -61,6 +61,10 @@ struct image_info {
abi_ulong interpreter_loadmap_addr;
abi_ulong interpreter_pt_dynamic_addr;
struct image_info *other_info;
+
+ /* For target-specific processing of NT_GNU_PROPERTY_TYPE_0. */
+ uint32_t note_flags;
+
#ifdef TARGET_MIPS
int fp_abi;
int interp_fp_abi;
diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h
index 731c3d5341..cabbfb762d 100644
--- a/linux-user/syscall_defs.h
+++ b/linux-user/syscall_defs.h
@@ -1277,6 +1277,10 @@ struct target_winsize {
#define TARGET_PROT_SEM 0x08
#endif
+#ifdef TARGET_AARCH64
+#define TARGET_PROT_BTI 0x10
+#endif
+
/* Common */
#define TARGET_MAP_SHARED 0x01 /* Share changes */
#define TARGET_MAP_PRIVATE 0x02 /* Changes are private */