The main purpose of this patch is to fix handling of unpause calls from PM while another call is ongoing. The solution to this problem sparked a full revision of the threading model, consisting of a large number of related changes: - all active worker threads are now always associated with a process, and every process has at most one active thread working for it; - the process lock is always held by a process's worker thread; - a process can now have both normal work and postponed PM work associated to it; - timer expiry and non-postponed PM work is done from the main thread; - filp garbage collection is done from a thread associated with VFS; - reboot calls from PM are now done from a thread associated with PM; - the DS events handler is protected from starting multiple threads; - support for a system worker thread has been removed; - the deadlock recovery thread has been replaced by a parameter to the worker_start() function; the number of worker threads has consequently been increased by one; - saving and restoring of global but per-thread variables is now centralized in worker_suspend() and worker_resume(); err_code is now saved and restored in all cases; - the concept of jobs has been removed, and job_m_in now points to a message stored in the worker thread structure instead; - the PM lock has been removed; - the separate exec lock has been replaced by a lock on the VM process, which was already being locked for exec calls anyway; - PM_UNPAUSE is now processed as a postponed PM request, from a thread associated with the target process; - the FP_DROP_WORK flag has been removed, since it is no longer more than just an optimization and only applied to processes operating on a pipe when getting killed; - assignment to "fp" now takes place only when obtaining new work in the main thread or a worker thread, when resuming execution of a thread, and in the special case of exiting processes during reboot; - there are no longer special cases where the yield() call is used to force a thread to run. Change-Id: I7a97b9b95c2450454a9b5318dfa0e6150d4e6858
321 lines
10 KiB
C
321 lines
10 KiB
C
#include "fs.h"
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <minix/vm.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/exec_elf.h>
|
|
#include "param.h"
|
|
|
|
/* Include ELF headers */
|
|
#include <sys/elf_core.h>
|
|
#include <machine/elf.h>
|
|
|
|
static void fill_elf_header(Elf32_Ehdr *elf_header, int phnum);
|
|
static void fill_prog_header(Elf32_Phdr *prog_header, Elf32_Word
|
|
p_type, Elf32_Off p_offset, Elf32_Addr p_vaddr, Elf32_Word p_flags,
|
|
Elf32_Word p_filesz, Elf32_Word p_memsz);
|
|
static int get_memory_regions(Elf32_Phdr phdrs[]);
|
|
static void fill_note_segment_and_entries_hdrs(Elf32_Phdr phdrs[],
|
|
Elf32_Nhdr nhdrs[]);
|
|
static void adjust_offsets(Elf32_Phdr phdrs[], int phnum);
|
|
static void dump_elf_header(struct filp *f, Elf32_Ehdr elf_header);
|
|
static void dump_notes(struct filp *f, Elf32_Nhdr nhdrs[], int csig,
|
|
char *proc_name);
|
|
static void dump_program_headers(struct filp *f, Elf_Phdr phdrs[], int
|
|
phnum);
|
|
static void dump_segments(struct filp *f, Elf32_Phdr phdrs[], int
|
|
phnum);
|
|
static void write_buf(struct filp *f, char *buf, size_t size);
|
|
|
|
/*===========================================================================*
|
|
* write_elf_core_file *
|
|
*===========================================================================*/
|
|
void write_elf_core_file(struct filp *f, int csig, char *proc_name)
|
|
{
|
|
/* First, fill in all the required headers, second, adjust the offsets,
|
|
* third, dump everything into the core file
|
|
*/
|
|
#define MAX_REGIONS 100
|
|
#define NR_NOTE_ENTRIES 2
|
|
Elf_Ehdr elf_header;
|
|
Elf_Phdr phdrs[MAX_REGIONS + 1];
|
|
Elf_Nhdr nhdrs[NR_NOTE_ENTRIES];
|
|
int phnum;
|
|
|
|
memset(phdrs, 0, sizeof(phdrs));
|
|
|
|
/* Fill in the NOTE Program Header - at phdrs[0] - and
|
|
* note entries' headers
|
|
*/
|
|
fill_note_segment_and_entries_hdrs(phdrs, nhdrs);
|
|
|
|
/* Get the memory segments and fill in the Program headers */
|
|
phnum = get_memory_regions(phdrs) + 1;
|
|
|
|
/* Fill in the ELF header */
|
|
fill_elf_header(&elf_header, phnum);
|
|
|
|
/* Adjust offsets in program headers - The layout in the ELF core file
|
|
* is the following: the ELF Header, the Note Program Header,
|
|
* the rest of Program Headers (memory segments), Note contents,
|
|
* the program segments' contents
|
|
*/
|
|
adjust_offsets(phdrs, phnum);
|
|
|
|
/* Write ELF header */
|
|
dump_elf_header(f, elf_header);
|
|
|
|
/* Write Program headers (Including the NOTE) */
|
|
dump_program_headers(f, phdrs, phnum);
|
|
|
|
/* Write NOTE contents */
|
|
dump_notes(f, nhdrs, csig, proc_name);
|
|
|
|
/* Write segments' contents */
|
|
dump_segments(f, phdrs, phnum);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* fill_elf_header *
|
|
*===========================================================================*/
|
|
static void fill_elf_header (Elf_Ehdr *elf_header, int phnum)
|
|
{
|
|
memset((void *) elf_header, 0, sizeof(Elf_Ehdr));
|
|
|
|
elf_header->e_ident[EI_MAG0] = ELFMAG0;
|
|
elf_header->e_ident[EI_MAG1] = ELFMAG1;
|
|
elf_header->e_ident[EI_MAG2] = ELFMAG2;
|
|
elf_header->e_ident[EI_MAG3] = ELFMAG3;
|
|
elf_header->e_ident[EI_CLASS] = ELF_TARG_CLASS;
|
|
elf_header->e_ident[EI_DATA] = ELF_TARG_DATA;
|
|
elf_header->e_ident[EI_VERSION] = EV_CURRENT;
|
|
elf_header->e_ident[EI_OSABI] = ELFOSABI_FREEBSD;
|
|
elf_header->e_type = ET_CORE;
|
|
elf_header->e_machine = ELF_TARG_MACH;
|
|
elf_header->e_version = EV_CURRENT;
|
|
elf_header->e_ehsize = sizeof(Elf_Ehdr);
|
|
elf_header->e_phoff = sizeof(Elf_Ehdr);
|
|
elf_header->e_phentsize = sizeof(Elf_Phdr);
|
|
elf_header->e_phnum = phnum;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* fill_prog_header *
|
|
*===========================================================================*/
|
|
static void fill_prog_header (Elf_Phdr *prog_header, Elf_Word p_type,
|
|
Elf_Off p_offset, Elf_Addr p_vaddr, Elf_Word p_flags,
|
|
Elf_Word p_filesz, Elf_Word p_memsz)
|
|
{
|
|
|
|
memset((void *) prog_header, 0, sizeof(Elf_Phdr));
|
|
|
|
prog_header->p_type = p_type;
|
|
prog_header->p_offset = p_offset;
|
|
prog_header->p_vaddr = p_vaddr;
|
|
prog_header->p_flags = p_flags;
|
|
prog_header->p_filesz = p_filesz;
|
|
prog_header->p_memsz = p_memsz;
|
|
|
|
}
|
|
|
|
#define PADBYTES 4
|
|
#define PAD_LEN(x) ((x + (PADBYTES - 1)) & ~(PADBYTES - 1))
|
|
|
|
/*===========================================================================*
|
|
* fill_note_segment_and_entries_hdrs *
|
|
*===========================================================================*/
|
|
static void fill_note_segment_and_entries_hdrs(Elf_Phdr phdrs[],
|
|
Elf_Nhdr nhdrs[])
|
|
{
|
|
int filesize;
|
|
const char *note_name = ELF_NOTE_MINIX_ELFCORE_NAME "\0";
|
|
int name_len, mei_len, gregs_len;
|
|
|
|
/* Size of notes in the core file is rather fixed:
|
|
* sizeof(minix_elfcore_info_t) +
|
|
* 2 * sizeof(Elf_Nhdr) + the size of the padded name of the note
|
|
* - i.e. "MINIX-CORE\0" padded to 4-byte alignment => 2 * 8 bytes
|
|
*/
|
|
|
|
name_len = strlen(note_name) + 1;
|
|
mei_len = sizeof(minix_elfcore_info_t);
|
|
gregs_len = sizeof(gregset_t);
|
|
|
|
/* Make sure to also count the padding bytes */
|
|
filesize = PAD_LEN(mei_len) + PAD_LEN(gregs_len) +
|
|
2 * sizeof(Elf_Nhdr) + 2 * PAD_LEN(name_len);
|
|
fill_prog_header(&phdrs[0], PT_NOTE, 0, 0, PF_R, filesize, 0);
|
|
|
|
/* First note entry header */
|
|
nhdrs[0].n_namesz = name_len;
|
|
nhdrs[0].n_descsz = sizeof(minix_elfcore_info_t);
|
|
nhdrs[0].n_type = NT_MINIX_ELFCORE_INFO;
|
|
|
|
/* Second note entry header */
|
|
nhdrs[1].n_namesz = name_len;
|
|
nhdrs[1].n_descsz = sizeof(gregset_t);
|
|
nhdrs[1].n_type = NT_MINIX_ELFCORE_GREGS;
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* adjust_offset *
|
|
*===========================================================================*/
|
|
static void adjust_offsets(Elf_Phdr phdrs[], int phnum)
|
|
{
|
|
int i;
|
|
long offset = sizeof(Elf_Ehdr) + phnum * sizeof(Elf_Phdr);
|
|
|
|
for (i = 0; i < phnum; i++) {
|
|
phdrs[i].p_offset = offset;
|
|
offset += phdrs[i].p_filesz;
|
|
}
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* write_buf *
|
|
*===========================================================================*/
|
|
static void write_buf(struct filp *f, char *buf, size_t size)
|
|
{
|
|
read_write(fp, WRITING, f, buf, size, VFS_PROC_NR);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* get_memory_regions *
|
|
*===========================================================================*/
|
|
static int get_memory_regions(Elf_Phdr phdrs[])
|
|
{
|
|
/* Print the virtual memory regions of a process. */
|
|
|
|
/* The same as dump_regions from procfs/pid.c */
|
|
struct vm_region_info vri[MAX_VRI_COUNT];
|
|
vir_bytes next;
|
|
int i, r, count;
|
|
Elf_Word pflags;
|
|
|
|
count = 0;
|
|
next = 0;
|
|
|
|
do {
|
|
r = vm_info_region(fp->fp_endpoint, vri, MAX_VRI_COUNT, &next);
|
|
if (r < 0) return r;
|
|
if (r == 0) break;
|
|
|
|
for (i = 0; i < r; i++) {
|
|
pflags = (vri[i].vri_prot & PROT_READ ? PF_R : 0)
|
|
| (vri[i].vri_prot & PROT_WRITE ? PF_W : 0)
|
|
| (vri[i].vri_prot & PROT_EXEC ? PF_X : 0);
|
|
|
|
fill_prog_header (&phdrs[count + 1], PT_LOAD,
|
|
0, vri[i].vri_addr, pflags,
|
|
vri[i].vri_length, vri[i].vri_length);
|
|
count++;
|
|
|
|
if (count >= MAX_REGIONS) {
|
|
printf("VFS: get_memory_regions Warning: "
|
|
"Program has too many regions\n");
|
|
return(count);
|
|
}
|
|
}
|
|
} while (r == MAX_VRI_COUNT);
|
|
|
|
return(count);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* dump_notes *
|
|
*===========================================================================*/
|
|
static void dump_notes(struct filp *f, Elf_Nhdr nhdrs[], int csig,
|
|
char *proc_name)
|
|
{
|
|
char *note_name = ELF_NOTE_MINIX_ELFCORE_NAME "\0";
|
|
char pad[4];
|
|
minix_elfcore_info_t mei;
|
|
int mei_len = sizeof(minix_elfcore_info_t);
|
|
int gregs_len = sizeof(gregset_t);
|
|
struct stackframe_s regs;
|
|
|
|
/* Dump first note entry */
|
|
mei.mei_version = MINIX_ELFCORE_VERSION;
|
|
mei.mei_meisize = mei_len;
|
|
mei.mei_signo = csig;
|
|
mei.mei_pid = fp->fp_pid;
|
|
memcpy(mei.mei_command, proc_name, sizeof(mei.mei_command));
|
|
|
|
write_buf(f, (char *) &nhdrs[0], sizeof(Elf_Nhdr));
|
|
write_buf(f, note_name, nhdrs[0].n_namesz);
|
|
write_buf(f, pad, PAD_LEN(nhdrs[0].n_namesz) - nhdrs[0].n_namesz);
|
|
write_buf(f, (char *) &mei, mei_len);
|
|
write_buf(f, pad, PAD_LEN(mei_len) - mei_len);
|
|
|
|
/* Get registers */
|
|
if (sys_getregs(®s, fp->fp_endpoint) != OK)
|
|
printf("VFS: Could not read registers\n");
|
|
|
|
if (sizeof(regs) != gregs_len)
|
|
printf("VFS: Wrong core register structure size\n");
|
|
|
|
/* Dump second note entry - the general registers */
|
|
write_buf(f, (char *) &nhdrs[1], sizeof(Elf_Nhdr));
|
|
|
|
write_buf(f, note_name, nhdrs[1].n_namesz);
|
|
write_buf(f, pad, PAD_LEN(nhdrs[1].n_namesz) - nhdrs[1].n_namesz);
|
|
write_buf(f, (char *) ®s, gregs_len);
|
|
write_buf(f, pad, PAD_LEN(gregs_len) - gregs_len);
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* dump_elf_header *
|
|
*===========================================================================*/
|
|
static void dump_elf_header(struct filp *f, Elf_Ehdr elf_header)
|
|
{
|
|
write_buf(f, (char *) &elf_header, sizeof(Elf_Ehdr));
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* dump_program_headers *
|
|
*===========================================================================*/
|
|
static void dump_program_headers(struct filp *f, Elf_Phdr phdrs[], int phnum)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < phnum; i++)
|
|
write_buf(f, (char *) &phdrs[i], sizeof(Elf_Phdr));
|
|
}
|
|
|
|
/*===========================================================================*
|
|
* dump_segments *
|
|
*===========================================================================*/
|
|
static void dump_segments(struct filp *f, Elf_Phdr phdrs[], int phnum)
|
|
{
|
|
int i;
|
|
vir_bytes len;
|
|
off_t off, seg_off;
|
|
int r;
|
|
static u8_t buf[CLICK_SIZE];
|
|
|
|
for (i = 1; i < phnum; i++) {
|
|
len = phdrs[i].p_memsz;
|
|
seg_off = phdrs[i].p_vaddr;
|
|
|
|
if (len > LONG_MAX) {
|
|
printf("VFS: segment too large to dump, truncating\n");
|
|
len = LONG_MAX;
|
|
}
|
|
|
|
for (off = 0; off < (off_t) len; off += CLICK_SIZE) {
|
|
vir_bytes p = (vir_bytes) (seg_off + off);
|
|
r = sys_vircopy(fp->fp_endpoint, p,
|
|
SELF, (vir_bytes) buf,
|
|
(phys_bytes) CLICK_SIZE);
|
|
|
|
if(r != OK) {
|
|
/* memory didn't exist; write as zeroes */
|
|
memset(buf, 0, sizeof(buf));
|
|
continue;
|
|
}
|
|
|
|
write_buf(f, (char *) buf, (off + CLICK_SIZE <= (off_t) len) ?
|
|
CLICK_SIZE : (len - off));
|
|
}
|
|
}
|
|
}
|