blob: 1bdd83bae5a1db84b377675b91a34e0a9a109398 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <string.h>
#include <time.h>
#include <hypervisor/address.h>
#include <hypervisor/io_port.h>
#include <zircon/syscalls/hypervisor.h>
#include <zircon/syscalls/port.h>
#include "acpi_priv.h"
// clang-format off
/* PIC configuration constants. */
#define PIC_INVALID UINT8_MAX
/* RTC register addresses. */
#define RTC_REGISTER_SECONDS 0u
#define RTC_REGISTER_MINUTES 2u
#define RTC_REGISTER_HOURS 4u
#define RTC_REGISTER_DAY_OF_MONTH 7u
#define RTC_REGISTER_MONTH 8u
#define RTC_REGISTER_YEAR 9u
#define RTC_REGISTER_A 10u
#define RTC_REGISTER_B 11u
/* RTC register B flags. */
#define RTC_REGISTER_B_DAYLIGHT_SAVINGS (1u << 0)
#define RTC_REGISTER_B_HOUR_FORMAT (1u << 1)
/* I8042 status flags. */
#define I8042_STATUS_OUTPUT_FULL (1u << 0)
#define I8042_STATUS_INPUT_FULL (1u << 1)
/* I8042 test constants. */
#define I8042_COMMAND_TEST 0xaa
#define I8042_DATA_TEST_RESPONSE 0x55
// clang-format on
void io_port_init(io_port_t* io_port) {
memset(io_port, 0, sizeof(*io_port));
}
static uint8_t to_bcd(int binary) {
return static_cast<uint8_t>(((binary / 10) << 4) | (binary % 10));
}
static zx_status_t handle_rtc(uint8_t rtc_index, uint8_t* value) {
time_t now = time(NULL);
struct tm tm;
if (localtime_r(&now, &tm) == NULL)
return ZX_ERR_INTERNAL;
switch (rtc_index) {
case RTC_REGISTER_SECONDS:
*value = to_bcd(tm.tm_sec);
break;
case RTC_REGISTER_MINUTES:
*value = to_bcd(tm.tm_min);
break;
case RTC_REGISTER_HOURS:
*value = to_bcd(tm.tm_hour);
break;
case RTC_REGISTER_DAY_OF_MONTH:
*value = to_bcd(tm.tm_mday);
break;
case RTC_REGISTER_MONTH:
*value = to_bcd(tm.tm_mon);
break;
case RTC_REGISTER_YEAR: {
// RTC expects the number of years since 2000.
int year = tm.tm_year - 100;
if (year < 0)
year = 0;
*value = to_bcd(year);
break;
}
case RTC_REGISTER_A:
// Ensure that UIP is 0. Other values (clock frequency) are obsolete.
*value = 0;
break;
case RTC_REGISTER_B:
*value = RTC_REGISTER_B_HOUR_FORMAT;
if (tm.tm_isdst)
*value |= RTC_REGISTER_B_DAYLIGHT_SAVINGS;
break;
default:
return ZX_ERR_NOT_SUPPORTED;
}
return ZX_OK;
}
zx_status_t io_port_read(const io_port_t* io_port, uint16_t port, zx_vcpu_io_t* vcpu_io) {
#ifdef __x86_64__
switch (port) {
case RTC_DATA_PORT: {
vcpu_io->access_size = 1;
mtx_lock((mtx_t*)&io_port->mutex);
uint8_t rtc_index = io_port->rtc_index;
mtx_unlock((mtx_t*)&io_port->mutex);
return handle_rtc(rtc_index, &vcpu_io->u8);
}
case I8042_DATA_PORT:
vcpu_io->access_size = 1;
mtx_lock((mtx_t*)&io_port->mutex);
vcpu_io->u8 = io_port->i8042_command == I8042_COMMAND_TEST ? I8042_DATA_TEST_RESPONSE : 0;
mtx_unlock((mtx_t*)&io_port->mutex);
break;
case I8042_COMMAND_PORT:
vcpu_io->access_size = 1;
vcpu_io->u8 = I8042_STATUS_OUTPUT_FULL;
break;
case PM1_EVENT_PORT + PM1A_REGISTER_STATUS:
vcpu_io->access_size = 2;
vcpu_io->u16 = 0;
break;
case PM1_EVENT_PORT + PM1A_REGISTER_ENABLE:
vcpu_io->access_size = 2;
mtx_lock((mtx_t*)&io_port->mutex);
vcpu_io->u16 = io_port->pm1_enable;
mtx_unlock((mtx_t*)&io_port->mutex);
break;
case PIC1_DATA_PORT:
vcpu_io->access_size = 1;
vcpu_io->u8 = PIC_INVALID;
break;
default:
return ZX_ERR_NOT_SUPPORTED;
}
return ZX_OK;
#else // __x86_64__
return ZX_ERR_NOT_SUPPORTED;
#endif // __x86_64__
}
zx_status_t io_port_write(io_port_t* io_port, const zx_packet_guest_io_t* io) {
#ifdef __x86_64__
switch (io->port) {
case I8042_DATA_PORT:
case I8253_CHANNEL_0:
case I8253_CONTROL_PORT:
case PIC1_COMMAND_PORT ... PIC1_DATA_PORT:
case PIC2_COMMAND_PORT ... PIC2_DATA_PORT:
case PM1_EVENT_PORT + PM1A_REGISTER_STATUS:
break;
case I8042_COMMAND_PORT:
if (io->access_size != 1)
return ZX_ERR_IO_DATA_INTEGRITY;
mtx_lock(&io_port->mutex);
io_port->i8042_command = io->u8;
mtx_unlock(&io_port->mutex);
break;
case PM1_EVENT_PORT + PM1A_REGISTER_ENABLE:
if (io->access_size != 2)
return ZX_ERR_IO_DATA_INTEGRITY;
mtx_lock(&io_port->mutex);
io_port->pm1_enable = io->u16;
mtx_unlock(&io_port->mutex);
break;
case RTC_INDEX_PORT:
if (io->access_size != 1)
return ZX_ERR_IO_DATA_INTEGRITY;
mtx_lock(&io_port->mutex);
io_port->rtc_index = io->u8;
mtx_unlock(&io_port->mutex);
break;
default:
return ZX_ERR_NOT_SUPPORTED;
}
return ZX_OK;
#else // __x86_64__
return ZX_ERR_NOT_SUPPORTED;
#endif // __x86_64__
}