static Rsdp * acpi_find_rsdp(void) { Rsdp *rsdp; uint32_t ebda; ebda = *(uint16_t *) pa2kva(0x40E); ebda = ebda * 0x10 & 0xFFFFF; rsdp = __acpi_find_rsdp(pa2kva(ebda), 0x1000); if (!rsdp) rsdp = __acpi_find_rsdp(pa2kva(0xE0000), 0x20000); return rsdp; }
static int acpi_parse_madt(Madt *madt) { uint8_t *p, *e; lapic = (uint8_t *) pa2kva(madt->lapic_address); e = (uint8_t *) madt + madt->header.length; for (p = (uint8_t *) (madt + 1); p < e; p += p[1]) { switch (p[0]) { case 1: // IO APIC structure if (ioapic) warn("more than one ioapic"); else { ioapic = pa2kva(((Io_apic *) p)->io_apic_address); } break; } } return 0; }
void acpi_init(void) { Rsdp *rsdp; rsdp = acpi_find_rsdp(); if (!rsdp) panic("acpi_init: cannot find rsdp"); kprintf("ACPI: RSDP %#lx %08x <v%02u %.*s>\n", kva2pa(rsdp), sizeof(*rsdp), rsdp->revision + 1, sizeof(rsdp->oemid), rsdp->oemid); acpi_parse_rsdt(pa2kva(rsdp->rsdt_address)); }
int dwarf_rip_debug_info(uintptr_t rip, struct Rip_debug_info *info) { // these symbols are defined in core/core.ld extern uint8_t _debug_aranges_begin[], _debug_aranges_end[]; info->file_name = "<unknown>"; info->fn_name = "<unknown>"; info->fn_offset = 0; if (_debug_aranges_end <= _debug_aranges_begin) // no debugging info? return -1; // Search the table of address ranges for the instruction pointer uint8_t *p = _debug_aranges_begin; while (p < _debug_aranges_end) { // read the header uint32_t length = extract_uint(p, 4, &p); uint8_t *e = p + length; extract_uint(p, 2, &p); // version uint32_t offset = extract_uint(p, 4, &p); extract_uint(p, 1, &p); // address size extract_uint(p, 1, &p); // segment size // the first touple begins at an offset that is a multiple of the size // of a single tuple (i.e. twice the size of an address) p += 4; while (p < e) { uint64_t range_addr = extract_uint(p, 8, &p); uint64_t range_length = extract_uint(p, 8, &p); //kprintf("%p - %p\n", range_addr, range_addr + range_length); // each set of tuples is terminated by a 0 for the address and 0 // for the length if (range_addr == 0 && range_length == 0) break; if (rip >= range_addr && rip < (range_addr + range_length)) // the instruction pointer belongs to this compile unit return parse_cu(pa2kva(offset), rip, info); } } return 0; }
static int acpi_parse_rsdt(Rsdt *rsdt) { unsigned nentries; uint32_t *p; if (memcmp(rsdt->header.signature, "RSDT", 4) != 0 || acpi_checksum8(rsdt, rsdt->header.length) != 0) { warn("ACPI: invalid RSDT"); return -1; } acpi_print_sdt_header(&rsdt->header); nentries = (rsdt->header.length - sizeof(rsdt->header)) / sizeof(rsdt->entries[0]); for (p = rsdt->entries; p < &rsdt->entries[nentries]; p++) { Sdt_header *header; int r; header = pa2kva(*p); if (acpi_checksum8(header, header->length)) { warn("invalid SDT header"); return -1; } acpi_print_sdt_header(header); if (!memcmp(header->signature, "FACP", 4)) r = acpi_parse_fadt((Fadt *) header); else if (!memcmp(header->signature, "APIC", 4)) r = acpi_parse_madt((Madt *) header); if (r < 0) return r; } kprintf("ACPI: all tables successfully acquired\n"); return 0; }
// Parse the compile unit. Find the function to which the instruction pointer // belongs and fill the info structure. static int parse_cu(uint8_t *p, uintptr_t rip, struct Rip_debug_info *info) { // read the compile unit header uint32_t length = extract_uint(p, 4, &p); uint8_t *e = p + length; extract_uint(p, 2, &p); // version uint32_t abbr_offset = extract_uint(p, 4, &p); extract_uint(p, 1, &p); // address size while (p < e) { uint64_t code; // skip null DIEs do { code = extract_uleb128(p, &p); } while (code == 0 && p < e); if (p >= e) break; // Search the abbreviation table for the declaration with this code uint8_t *abbr = find_abbrev_decl(pa2kva(abbr_offset), code); uint64_t tag = extract_uleb128(abbr, &abbr); extract_uint(abbr, 1, &abbr); // skip the has_children tag // read the attributes // we're interested only in several tags and attributes uintptr_t fn_lo = 0, fn_hi = 0; char *fn_name = NULL; for (;;) { uint64_t name = extract_uleb128(abbr, &abbr); uint64_t form = extract_uleb128(abbr, &abbr); union Attr_val val; if (name == 0 && form == 0) break; // extract the attribute value if (extract_value(p, form, &val, &p) == -1) return -1; if (tag == DW_TAG_compile_unit && name == DW_AT_name) { info->file_name = val.string; } else if (tag == DW_TAG_subprogram) { if (name == DW_AT_name) fn_name = val.string; else if (name == DW_AT_low_pc) fn_lo = val.number; else if (name == DW_AT_high_pc) fn_hi = val.number; } } if (tag == DW_TAG_subprogram) { // if the recently parsed tag was a function tag... if (rip >= fn_lo && rip <= fn_hi) { // ... and the instruction pointer belongs to this function if (fn_name) info->fn_name = fn_name; info->fn_offset = rip - fn_lo; } } } return 0; }
// Extract the attribute value of the given form static int extract_value(uint8_t *p, uint64_t form, union Attr_val *value, uint8_t **e) { switch (form) { case DW_FORM_data1: case DW_FORM_ref1: case DW_FORM_flag: value->number = extract_uint(p, 1, &p); break; case DW_FORM_data2: case DW_FORM_ref2: value->number = extract_uint(p, 2, &p); break; case DW_FORM_data4: case DW_FORM_ref4: value->number = extract_uint(p, 4, &p); break; case DW_FORM_data8: case DW_FORM_ref8: case DW_FORM_addr: case DW_FORM_ref_addr: value->number = extract_uint(p, 8, &p); break; case DW_FORM_sdata: value->number = extract_sleb128(p, &p); break; case DW_FORM_udata: case DW_FORM_ref_udata: value->number = extract_uleb128(p, &p); break; case DW_FORM_string: value->string = (char *) p; while (*p++) ; break; case DW_FORM_strp: value->string = (char *) pa2kva(extract_uint(p, 4, &p)); break; case DW_FORM_block1: value->block.length = extract_uint(p, 1, &p); value->block.data = (char *) p; p += value->block.length; break; case DW_FORM_block2: value->block.length = extract_uint(p, 2, &p); value->block.data = (char *) p; p += value->block.length; break; case DW_FORM_block4: value->block.length = extract_uint(p, 4, &p); value->block.data = (char *) p; p += value->block.length; break; case DW_FORM_block: value->block.length = extract_uleb128(p, &p); value->block.data = (char *) p; p += value->block.length; break; case DW_FORM_indirect: form = extract_uleb128(p, &p); return extract_value(p, form, value, &p); default: kprintf("uknown attribute form: %x\n", form); return -1; } if (e) *e = p; return 0; }
/* * sys_disk_request * * Disk I/O without going through the buffer cache. * * xn_user is the name of a pxn that grants access to the disk * reqbp is a list of scatter/gather requests * k is which capability in the env should be checked * * permission is granted to perform the operation if: * 1) the blocks in reqbp are covered by the pxn * 2) the capability gives access to the pxn * */ int sys_disk_request (u_int sn, struct Xn_name *xn_user, struct buf *reqbp, u_int k) { struct Xn_name xn; struct Xn_xtnt xtnt; struct Pxn *pxn; cap c; int ret; int access; struct disk *di; int *resptr = NULL; u_int bcount = 0; struct buf *bp, *segbp, *nsegbp; int noncontigs = 0, nctemp; #ifdef MEASURE_DISK_TIMES disk_pctr_start = rdtsc(); #endif /* XXX - use PFM or copyin instead of isreadable_* */ /* bypass for direct scsi commands */ if (reqbp->b_flags & B_SCSICMD) { return sys_disk_scsicmd (sn, k, reqbp); } /* get the capability */ if ((ret = env_getcap (curenv, k, &c)) < 0) return ret; /* and the pxn */ copyin (xn_user, &xn, sizeof (xn)); if (! (pxn = lookup_pxn (&xn))) { warn ("sys_disk_request: no pxn found"); return (-E_NOT_FOUND); } /* XXX - do we need to check that this is a physical disk? */ /* get a refernce to the disk unit for this command */ di = &(si->si_disks[xn.xa_dev]); /* Iterate over the request list checking: -- if the request is transfering data to/from memory that this user can read/write. -- if the pxn and capability specified give access to these blocks */ for (segbp = reqbp; ; segbp = (struct buf *) segbp->b_sgnext) { if (! (isreadable_varange ((u_int)segbp, sizeof(struct buf)))) { warn ("sys_disk_request: bad reqbp (%p)", segbp); return (-E_FAULT); } if (segbp->b_flags & B_READ) { access = ACL_R; } else { access = ACL_W; } xtnt.xtnt_block = segbp->b_blkno; xtnt.xtnt_size = segbp->b_bcount / di->d_bsize; bcount += segbp->b_bcount; if (! pxn_authorizes_xtnt (pxn, &c, &xtnt, access, &ret)) { warn ("sys_disk_request: pxn/cap does not grant access to block(s)"); return ret; } if (! ((reqbp->b_flags & B_READ) ? iswriteable_varange : isreadable_varange) ((u_int) segbp->b_memaddr, segbp->b_bcount)) { warn ("sys_disk_request: bad b_memaddr: %p (b_bcount %d)", segbp->b_memaddr, segbp->b_bcount); return (-E_FAULT); } if (! (segbp->b_flags & B_SCATGATH)) { if (segbp->b_resptr) { resptr = segbp->b_resptr; if ((((u_int) resptr) % sizeof(u_int)) || !(isvawriteable (resptr))) { warn ("sys_disk_request: bad resptr (%p)", resptr); return (-E_FAULT); } resptr = (int *) pa2kva (va2pa (resptr)); } break; } } if ((reqbp->b_flags & B_SCATGATH) && bcount != reqbp->b_sgtot) { warn ("sys_disk_request: invalid scatter/gather, with total (%u) unequal " "to sum of parts (%u)", reqbp->b_sgtot, bcount); return (-E_INVAL); } /* are we done before we've started? */ if (bcount == 0) { if (resptr) (*resptr)--; return (0); } if (bcount & di->d_bmod) { warn ("sys_disk_request: bad bcount %u", bcount); return (-E_INVAL); } /* copy request into kernel buffer */ segbp = reqbp; nsegbp = NULL; reqbp = NULL; do { segbp->b_dev = di->d_id; bp = copy_and_pin(segbp, segbp->b_bcount, &nctemp); if (!bp) { warn ("sys_disk_request: could not copy_and_pin"); /* XXX - cleanup before returning */ return (-E_NO_MEM); } noncontigs += nctemp; if (nsegbp) nsegbp->b_sgnext = bp; if (!reqbp) reqbp = bp; if (noncontigs >= DISK_MAX_SCATTER) { warn ("sys_disk_request: would require too many scatter/gather entries " "(%d)", noncontigs); /* XXX - cleanup before returning */ return (-E_INVAL); } nsegbp = bp; segbp = segbp->b_sgnext; } while (nsegbp->b_flags & B_SCATGATH); nsegbp->b_resptr = resptr; if (resptr) ppage_pin (kva2pp((u_int) resptr)); /* call appropriate strategy routine */ di->d_strategy (reqbp); #ifdef MEASURE_DISK_TIMES disk_pctr_return = rdtsc(); #endif return (0); }
/* * sys_disk_mbr * * Read/Write the master boot record of a disk. * * The mbr contains the bootstrap code that the BIOS * loads on startup. This is always in sector 0 * of the disk being booted. The mbr also contains the * partition table for the disk. * */ int sys_disk_mbr (u_int sn, int write, u_int dev, int k, char *buffer, int *resptr) { cap c; int ret; struct buf *diskbuf; /* get the capability */ if ((ret = env_getcap (curenv, k, &c)) < 0) return ret; /* make sure the root cap was passed in */ if (!cap_isroot (&c)) return -E_CAP_INSUFF; /* verify the dev */ if (dev >= si->si_ndisks) return -E_NOT_FOUND; /* check and translate the buffers we were given */ if ((((u_int) resptr) % sizeof(u_int)) || !(isvawriteable (resptr))) { warn ("sys_disk_mrb: bad resptr (%p)", resptr); return (-E_FAULT); } ppage_pin (pa2pp ((va2pa (resptr)))); resptr = (int *) pa2kva (va2pa (resptr)); if (write) { if (! (iswriteable_varange ((u_int)buffer, 512))) { warn ("sys_disk_mbr: bad buffer (%p)", buffer); return (-E_FAULT); } } else { if (! (isreadable_varange ((u_int)buffer, 512))) { warn ("sys_disk_mbr: bad buffer (%p)", buffer); return (-E_FAULT); } } /* get a disk req buffer and fill it in */ diskbuf = disk_buf_alloc (); if (!diskbuf) return -E_NO_MEM; diskbuf->b_next = NULL; diskbuf->b_sgnext = NULL; diskbuf->b_dev = dev; diskbuf->b_blkno = 0; diskbuf->b_bcount = 512; /* only want to read the first sector */ diskbuf->b_sgtot = 512; diskbuf->b_memaddr = buffer; diskbuf->b_envid = curenv->env_id; diskbuf->b_resid = 0; diskbuf->b_resptr = resptr; diskbuf->b_flags = B_ABSOLUTE; /* bypass partitions table */ if (write) { diskbuf->b_flags |= B_WRITE; } else { diskbuf->b_flags |= B_READ; } /* pin it in case the user frees it before the request completes. This will be unpinned when sched_reqcomplete is called which in turn calls disk_buf_free which calls ppage_unpin. */ ppage_pin (pa2pp ((va2pa (buffer)))); /* start the request */ si->si_disks[dev].d_strategy (diskbuf); return 0; }
/* XXX - we should use copyin, etc, instead of isreadable_* so that user will get pagefaults he can handle transparently */ static int sys_disk_scsicmd (u_int sn, u_int k, struct buf *reqbp) { struct buf *bp; struct scsicmd *scsicmd = (struct scsicmd *) reqbp->b_memaddr; struct scsicmd *scsicmd2; int noncontigs; struct disk *di; /* must have root capability for system to do a raw SCSI command!! */ /* XXX -- later, if desired, deeper checking of validity can reduce */ /* this restriction... */ if (k >= curenv->env_clen || ! curenv->env_clist[k].c_valid) { warn ("sys_disk_scsicmd: bad capability number %u\n", k); return (-E_CAP_INVALID); } if (! cap_isroot(&curenv->env_clist[k])) { warn ("sys_disk_scsicmd: cap %u is not root capability for system\n", k); return (-E_CAP_INSUFF); } /* must be able to read the reqbp ... */ if (! (isreadable_varange ((u_int) reqbp, sizeof (struct buf)))) { warn ("sys_disk_scsicmd: bad reqbp (%p)", reqbp); return (-E_FAULT); } /* Should be a SCSICMD */ if (! (reqbp->b_flags & B_SCSICMD)) { warn ("sys_disk_scsicmd: not a B_SCSICMD\n"); return (-E_INVAL); } /* Must be proper environment */ if (reqbp->b_envid != curenv->env_id) { warn ("sys_disk_scsicmd: bad envid\n"); return (-E_INVAL); } /* no scatter/gather support for raw SCSI commands */ if (reqbp->b_flags & B_SCATGATH) { warn ("sys_disk_scsicmd: B_SCATGATH not allowed with B_SCSICMD\n"); return (-E_INVAL); } /* can't send request to non-existent disk... */ if (reqbp->b_dev >= si->si_ndevs) { warn ("sys_disk_scsicmd: there is no disk %u in system\n", reqbp->b_dev); return (-E_NOT_FOUND); } /* check that everything is readable */ if (! isreadable_varange ((u_int) reqbp->b_memaddr, sizeof (struct scsicmd))) { warn ("sys_disk_scsicmd: SCSI command description is not readable\n"); return (-E_FAULT); } if (! isreadable_varange ((u_int) scsicmd->scsi_cmd, scsicmd->cmdlen) ) { warn ("sys_disk_scsicmd: SCSI command itself is not readable\n"); return (-E_FAULT); } if (! isreadable_varange ((u_int)scsicmd->data_addr, scsicmd->datalen) ) { warn ("sys_disk_scsicmd: data area for SCSI command is not readable\n"); return (-E_FAULT); } /* length of SCSI command must not be greater than B_SCSICMD_MAXLEN */ if (scsicmd->cmdlen > B_SCSICMD_MAXLEN) { /* XXX - why do we compare scsicmd->cmdlen, but we print out reqbp->b_bcount? */ warn ("sys_disk_scsicmd: specified SCSI command too large (%d > %d)\n", reqbp->b_bcount, B_SCSICMD_MAXLEN); return (-E_INVAL); } /* copy the SCSI command to avoid sharing it with app */ bp = bp_copy (reqbp); if (bp == NULL) { warn ("sys_disk_scsicmd: kernel malloc for bp failed\n"); return (-E_NO_MEM); } bp->b_memaddr = malloc (sizeof (struct scsicmd)); if (bp->b_memaddr == NULL) { warn ("sys_disk_scsicmd: kernel malloc for scsicmd failed\n"); free (bp); return (-E_NO_MEM); } scsicmd2 = (struct scsicmd *) bp->b_memaddr; bcopy (scsicmd, scsicmd2, sizeof (struct scsicmd)); scsicmd2->scsi_cmd = (struct scsi_generic *) malloc (scsicmd->cmdlen); if (scsicmd2->scsi_cmd == NULL) { warn ("sys_disk_scsicmd: second kernel malloc failed\n"); free (bp->b_memaddr); free (bp); return (-E_NO_MEM); } bcopy (scsicmd->scsi_cmd, scsicmd2->scsi_cmd, scsicmd->cmdlen); scsicmd2->bp = bp; bp->b_resid = scsicmd->datalen; bp->b_resptr = (int *) pa2kva (va2pa (reqbp->b_resptr)); /* pin down the app pages that will later be used by the driver */ ppage_pin (kva2pp ((u_int) bp->b_resptr)); noncontigs = pin_and_count_noncontigs (scsicmd2->data_addr, scsicmd2->datalen); if (noncontigs >= DISK_MAX_SCATTER) { warn ("sys_disk_scsicmd: will require too many scatter/gather entries " "(%d)", noncontigs); disk_buf_free (bp); return (-E_TOO_BIG); } /* XXX */ /* call down to the low-level driver. GROK -- since the partition stuff */ /* creates and abstract disk that is separate from the real one, a hack */ /* is needed to get the actual disk strategy routine for raw SCSI commands */ /* This is fine as long as all disks actually go to the same strategy */ /* routine. */ di = &(si->si_disks[0]); di->d_strategy (bp); return (0); }
static msgringent * msgringent_setup (msgringent * u_msgringent) { msgringent *ktmp; Pte *pte = NULL; int scatptr = 0; int total_len = 0; ktmp = (msgringent *) malloc (sizeof (msgringent)); if (ktmp == NULL) { warn ("msgringent_setup: failed malloc"); return (NULL); } ktmp->appaddr = u_msgringent; ktmp->owner = NULL; ktmp->body.n = 0; /* Verify and translate owner field */ if ((((u_int) u_msgringent->owner % sizeof (int)) || ! (pte = va2ptep ((u_int) u_msgringent->owner)) || ((*pte & WRITE_MASK) != WRITE_MASK))) { warn ("msgringent_setup: owner field failed\n"); msgringent_free (ktmp); return (NULL); } ktmp->owner = (u_int *) pa2kva (va2pa (u_msgringent->owner)); ppage_pin (kva2pp ((u_long) ktmp->owner)); /* Verify and translate data field */ if (u_msgringent->body.n > 1) { warn ("msgringent_setup: not allowed to setup disjoint message body\n"); msgringent_free (ktmp); return (NULL); } scatptr = 0; total_len = 0; { int len = u_msgringent->body.r[0].sz; caddr_t addr = u_msgringent->body.r[0].data; u_int pagebound = NBPG-(((u_long)addr)&(NBPG - 1)); while (len > 0) { u_int slen = min (len, pagebound); if (!(pte = va2ptep ((u_int) addr)) || ((*pte & READ_MASK) != READ_MASK)) { /* physical page is not accessible */ warn ("msgringent_setup: can't read scatter ptr\n"); msgringent_free (ktmp); return (NULL); } ktmp->body.r[scatptr].data = (char *) pa2kva (va2pa (addr)); ktmp->body.r[scatptr].sz = slen; ktmp->body.n++; /* pin the page to prevent re-allocation */ ppage_pin (kva2pp ((u_long) ktmp->body.r[scatptr].data)); len -= slen; addr += slen; total_len += slen; pagebound = NBPG; scatptr++; if (scatptr > IPC_MAX_SCATTER_PTR || total_len > IPC_MAX_MSG_SIZE) { msgringent_free (ktmp); warn ("msgringent_setup: message body too big\n"); return (NULL); } } } return (ktmp); }