blob: edd2683cad1696166b06020dfaff93d74e88e32a [file] [log] [blame]
// Copyright 2019 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.
#ifndef ZIRCON_BOOTLOADER_SRC_BOOTBYTE_H_
#define ZIRCON_BOOTLOADER_SRC_BOOTBYTE_H_
#include <stdint.h>
// CMOS I/O port
#define RTC_BASE_PORT 0x70u
// CMOS register offset
#define RTC_BOOT_BYTE 48
// flags and fields in RTC_BOOT_BYTE
#define RTC_BOOT_NORMAL 0x1u // boot_option
#define RTC_BOOT_RECOVERY 0x2u // boot_option
#define RTC_BOOT_BOOTLOADER 0x4u // boot_option
#define RTC_BOOT_COUNT_MASK (0xf0u) // reboot_counter field mask
#define RTC_BOOT_COUNT_SHIFT (4) // reboot_counter shift amount
#define RTC_BOOT_COUNT_INITIAL (uint8_t)3 // reboot_counter initial value
// TODO(jonmayo): use a platform API instead of this hack
#if defined(__x86_64__)
static inline uint8_t inp(uint16_t _port) {
uint8_t rv;
__asm__ __volatile__("inb %1, %0" : "=a"(rv) : "dN"(_port));
return (rv);
}
static inline void outp(uint16_t _port, uint8_t _data) {
__asm__ __volatile__("outb %1, %0" : : "dN"(_port), "a"(_data));
}
static inline void rtc_write(unsigned char addr, unsigned char value) {
uint16_t ofs;
if (addr < 128) {
ofs = 0;
} else {
ofs = 2;
addr -= 128;
}
outp(RTC_BASE_PORT + ofs, addr);
outp(RTC_BASE_PORT + ofs + 1, value);
}
static inline unsigned char rtc_read(unsigned char addr) {
uint16_t ofs;
if (addr < 128) {
ofs = 0;
} else {
ofs = 2;
addr -= 128;
}
outp(RTC_BASE_PORT + ofs, addr);
return inp(RTC_BASE_PORT + ofs + 1);
}
static inline unsigned char bootbyte_read(void) { return rtc_read(RTC_BOOT_BYTE); }
static inline void bootbyte_clear(void) { rtc_write(RTC_BOOT_BYTE, 0xffu); }
static inline void bootbyte_set_normal(void) { rtc_write(RTC_BOOT_BYTE, RTC_BOOT_NORMAL); }
static inline void bootbyte_set_recovery(void) { rtc_write(RTC_BOOT_BYTE, RTC_BOOT_RECOVERY); }
static inline int bootbyte_decrement(void) {
uint8_t boot_byte = rtc_read(RTC_BOOT_BYTE);
unsigned reboot_count = (boot_byte & RTC_BOOT_COUNT_MASK) >> RTC_BOOT_COUNT_SHIFT;
// do not decrement on a count of 0 or 15.
// 0 means count down ended, 15 means uninitialized/invalid.
if (reboot_count > 0u && reboot_count < (RTC_BOOT_COUNT_MASK >> RTC_BOOT_COUNT_SHIFT)) {
// decrement top nibble
boot_byte &= ~RTC_BOOT_COUNT_MASK;
boot_byte |= (reboot_count - 1) << RTC_BOOT_COUNT_SHIFT;
rtc_write(RTC_BOOT_BYTE, boot_byte);
return 0; // success
}
return -1; // failure
}
#elif defined(__aarch64__)
// TODO(jonmayo): add support for aarch64. currently stubbed out with dummy behavior
static inline unsigned char bootbyte_read(void) { return 0xff; }
static inline void bootbyte_clear(void) {}
static inline void bootbyte_set_normal(void) {}
static inline void bootbyte_set_recovery(void) {}
static inline int bootbyte_decrement(void) { return 0; }
#else
#error "add code for other arches here"
#endif
#endif // ZIRCON_BOOTLOADER_SRC_BOOTBYTE_H_