// Finish processing of the transition to State::DEAD. Some things need to be done // outside of holding |get_lock()|. Beware this is called from several places // including on_zero_handles(). void ProcessDispatcher::FinishDeadTransition() { DEBUG_ASSERT(!completely_dead_); completely_dead_ = true; // clean up the handle table LTRACEF_LEVEL(2, "cleaning up handle table on proc %p\n", this); fbl::DoublyLinkedList<Handle*> to_clean; { Guard<fbl::Mutex> guard{&handle_table_lock_}; for (auto& handle : handles_) { handle.set_process_id(ZX_KOID_INVALID); } to_clean.swap(handles_); } // zx-1544: Here is where if we're the last holder of a handle of one of // our exception ports then ResetExceptionPort will get called (by // ExceptionPort::OnPortZeroHandles) and will need to grab |get_lock()|. // This needs to be done outside of |get_lock()|. while (!to_clean.is_empty()) { // Delete handle via HandleOwner dtor. HandleOwner ho(to_clean.pop_front()); } LTRACEF_LEVEL(2, "done cleaning up handle table on proc %p\n", this); // tear down the address space aspace_->Destroy(); // signal waiter LTRACEF_LEVEL(2, "signaling waiters\n"); UpdateState(0u, ZX_TASK_TERMINATED); // The PROC_CREATE record currently emits a uint32_t koid. uint32_t koid = static_cast<uint32_t>(get_koid()); ktrace(TAG_PROC_EXIT, koid, 0, 0, 0); // Call job_->RemoveChildProcess(this) outside of |get_lock()|. Otherwise // we risk a deadlock as we have |get_lock()| and RemoveChildProcess grabs // the job's |lock_|, whereas JobDispatcher::EnumerateChildren obtains the // locks in the opposite order. We want to keep lock acquisition order // consistent, and JobDispatcher::EnumerateChildren's order makes // sense. We don't need |get_lock()| when calling RemoveChildProcess // here. ZX-880 // RemoveChildProcess is called soon after releasing |get_lock()| so that // the semantics of signaling ZX_JOB_NO_PROCESSES match that of // ZX_TASK_TERMINATED. job_->RemoveChildProcess(this); }
static void test_lk_time_to_cntpct(uint32_t cntfrq, lk_time_t lk_time) { uint64_t cntpct = lk_time_to_cntpct(lk_time); uint64_t expected_cntpct = ((uint64_t)cntfrq * lk_time + 500) / 1000; test_time_conversion_check_result(cntpct, expected_cntpct, 1, false); LTRACEF_LEVEL(2, "lk_time_to_cntpct(%u): got %llu, expect %llu\n", lk_time, cntpct, expected_cntpct); }
static uint64_t read_cntpct(void) { uint64_t cntpct; cntpct = READ_TIMER_REG64(TIMER_REG_CT); LTRACEF_LEVEL(3, "cntpct: 0x%016llx, %llu\n", cntpct, cntpct); return cntpct; }
static void test_cntpct_to_lk_bigtime(uint32_t cntfrq, uint64_t expected_s) { lk_bigtime_t expected_lk_bigtime = expected_s * 1000 * 1000; uint64_t cntpct = (uint64_t)cntfrq * expected_s; lk_bigtime_t lk_bigtime = cntpct_to_lk_bigtime(cntpct); test_time_conversion_check_result(lk_bigtime, expected_lk_bigtime, (1000 * 1000 + cntfrq - 1) / cntfrq, false); LTRACEF_LEVEL(2, "cntpct_to_lk_bigtime(%llu): got %llu, expect %llu\n", cntpct, lk_bigtime, expected_lk_bigtime); }
static void test_cntpct_to_lk_time(uint32_t cntfrq, lk_time_t expected_lk_time, uint32_t wrap_count) { lk_time_t lk_time; uint64_t cntpct; cntpct = (uint64_t)cntfrq * expected_lk_time / 1000; if ((uint64_t)cntfrq * wrap_count > UINT_MAX) cntpct += (((uint64_t)cntfrq << 32) / 1000) * wrap_count; else cntpct += (((uint64_t)(cntfrq * wrap_count) << 32) / 1000); lk_time = cntpct_to_lk_time(cntpct); test_time_conversion_check_result(lk_time, expected_lk_time, (1000 + cntfrq - 1) / cntfrq, true); LTRACEF_LEVEL(2, "cntpct_to_lk_time(%llu): got %u, expect %u\n", cntpct, lk_time, expected_lk_time); }
static void write_cntp_tval(int32_t cntp_tval) { LTRACEF_LEVEL(3, "cntp_tval: 0x%08x, %d\n", cntp_tval, cntp_tval); WRITE_TIMER_REG32(TIMER_REG_TVAL, cntp_tval); }
static void write_cntp_cval(uint64_t cntp_cval) { LTRACEF_LEVEL(3, "cntp_cval: 0x%016llx, %llu\n", cntp_cval, cntp_cval); WRITE_TIMER_REG64(TIMER_REG_CVAL, cntp_cval); }
static void write_cntp_ctl(uint32_t cntp_ctl) { LTRACEF_LEVEL(3, "cntp_ctl: 0x%x %x\n", cntp_ctl, read_cntp_ctl()); WRITE_TIMER_REG32(TIMER_REG_CTL, cntp_ctl); }
zx_status_t InputDevice::Init() { LTRACEF("Device %p\n", this); fbl::AutoLock lock(&lock_); // Reset the device and read configuration DeviceReset(); SelectConfig(VIRTIO_INPUT_CFG_ID_NAME, 0); LTRACEF_LEVEL(2, "name %s\n", config_.u.string); SelectConfig(VIRTIO_INPUT_CFG_ID_SERIAL, 0); LTRACEF_LEVEL(2, "serial %s\n", config_.u.string); SelectConfig(VIRTIO_INPUT_CFG_ID_DEVIDS, 0); if (config_.size >= sizeof(virtio_input_devids_t)) { LTRACEF_LEVEL(2, "bustype %d\n", config_.u.ids.bustype); LTRACEF_LEVEL(2, "vendor %d\n", config_.u.ids.vendor); LTRACEF_LEVEL(2, "product %d\n", config_.u.ids.product); LTRACEF_LEVEL(2, "version %d\n", config_.u.ids.version); } SelectConfig(VIRTIO_INPUT_CFG_EV_BITS, VIRTIO_INPUT_EV_KEY); uint8_t cfg_key_size = config_.size; SelectConfig(VIRTIO_INPUT_CFG_EV_BITS, VIRTIO_INPUT_EV_REL); uint8_t cfg_rel_size = config_.size; SelectConfig(VIRTIO_INPUT_CFG_EV_BITS, VIRTIO_INPUT_EV_ABS); uint8_t cfg_abs_size = config_.size; // We only support one of pointer events or key events. In the advent that // the device is trying to present both to us we shall preference the // keyboard events as these are more useful to us. if (cfg_key_size > 0) { // Keyboard dev_class_ = HID_DEVICE_CLASS_KBD; } else if (cfg_rel_size > 0 || cfg_abs_size > 0) { // Pointer dev_class_ = HID_DEVICE_CLASS_POINTER; } else { return ZX_ERR_NOT_SUPPORTED; } DriverStatusAck(); // Plan to clean up unless everything succeeds. auto cleanup = fbl::MakeAutoCall([this]() { Release(); }); // Allocate the main vring zx_status_t status = vring_.Init(0, kEventCount); if (status != ZX_OK) { zxlogf(ERROR, "Failed to allocate vring: %s\n", zx_status_get_string(status)); return status; } // Allocate event buffers for the ring. // TODO: Avoid multiple allocations, allocate enough for all buffers once. for (uint16_t id = 0; id < kEventCount; ++id) { static_assert(sizeof(virtio_input_event_t) <= PAGE_SIZE, ""); status = io_buffer_init(&buffers_[id], bti_.get(), sizeof(virtio_input_event_t), IO_BUFFER_RO | IO_BUFFER_CONTIG); if (status != ZX_OK) { zxlogf(ERROR, "Failed to allocate I/O buffers: %s\n", zx_status_get_string(status)); return status; } } // Expose event buffers to the host vring_desc* desc = nullptr; uint16_t id; for (uint16_t i = 0; i < kEventCount; ++i) { desc = vring_.AllocDescChain(1, &id); if (desc == nullptr) { zxlogf(ERROR, "Failed to allocate descriptor chain\n"); return ZX_ERR_NO_RESOURCES; } ZX_ASSERT(id < kEventCount); desc->addr = io_buffer_phys(&buffers_[id]); desc->len = sizeof(virtio_input_event_t); desc->flags |= VRING_DESC_F_WRITE; LTRACE_DO(virtio_dump_desc(desc)); vring_.SubmitChain(id); } // Prepare the HID report buffer memset(&report_, 0, sizeof(report_)); StartIrqThread(); DriverStatusOk(); device_ops_.release = virtio_input_release; hidbus_ops_.query = virtio_input_query; hidbus_ops_.start = virtio_input_start; hidbus_ops_.stop = virtio_input_stop; hidbus_ops_.get_descriptor = virtio_input_get_descriptor; hidbus_ops_.get_report = virtio_input_get_report; hidbus_ops_.set_report = virtio_input_set_report; hidbus_ops_.get_idle = virtio_input_get_idle; hidbus_ops_.set_idle = virtio_input_set_idle; hidbus_ops_.get_protocol = virtio_input_get_protocol; hidbus_ops_.set_protocol = virtio_input_set_protocol; hidbus_ifc_.ops = nullptr; device_add_args_t args = {}; args.version = DEVICE_ADD_ARGS_VERSION; args.name = "virtio-input"; args.ctx = this; args.ops = &device_ops_; args.proto_id = ZX_PROTOCOL_HIDBUS; args.proto_ops = &hidbus_ops_; status = device_add(bus_device_, &args, &device_); if (status != ZX_OK) { zxlogf(ERROR, "Failed to add device: %s\n", zx_status_get_string(status)); device_ = nullptr; return status; } vring_.Kick(); cleanup.cancel(); return ZX_OK; }