| // 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_ |