2017年12月21日 星期四

boot process

In the early days, bootstrapping a computer meant feeding a paper tape containing a boot program or manually loading a boot program using the front panel address/data/control switches. Today's computers are equipped with facilities to simplify the boot process, but that doesn't necessarily make it simple.
Let's start with a high-level view of Linux boot so you can see the entire landscape. Then we'll review what's going on at each of the individual steps. Source references along the way will help you navigate the kernel tree and dig in further.

Overview

Figure 1 gives you the 20,000-foot view.
Figure 1. The 20,000-foot view of the Linux boot process
High-level view of the Linux kernel boot
When a system is first booted, or is reset, the processor executes code at a well-known location. In a personal computer (PC), this location is in the basic input/output system (BIOS), which is stored in flash memory on the motherboard. The central processing unit (CPU) in an embedded system invokes the reset vector to start a program at a known address in flash/ROM. In either case, the result is the same. Because PCs offer so much flexibility, the BIOS must determine which devices are candidates for boot. We'll look at this in more detail later.
When a boot device is found, the first-stage boot loader is loaded into RAM and executed. This boot loader is less than 512 bytes in length (a single sector), and its job is to load the second-stage boot loader.
啟動裝置被找到以後,第一個階段是將boot loader載入至RAM中並且執行。boot loader小於 512bytes(一個sector大小),boot loader的任務是交棒給第二階段。
When the second-stage boot loader is in RAM and executing, a splash screen is commonly displayed, and Linux and an optional initial RAM disk (temporary root file system) are loaded into memory. When the images are loaded, the second-stage boot loader passes control to the kernel image and the kernel is decompressed and initialized. At this stage, the second-stage boot loader checks the system hardware, enumerates the attached hardware devices, mounts the root device, and then loads the necessary kernel modules. When complete, the first user-space program (init) starts, and high-level system initialization is performed.
當第二階段boot loader已經在RAM中執行,螢幕通常已經被點亮,linux以及擴充RAM(temporary root file system)被載入至記憶體中。當原本在儲存裝置裡的image已經載入到記憶體中,第二階段boot loader會交移控制給kernel並且kernel解壓鎖並且初始化。在這個階段的時候,要檢查系統的硬體,列舉出附屬的硬體裝置,掛載ROOT的裝置,然後載入必要的kernel模組。完成後開始第一使用者空間程式初始化,執行high level初始化。
That's Linux boot in a nutshell(簡而言之). Now let's dig in a little further and explore some of the details of the Linux boot process.

System startup

The system startup stage depends on the hardware that Linux is being booted on. On an embedded platform, a bootstrap environment is used when the system is powered on, or reset. Examples include U-Boot, RedBoot, and MicroMonitor from Lucent. Embedded platforms are commonly shipped(運送) with a boot monitor. These programs reside in special region of flash memory on the target hardware and provide the means to download a Linux kernel image into flash memory and subsequently execute it. In addition to having the ability to store and boot a Linux image, these boot monitors perform some level of system test and hardware initialization. In an embedded target, these boot monitors commonly cover both the first- and second-stage boot loaders.
系統啟動的階段,取決於正在booted on的硬體。在嵌入式環境,bootstrap是使用在上電開機的時候。嵌入式的平台,通常會帶有boot的監控器。boot monitor的程式通常在flash的特定區塊,被安置在目標的硬體上,並提供方法可以下載image進入flash中並執行。除了能夠儲存和boot Linux的image,boot monitors也實現系統層級的test,和硬體初始化。在嵌入式平台上,boot monitor通常就做掉第一和第二階段的boot loader工作。


In a PC, booting Linux begins in the BIOS at address 0xFFFF0. The first step of the BIOS is the power-on self test (POST). The job of the POST is to perform a check of the hardware. The second step of the BIOS is local device enumeration and initialization.

在PC環境中,BIOS過程中 booting 起始在address 位址 0xFFFF0處。BIOS的第一步是POST(上電自我檢查),POST的工作就是檢查硬體。第二步是列舉所有硬體及初始化。
Given the different uses of BIOS functions, the BIOS is made up of two parts: the POST code and runtime services. After the POST is complete, it is flushed from memory, but the BIOS runtime services remain and are available to the target operating system.
鑒於BIOS function 有不同用途,BIOS主要由兩個部分組成,POST的code與執行時期的服務(services)。POST完成以後,完成以後POST的code就可以從記憶體中拿掉或覆蓋了,除了BIOS runtime services會保留到操作系統起來。
To boot an operating system, the BIOS runtime searches for devices that are both active and bootable in the order of preference defined by the complementary metal oxide semiconductor (CMOS) settings. A boot device can be a floppy disk, a CD-ROM, a partition on a hard disk, a device on the network, or even a USB flash memory stick.
為了boot作業系統起來,BIOS的執行時期按照CMOS的設定去搜尋可以被boot且是active的裝置。這個boot裝置可以是軟碟、CDROM或是硬碟的區塊、網路裝置或是USB(flash)啟動。
Commonly, Linux is booted from a hard disk, where the Master Boot Record (MBR) contains the primary boot loader. The MBR is a 512-byte sector, located in the first sector on the disk (sector 1 of cylinder 0, head 0). After the MBR is loaded into RAM, the BIOS yields control to it.
通常都是從硬碟boot,在硬碟MBR(master boot record)的位置,那裡存放了primary boot loader。MBR是512byte的sector,位在硬碟第一個區塊(sector 1 of cylinder 0, head 0),在MBR載入至RAM後,BIOS就交移控制權給boot loader。

Stage 1 boot loader

The primary boot loader that resides in the MBR is a 512-byte image containing both program code and a small partition table (see Figure 2). The first 446 bytes are the primary boot loader, which contains both executable code and error message text. The next sixty-four bytes are the partition table, which contains a record for each of four partitions (sixteen bytes each). The MBR ends with two bytes that are defined as the magic number (0xAA55). The magic number serves as a validation check of the MBR.
boot loader放在MBR,是一塊512byte的image,裡面包含程式和分區表。前面的446byte是 primary boot loader,包含可以執行的code和error訊息文字。剩下的64byte是分區表,最後有2byte是0xAA55,用來辨識是MBR。

Figure 2. Anatomy of the MBR
Anatomy of the MBR
The job of the primary boot loader is to find and load the secondary boot loader (stage 2). It does this by looking through the partition table for an active partition. When it finds an active partition, it scans the remaining partitions in the table to ensure that they're all inactive. When this is verified, the active partition's boot record is read from the device into RAM and executed.
primary boot loader 的工作是去找到並且載入第二個boot loader(stage 2)。透過查找分區表找裡面是active的分區。找到active的分區以後,繼續搜尋分區表確保裡面剩下的都是inactive的分區。當驗證後,將active分區的boot record寫進到RAM並執行。

Stage 2 boot loader

The secondary, or second-stage, boot loader could be more aptly called the kernel loader. The task at this stage is to load the Linux kernel and optional initial RAM disk.
第二階段boot loader可以呼叫kernel loader,這階段的任務是載入Kernel和選擇性的初始化RAM disk。
The first- and second-stage boot loaders combined are called Linux Loader (LILO) or GRand Unified Bootloader (GRUB) in the x86 PC environment. Because LILO has some disadvantages that were corrected in GRUB, let's look into GRUB. (See many additional resources on GRUB, LILO, and related topics in the Resources section later in this article.)
第一和第二階段結合的 boot loader叫 LILO(Linux Loader)或是 GRUB(GRand Unified Bootloader)。
The great thing about GRUB is that it includes knowledge of Linux file systems. Instead of using raw sectors on the disk, as LILO does, GRUB can load a Linux kernel from an ext2 or ext3 file system. It does this by making the two-stage boot loader into a three-stage boot loader. Stage 1 (MBR) boots a stage 1.5 boot loader that understands the particular file system containing the Linux kernel image. Examples include reiserfs_stage1_5 (to load from a Reiser journaling file system) or e2fs_stage1_5 (to load from an ext2 or ext3 file system). When the stage 1.5 boot loader is loaded and running, the stage 2 boot loader can be loaded.
GRUB最棒的事情是包含LINUX檔案系統的知識。GRUB可以從ext2或是ext3的檔案系統載入Linux Kernel。MBR(stage 1) boot stage 1.5boot loader釐清特定的檔案系統裡面包含linux kernel。stage 1.5完成 stage 2就可以被載入。
With stage 2 loaded, GRUB can, upon request, display a list of available kernels (defined in /etc/grub.conf, with soft links from /etc/grub/menu.lst and /etc/grub.conf). You can select a kernel and even amend it with additional kernel parameters. Optionally, you can use a command-line shell for greater manual control over the boot process.
當stage 2載入,GRUB可以顯示可用的kernel清單(定義在 /etc/grub.conf),你可以選擇一個kernel甚至使用其他的參數來修改kernel。
With the second-stage boot loader in memory, the file system is consulted, and the default kernel image and initrd image are loaded into memory. With the images ready, the stage 2 boot loader invokes the kernel image.
stage 2 boot loader在記憶體中,檔案系統會被參考到,kernel 和 initrd的image會被載入到記憶體中。當image都準備好,stage 2的boot loader會調用(invoke) kernel image。

Kernel

With the kernel image in memory and control given from the stage 2 boot loader, the kernel stage begins. The kernel image isn't so much an executable kernel, but a compressed kernel image. Typically this is a zImage (compressed image, less than 512KB) or a bzImage (big compressed image, greater than 512KB), that has been previously compressed with zlib. At the head of this kernel image is a routine that does some minimal amount of hardware setup and then decompresses the kernel contained within the kernel image and places it into high memory. If an initial RAM disk image is present, this routine moves it into memory and notes it for later use. The routine then calls the kernel and the kernel boot begins.
當kernel image在記憶體中且控制權從stage 2 boot loader移交過來,kernel stage 開始。kernel 是一個壓縮的image而不是可以直接執行的kernel。在image的前段包含需要最輕量化可以啟動硬體的routine然後解壓縮kernel內容放到高的記憶體。如果現在是在RAM disk image,會將routine 移到記憶體中並記錄下來供之後使用。然後routine可以呼叫kernel然後kernel boot 開始。
When the bzImage (for an i386 image) is invoked, you begin at ./arch/i386/boot/head.Sin the start assembly routine (see Figure 3 for the major flow). This routine does some basic hardware setup and invokes the startup_32routine in./arch/i386/boot/compressed/head.S. This routine sets up a basic environment (stack, etc.) and clears the Block Started by Symbol (BSS). The kernel is then decompressed through a call to a C function called decompress_kernel(located in./arch/i386/boot/compressed/misc.c). When the kernel is decompressed into memory, it is called. This is yet another startup_32 function, but this function is in ./arch/i386/kernel/head.S.
routine 會做基本的硬體建立並調用startup_32 routine 建立起基本的環境(stack, etc.)然後清除 Block Started by Symbol(BSS),kernel解壓縮是透過呼叫C函式 decompress_kernel。

In the new startup_32 function (also called the swapper or process 0), the page tables are initialized and memory paging is enabled. The type of CPU is detected along with any optional floating-point unit (FPU) and stored away for later use. The start_kernel function is then invoked (init/main.c), which takes you to the non-architecture specific Linux kernel. This is, in essence, the main function for the Linux kernel.
startup_32 (也叫切換或是程序0),分頁表被初始化然後記憶體分頁被致能。偵測CPU的型別是否有任何FPU然後儲存之後可以使用。start_kernel(init/main.c)被調用,帶你到非特定linux kernel 結構。實質上,這是linux kernel的主要功能。
Figure 3. Major functions flow for the Linux kernel i386 boot
Major Functions in Linux Kernel i386 Boot Process
With the call to start_kernel, a long list of initialization functions are called to set up interrupts, perform further memory configuration, and load the initial RAM disk. In the end, a call is made to kernel_thread (in arch/i386/kernel/process.c) to start the init function, which is the first user-space process. Finally, the idle task is started and the scheduler can now take control (after the call to cpu_idle). With interrupts enabled, the pre-emptive scheduler periodically takes control to provide multitasking.
呼叫starta_kernel,會有一個清單的函式被呼叫,用來建立中斷,平台更進一步記憶體組態,然後載入初始化RAM disk,用來跑第一使用者空間程序。最後,空閒的task開始排程,可以開始控制排程。當中斷被致能,pre-emptive 排程器週期性地掌控控制權以提供multitasking。
During the boot of the kernel, the initial-RAM disk (initrd) that was loaded into memory by the stage 2 boot loader is copied into RAM and mounted. This initrd serves as a temporary root file system in RAM and allows the kernel to fully boot without having to mount any physical disks. Since the necessary modules needed to interface with peripherals can be part of the initrd, the kernel can be very small, but still support a large number of possible hardware configurations. After the kernel is booted, the root file system is pivoted (via pivot_root) where the initrd root file system is unmounted and the real root file system is mounted.
在kernel boot的期間,初始化RAM disk被載入到記憶體中,被stage 2 boot loader複製進RAM且安裝。initrd 服務一個暫時的root檔案系統在RAM然後允許kernel boot。
The initrd function allows you to create a small Linux kernel with drivers compiled as loadable modules. These loadable modules give the kernel the means to access disks and the file systems on those disks, as well as drivers for other hardware assets. Because the root file system is a file system on a disk, the initrdfunction provides a means of bootstrapping to gain access to the disk and mount the real root file system. In an embedded target without a hard disk, the initrd can be the final root file system, or the final root file system can be mounted via the Network File System (NFS).

Init

After the kernel is booted and initialized, the kernel starts the first user-space application. This is the first program invoked that is compiled with the standard C library. Prior to this point in the process, no standard C applications have been executed.
In a desktop Linux system, the first application started is commonly /sbin/init. But it need not be. Rarely do embedded systems require the extensive initialization provided by init (as configured through /etc/inittab). In many cases, you can invoke a simple shell script that starts the necessary embedded applications.

Summary

Much like Linux itself, the Linux boot process is highly flexible, supporting a huge number of processors and hardware platforms. In the beginning, the loadlin boot loader provided a simple way to boot Linux without any frills. The LILO boot loader expanded the boot capabilities, but lacked any file system awareness. The latest generation of boot loaders, such as GRUB, permits Linux to boot from a range of file systems (from Minix to Reiser).

2017年12月12日 星期二

VGA硬體控制與暫存器

VGA的暫存器,大致可分為六組:

一般暫存器(General Registers)

  • Miscellaneous Out Register
    • 寫 3C2
    • 讀 3CC
  • Input Status Register 0
    • 讀 3C2
  • Input Status Register 1
    • 讀 單色:3BA/彩色:3DA
  • Feature Control Register
    • 寫 單色:3BA/彩色:3DA
    • 讀 3CA
  • Video SubSystem Enable
    • 讀寫 3C3

Sequencer暫存器(Sequencer Registers)

  • 指標暫存器
    • 讀寫 3C4
  • 資料暫存器
    • 讀寫 3C5

CRT控制暫存器(CRTC Registers)

  • 指標暫存器
    • 讀寫 單色:3B4/彩色:3D4
  • 資料暫存器
    • 讀寫 單色:3B5/彩色:3D5

Graphics暫存器(Graphics Registers)

  • 指標暫存器
    • 讀寫 3CE
  • 資料暫存器
    • 讀寫 3CF

Attribute暫存器(Attribute Registers)

  • 指標暫存器
    • 讀寫 3C0
  • 資料暫存器
    • 寫 3C0
    • 讀 3C1

RAM DAC暫存器(RAM DAC Registers)


  • 指標暫存器
  • 資料暫存器




顯示卡與PC之間的關係

顯示卡大致上可以分成四個部分:
顯示記憶體(Display Memory)
負責儲存所有圖像資料,螢幕上之所以能夠看到各種不同的畫面,都是因為顯示記憶體中存放的內容不同。也就是說顯示記憶體的內容,決定了螢幕上所顯示的畫面。

震盪器(Oscillator)
和CPU相同,VGA同樣需要使用不同的時脈(Clock),來幫助VGA在有不同解析度的需求時,達到適當的同步控制。

VGA控制器(VGA Controller)
VGA控制晶片,基本上有兩個功能,一是將CPU傳來的資料寫入顯示記憶體,二是不斷地抓取顯示記憶體上的資料顯示到螢幕上,後面的動作並不佔用CPU時間。

顯示卡上的BIOS
這裡指的BIOS並不是主機板上的BIOS,它提供了一些基本功能呼叫給程式設計員,便於操控顯示卡,而這些功能呼叫一般是透過INT 10H來執行的。

一般我們看到顯示在螢幕上的圖像,都是經由CPU將資料寫入顯示記憶體,再經由VGA控制器的運作將資料顯示到螢幕上。至於CPU對顯示記憶體的定址方式,顯示記憶體通常落在A0000H~BFFFFH範圍之內128K。

2017年12月3日 星期日

DOS指令

DOS有內部命令和外部命令,外部命令以檔案格式exe.com儲存在磁碟裡。

CD:到指定的目錄

DIR:顯示目錄,這個目錄可以是磁碟機,也可以是資料夾。

如果使用DIR/P每次就只會出現一頁,避免頁數太多,前面出現的就不見了

REN:重新命名。只要輸入REN TEXT1 R_TEST 。就會改成後面那個名子。

MODE.EXE
可以用來設定COM port
MODE com1:96,n,8,1

INT.EXE
使用INT

2017年11月29日 星期三

PCI裝置掃描



PCI裝置有提供IO和MMIO的方式可以存取,但是在資源還沒有分配的時候,還是只能透過IO進行讀寫。

要從IO去存取PCI BUS上的裝置,需要透過PCI 的兩個 IO port,address port(0xcf8) and data port(0xcfc)。在address port 的位置寫入要存取的PFA(PCI function address),即可對目標的暫存器進行讀寫。


PFA由PCI裝置的BUS[23:16]、DEVICE[15:11]、FUNCTION[10:8]和REG offset[7:2]組成。[31]固定填1。

根據位元數決定最大支援多少BUS,或裝置。

Bit[31]: It is an enable flag for determining when accesses to CONFIG_DATA are to be translated to configuration transactions on the PCI bus.
Bit[30:24]: Reserved
Bit[23:16]: Bits 23 through 16 choose a specific PCI bus in the system.
Bit[15:11]: Bits 15 through 11 choose a specific device on the bus.
Bit[10:8]: Bits 10 through 8 choose a specific function in a device.
Bit[7:2]: Bits 7 through 2 choose a DWORD in the device's Configuration Space.
Bit[1:0]: Bits 1 and 0 are read-only and must return 0's when read.


31302928272625242322212019181716151413121110 9 8 7 6 5 4 3 2  1 0
 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 1 1 0 0 0 0 0 1 1 1 1 0
 0

在PCI SCAN後要完成 Configuration setting 和 Linking list of PCI device的建立。在這個LIST裡面,每個NODE都是一個PCI device,SCAN的過程中就要去設置好PCI裝置的組態

2017年11月27日 星期一

PCI裝置-VGA

Register Overview

  1. IO空間:分成三塊,PCI介面、VGA空間、擴充IO空間
  2. 記憶體空間:分成三塊,Base 0是留給繪圖緩衝區的4M Bytes大小的記憶體、Base 1是保留給MMIO的128 K Bytes大小的記憶體、Base 2是保留給IO空間的128K Bytes大小的記憶體。
    • MB0開始於PCI組態表頭中偏移量10h的位址,4MB。
    • MB1開始於PCI組態表頭中偏移量14h的位址,128KB。
    • MB2開始於PCI組態表頭中偏移量18h的位址,128KB。
  3. 映射記憶體空間
    • 0~128k-1
    • 0x0000~0x00FF
    • 0x0500~0x05FF
PCI Controller

Device and Vendor ID Reg
Command and Status Reg
Class and Revision Reg
Miscellaneous Reg
BAR0
BAR1
BAR2
subsystem ID Reg
Expansion ROM BAR
capability Reg
Interrupt Reg
PCI PM capability Reg


2017年11月26日 星期日

PCI-to-PCI 橋接器

PCI to PCI 的 Class Code

強制要實作的。在DWORD 2的byte 0, byte 1, byte 2。
對PCI-to-PCI 橋接器而言,類別碼為06h,子類別04h,
程式介面:

  • 00h:正常的PCI-to-PCI橋接器
  • 01h:除了正常的PCI-to-PCI橋接器操作以外,還可以在橋接器主要端進行相減解碼的PCI-to-PCI橋接器。
還沒有組態橋接器之前,PCI Scan只會掃到BUS 0上看得到的PCI device與PCI橋接器。
EX: host/PCI橋接器(0600)、PCI/ISA橋接器(0601)、PCI-to-PCI橋接器(0604)。


可延展的匯流排架構

只含有一個PCI匯流排的機器受到很明顯的限制,EX:
  • 假如有太多電力負載被放在同一條PCI匯流排上,會無法正常運作。
  • 安裝在特定PCI匯流排上的裝置可能無法好好的共存。某些Master裝置可能需要大量匯流排時間,會降子系統效能。
  • 一條PCI匯流排只支援有限數量的PCI擴充連接器。

每一個PCI-to-PCI橋接器連接兩條PCI匯流排,被稱為它的主匯流排(Primary)與次(Secondary)匯流排。

  • Downstream(下游):當交易被起始,透過一或多個PCI-to-PCI橋接器被傳遞,並流向遠離主處理器的方向時,它就被稱為下游。
  • Upstream(上游):當交易被起始,透過一或多個PCI-to-PCI橋接器被傳遞,並流向主處理器時,它就被稱為上游。
  • 主匯流排(Primary bus):在橋接器的上游端之PCI匯流排。
  • 次匯流排(Secondary bus):在橋接器的下游端之PCI匯流排。[write to video memory]
  • 附屬匯流排(Subordinate bus):在橋接器的下游端號碼最大的PCI匯流排。


主匯流排號碼,被軟體以橋接器上游的PCI匯流排號碼初始化。由於host/PCI橋接器只會連接一個PCI匯流排,所以只有實作一個匯流排號碼暫存器。即host/PCI橋接器不需要實作次匯流排號碼暫存器。

次匯流排號碼,被軟體以橋接器下游的PCI匯流排號碼(換言之,第幾個BUS的編號)初始化。pci scan往下游找的時候,找到新的橋接器開始依序填值在次匯流排號碼中,新的橋接器代表下面又連了一個新的匯流排。每找到一個新的次匯流排,就往上回填附屬匯流排號碼。

匯流排號碼暫存器

剛開始作PCI SCAN的時候,要先掃出所有PCI裝置,掃到橋接器的時候要開始記錄號碼,並填入匯流排號碼。否則橋接器下游的裝置會看不到,往下游繼續找直到掃出所有裝置。

每一個PCItoPCI橋接器必須實作三個強制性的匯流排暫存器。它們都是可讀寫的,並且在重置時會被清除為0。在組態過程中,組態軟體會初始化這三暫存器,來指定匯流排號碼。這些暫存器是:

  • 主匯流排號碼暫存器(Primary Bus Number register)
  • 次匯流排號碼暫存器(Secondary Bus Number register)
  • 附屬匯流排號碼暫存器(Subordinate Bus Number register)

在主要端的Type 1組態讀取與寫入,它會被當作Type 0組態存取(假如target是在橋接器的次匯流排上)或是Type 1組態存取(假如Target是在附屬屬於橋接器次匯流排的匯流排上)。
在主要或次要端上接收到Type 1組態寫入,會被轉換成在指定的target匯流排上的特殊週期。

顯示器組態

可能有兩個顯示配接卡存在
假設一個平台安裝了兩張顯示配接卡,但是只有一個顯示器。其中一張配接卡是與VGA相容的ˊ,另一張是Advanced Graphics Adapter(進階圖形配接卡),或GFX。

兩張配接卡中都有DAC,用來驅動CRT,DAC含有一組調色盤暫存器集(color palette register set)。當每一個點(dot/pixel)被寫到顯示幕上時,圖形配接卡會提供資料給DAC。

兩個配接卡的識別
VGA配接卡的類別碼為03h,其子類別碼為00h,GFX配接卡的類別碼為03h,其子類別碼為08h。

BUS上可能的CASE

配接卡可能在相同或不同的匯流排上

對調色盤暫存器的IO寫入都必須同時更新在VGA和GFX這兩個配接卡DAC裡的調色盤暫存器。這兩個顯示配接卡會位於機器的何處有許多不同可能。

假如它們是在相同的PCI匯流排上,且IO寫入進行對調色盤暫存器寫入,除非有某件事情可以阻止它,否則在IO讀取或寫入在調色盤暫存器上進行時,兩個裝置都可以驅動DEVSEL#到低態來宣告交易。 >>>> 這會違反協定的現象。

假如配接卡在不同匯流排上,則是兩匯流排間的橋接器必須被程式規劃,以確保兩個配接卡都看到IO寫入交易,但是只有一個會當作交易的target來主動參與。

配接卡在不同BUS上的解決方案

  • 在顯示配接卡的指令暫存器裡的VGA調色盤監控(Palette Snoop)位元。
  • 在橋接器的橋接器控制暫存器裡的VGA致能位元。
  • 在橋接器的指令暫存器裡的VGA調色盤監控位元。

配接卡與橋接器的偵測與組態

用來偵測及設定VGA相容的控制器與非VGA,即GFX控制器的程序如下:
  1. 確認要在啟動程序中使用的VGA顯示裝置。這是藉著先掃描標準擴充匯流排(EX:ISA、EISA)的方式達成的。假如在擴充匯流排上找到顯示裝置的話,該顯示器會在啟動程序中使用,而且初始化程序完成。不用進行下列的步驟。假如在擴充匯流排上找不到VGA裝置,則從具有最大匯流排號碼的匯流排開始,掃描PCI匯流排。假如在PCI匯流排上找到的話,則儲存匯流排號碼,給在初始化程序的其餘步驟使用。
  2. 設定在裝置的指令暫存器裡的IO空間與記憶體空間致能位元,讓它可以回應VGA存取。
  3. 從啟動顯示裝置所在的PCI匯流排開始,掃描PCI匯流排階層的上游(到匯流排0為止)。在每一個偵測到的PCI-to-PCI橋接器裡,設定在其橋接器控制暫存器裡的VGA致能位元。
  4. 從啟動顯示器裝置所在的PCI匯流排開始,掃描PCI匯流排下游(所有附屬於此匯流排的匯流排),尋找GFX。當找到第一個GFX的案例時,中止掃描。設定GFX裝置指令暫存器的IO空間與VGA調色盤監控致能位元。
  5. 從GFX所在的匯流排往上游方向掃描回去,直到啟動顯示器所在的匯流排。在每一個所遇到的PCI-to-PCI橋接器裡,設定橋接器指令暫存器的VGA調色盤監控致能位元。
  6. 最後,設定在啟動顯示裝置的指令暫存器內的VGA調色盤監控致能位元。
PCI-to-PCI橋接器規格提供了兩個虛擬碼(pseudo-code),它們可以用來實作上述步驟的程序。

  • DispalyInit()。這是最頂層的程序。沒有輸入參數,並且它呼叫GFXScanR程序。
  • GFXScanR(BusNum)。此程序掃描附屬於啟動顯示器所在之匯流排的匯流排,以尋找第一個GFX裝置案例(Instance)







2017年11月24日 星期五

ROM之於PCI

ROM的目的:裝置可以在啟動程序中被使用

為了啟動OS到記憶體中,系統需要三個裝置:

  • 用來從其中載入OS的大量儲存媒體。這有時也稱為IPL(Initial Program Load,初始程式載入)裝置,通常是一個IDE或SCSI硬碟。
  • 在啟動過程中,用來顯示進行中訊息的顯示配接卡。通稱為輸出裝置。
  • 在啟動過程中,用來讓使用者與機器交談的鍵盤。通稱為輸入裝置。
OS必須同時找到這三個裝置,同時必須找到與每一個裝置有關的裝置驅動程式。這個階段還未從磁碟機載入任何可載入的裝置驅動程式到記憶體中。這是ROM存在的主因。它包含允許裝置在啟動過程中被使用的裝置驅動程式。

ROM偵測

當組態軟體正在組態PCI功能時,它會檢查設計者是否實作擴充ROM基底位址暫存器的方式,決定是否有功能特定的ROM存在。

程式執行裝置下列方式,確認裝置是否存在:

  • 指定一"基底位址"給暫存器的基底位址欄位,
  • 致能其解碼器(以設定暫存器的位元0為1的方式),
  • 設定功能指令暫存器的記憶體空間位元,
  • 然後嘗試去讀取ROM的前兩個位置。

假如前兩個位置包含ROM簽名,AA55h的話,則ROM存在。


ROM Shadowing的必要

  • PCI規格要求裝置ROM程式碼不能在原地執行(即在ROM裡面)。它必須被複製到主記憶體裡面。這稱為"Shadowing"ROM程式碼。這要求的存在有兩個原因:

ROM的存取時間通常很慢,每次讀取ROM程式碼來執行都會造成極差的效能。

  • 一旦在ROM裡裝置驅動程式的初始化程式碼部分被執行後,它就可以被丟棄,並且在主記憶體裡的程式碼影像(code image)可以被縮短到只剩執行期操作所需要的部分。配置給儲存程式碼的初始化部分之主記憶體可以被釋放,使得主記憶體的使用更有效率。
一旦發現裝置ROM存在,組態軟體必須複製程式碼影像到主記憶體中,然後關閉(Disable)ROM位址解碼器。在非PC環境裡,儲存程式碼影像副本的記憶體區域可以在4GB空間的任何一處。該環境所用的規格可以定義一個特定的區域給程式碼影像。

在PC環境裡,ROM程式碼影像必須被複製到主記憶體裡與裝置ROM在歷史上有相關聯的位址範圍內:000C0000h 到 000DFFFFh。假如類別碼表示這是一個VGA的裝置ROM,則其程式碼必須被複製到000C0000h位置最開始的地方。


BIOS呼叫不同匯流排環境的匯流排列舉器(Bus Enumerator)

一個機器架構裡面可以含有許多種不同的裝置環境。例如PCI、CardBus、EISA、Plug-and-Play、ISA等等。用來存取每一個與這些不同裝置種類相關的組態暫存器的方法彼此間有很大的差異。以外,其組態暫存器的配置和格式也有相當大的差異。

BIOS會含有每一個環境所需的一個單獨的、匯流排特定的程式。這程式經常被稱為匯流排列舉器(Bus Enumerator)。匯流排列舉器知道:

  • 如何存取特定型態裝置(例如:PCI裝置)裡的組態暫存器。
  • 如何找到在這環境裡的裝置。例如,在PCI環境裡,程式執行裝置讀取PCI功能製造商識別碼暫存器裡的製造商識別碼。任何非FFFFh的值代表有效的識別碼,而FFFFh代表在目前的定址的位置上沒有任何裝置存在。
  • 如何探測裝置的組態暫存器,以便找到裝置的資源需求。
  • 如何配置已選定的資源給裝置。

系統BIOS必須呼叫每一個所支援之平台匯流排環境的匯流排列舉器。每當特定的列舉器被呼叫時,它會找到所有其target環境裡的裝置,找到每一個裝置所需的資源,並且將不互相衝突的資源配置給每一個裝置。不過,它不會致能這些裝置。列舉器在記憶體內建立一個資料結構,列出它所找到屬於其形態的所有裝置。然後,它將指向該資料結構開頭的指標傳回給系統BIOS。

BIOS選擇啟動裝置並找到它們的驅動程式

接著,系統BIOS掃描資料結構,找出輸入裝置、輸出裝置、以及一個IPL裝置,以便在啟動OS到記憶體時使用。為了在啟動程序裡,使用這些裝置,因此需要有每一個裝置的裝置驅動程式。驅動程式可以內嵌在BIOS本身裡面,或是在所找到的每一個裝置的ROM裡面。

對這三個啟動裝置的每一個說,BIOS接下來會:
  • 呼叫在裝置驅動程式裡的初始化程式碼。接著,初始化程式碼完成使用裝置的準備
  • 然後,BIOS設定在組態指令暫存器裡適當的位元(EX記憶體空間;IO空間;Bus Master),致能此裝置,並讓它上線。

BIOS啟動隨插即用OS並將指標傳給它

然後,系統BIOS使用這三個裝置,啟動OS到記憶體內,並且將控制權傳給OS。它同時把指向這資料結構清單開頭的指標傳給OS,這資料結構用來確認所有OS必須操作的裝置。
OS找到並載入驅動程式,然後呼叫每一個裝置的初始化程式碼(Init Code)
請注意,Init Code(初始化程式碼)代表驅動程式的初始化部分。其次,OS找出每一個裝置在磁碟上的驅動程式,然後將它們一個接著一個地載入到記憶體內。每當它載入一個驅動程式時,它就會呼叫其初始化程式碼進入點,而驅動程式會完成裝置特定的設定,並且讓裝置上線。如此,機器便完成啟動並開始運作,從這一刻起,由OS來管理系統裝置。

2017年11月23日 星期四

關於 PCI Configuration 的認識

Configuration 組態

組態表頭區域(Configuration Header Region)

除了 host/PCI 橋接器以外,每一個PCI功能都必須實作PCI組態空間,而PCI組態暫存器就是位於此空間中。host/PCI橋接器可以在PCI組態空間、在IO空間或在記憶體空間實做這些暫存器。

PCI組態空間,透過這個區塊,設計程式的人可以先從這個表中,讀取某些唯讀的區塊,獲取硬體相關的資訊,因為只要是符合PCI規範的裝置,這個表的實作格式都會一樣。

每一個PCI功能擁有保留給實作其組態暫存器用的64個組態 dword區塊。前面16個dword的格式與使用是由PCI規格預先定義的。這區域被稱為裝置的組態表頭區域(或表頭空間(Header Space))。規格目前定義了三種表頭格式。


Header Type One 為 PCI to PCI 橋接器定義的。


Header Type Two 為 PCI to CardBus 橋接器定義的。在PC Card規格有完整的定義。


Header Type Zero 是給所有除了 PCI to PCI 與 CardBus橋接器以外的裝置使用的



Header Type Zero 在表頭內的佔存器可用通用的方式來確認裝置、控制其PCI功能、以及偵測其狀態。裝置組態空間的其他48個dword的使用是裝置特定的。64(PCI device)=16(header)+48(for device)。


2017年7月24日 星期一

# include

Marco #include

GCC documents > Computed Includes

在程式中常用到的
#include "stdio.h"
是 marco 的用法,是complie的時候,preprocessor會去處理,將 include的文件,在敘述中展開。
被稱為 computed include.

#if SYSTEM_1
# include "system_1.h"
#elif SYSTEM_2
# include "system_2.h"
#elif SYSTEM_3
…
#endif


使用 include 的時候,會根據雙引號與角括號的不同,以對應的規則去搜尋包含的文件名稱。
雙引號優先以source code當前的目錄做搜尋,角括號則以環境參數所預設的 include路徑為主。
#include "file"
#include <file>

include 用來包含 header file
頭文件中可以用來包含的函式的宣告,可以將一份宣告透過共用於多個文件。
當被用到的函式,也是其他程式中很常被呼叫的,就可以宣告在頭文件中。
可以避免像是同樣的函式在每份源文件中宣告出錯,修改函式也只需要一次。

2017年7月17日 星期一

序列傳輸 - 序列埠通訊

Serial Communication串列傳輸




Introduction 


一般的嵌入式系統泛指由 IC 和 CPU 互聯電路所共同組成的系統,其中目標包含內部獨立的電路可以交換、傳遞彼此的資訊,必須能夠透過共同的通訊協定來溝通。 所以為了使各種元件或 IC 可以順利溝通,出現了各種不一樣的通訊協定,可以達到資料交換的目的。 一般而言,會將通訊類型分成兩大類,並列式(parallel)和序列式傳輸(serial)。

Parallel v.s. Serial


並列式傳輸是可以同時間傳送多個位元,常見的匯流排(bus)是 8 ,16 或更多線所組成。

序列式傳輸則是一個時間點只會送出一個位元,將位元依序送出,最少可以只用一條線,常見的應用一般不使用超過四條線。

Asynchronous Serial


經過長時間的發展,已經有各種序列式的通訊協定出現,可以適用各種不同嵌入式系統所需的條件。如 USB 和 乙太網路都是比較常見的通訊方式。 其他常見的序列式介面包含 SPI、I2C 。

序列傳輸又可以分成兩類,同步(Synchronous)與非同步(Asynchronous)。

同步傳輸的序列介面,除了資料線以外,會多一條線作為傳輸時脈(Clock)信號,如此一來只要是在此序列同步傳輸的匯流排上的裝置,都可以參考同一個 clock 做資料同步。

非同步傳輸則是沒有額外的訊號線作為傳輸時脈的用途,這代表必須要用其他的方式來使得傳輸更可靠。

Rules of Serial

非同步序列的傳輸,有很多種的規則,必須要透過規範的機制,來使得傳輸更加可靠,才不會造成資料丟失或者資料錯誤的情況發生。

這個機制需要透過額外的資訊來實現

Data bits,
Synchronization bits,
Parity bits,
Baud rate.

這個機制的調整彈性相當的大,並且沒有辦法連續的一直送出資料,而是將資料分成很多的小訊框在送出,這個機制下最重要的是確保通訊的兩方要在同樣通訊協定的配置下。


https://learn.sparkfun.com/tutorials/serial-communication 


2017年7月7日 星期五

malloc() 動態設置記憶體


int *ptr = malloc(sizeof(int));

void *malloc(size_t size)

size 所需記憶體區塊大小的byte數

回傳值是指向所指定的記憶體區塊的指標,失敗的話回傳NULL


#include <stdio.h>
 #include <stdlib.h> 

int main(void) 
  int *ptr = malloc(sizeof(int));
  printf("空間位置:%p\n", ptr);
  printf("空間儲存值:%d\n", *ptr);
  *ptr = 200;
  printf("空間位置:%p\n", ptr);
  printf("空間儲存值:%d\n", *ptr);
  free(ptr);
  return 0;
}


執行結果:

空間位置:0xb27010 
空間儲存值:0 
空間位置:0xb27010 
空間儲存值:200

如果使用了 malloc 分配動態記憶體,可是卻沒有使用 free 歸還記憶體,可能會發生記憶體用盡的情況。

陣列在使用的時候,必須事先決定好陣列的大小,有時候一開始不知道會使用多大的陣列,或是希望由使用者來自定大小,就可以使用動態記憶體配置,加上指標來實作。

int *arry = malloc(1000 * sizeof(int));

此行分配了 1000 個 int 大小的空間,並傳回空間的第一個位址配置後的空間資料是未知的。可以使用 calloc() 來宣告空間配置。

int *arry = calloc(1000, sizeof(int));

分配了 1000 個 int 大小的空間,並將空間裡的所有值初始化成 0。在使用 malloc 或是 calloc 進行記憶體配置,在不使用時,應該使用 free() 釋放記憶體。



參考:
https://openhome.cc/Gossip/CGossip/MallocFree.html


2017年7月5日 星期三

使用 sprintf 將字串組合

格式


int sprintf (char * str, const chat * format, ...);

sprintf 格式的組成和 printf 的使用一樣,不一樣的是可以將 C 的字串組合後,藉由指向 char 的 pointer str 存入 buffer 內容中。

要注意這裡使用的 buffer 應該要夠大,才能夠放進組合後整個字串的大小。


str:指向 buffer 的 pointer,用來儲存字串。大小應該要足夠存放運算結果的字串。

format:與 printf 的格式相同, %d, %x, %s, ...

Return Value:函數前面的型態是 int,代表回傳值會是一個 int ,如果函數執行成功,會回傳寫進的字元的總數


---------------------
#include <stdio.h>

char buffer[20];  // the number of result that both added cant larger than 20 bytes
char string[] = "TEXT";
int number = 1234;


main()
{
  sprintf( buffer, "%s, + %d \n" , string, number);
  printf("print result : %s \n",buffer);

  return 0;
}



--------------------

另外看 printf 的格式有哪些


#include <stdio.h> 

 int main() 
{
  printf ("Characters: %c %c \n", 'a', 65); 
  printf ("Decimals: %d %ld\n", 1977, 650000L);  
  printf ("Preceding with blanks: %10d \n", 1977); 
  printf ("Preceding with zeros: %010d \n", 1977); 
  printf ("Some different radixes: %d %x %o %#x %#o \n", 100, 100, 100, 100, 100); 
  printf ("floats: %4.2f %+.0e %E \n", 3.1416, 3.1416, 3.1416); 
  printf ("Width trick: %*d \n", 5, 10); 
  printf ("%s \n", "A string"); return 0; 
}
D:\cp\code>gcc printf.c -o printf 

D:\cp\code>printf 
Characters: a A 
Decimals: 1977 650000 
Preceding with blanks:            1977 
Preceding with zeros: 0000001977 
Some different radixes: 100 64 144 0x64 0144 
floats: 3.14 +3e+000 3.141600E+000 
Width trick: 10 A string
符號說明範例
c字元 (char)a
d整數 (Decimal integer)372
i整數 (Decimal integer) (同 d)372
f浮點數 (Floating Point)372.56
e科學記號 (Scientific notation)3.7256e+2
E科學記號 (Scientific notation)3.7256E+2
g取浮點數或科學記號當中短的那個372.56
G取浮點數或科學記號當中短的那個372.56
o八進位 (Octal Integer)735
s字串 (String)372
u無號數 (unsigned integer)372
x十六進位 (Hexadecimal integer)3fb
X十六進位 (Hexadecimal integer)3FB
p指標位址B800:0000
n不列印, 用來取得目前輸出長度%n
%印出 % 符號%%


參考


http://programmermagazine.github.io/201401/htm/article2.html







BLE B-001 藍芽模組 (buletooth 4.0)

BLE


概述


使用 TI 的 CC2541F256 晶片 的射頻模組,有低功耗的藍芽版本和體積較小等優點,可以用來實現手機與藍芽模組通訊,模組與模組的點對點通訊。


規格


腳位圖


Command Set

  指令集分成兩類,一類是設置的指令,另一類是用來查看參數。 在要進入設置模式以前,要注意模組的連線配置:同時將腳位 RTS(P0_0) 和 CMD(P0_1) 拉 LOW 電位。

Command Format

與一般藍芽模組不同,不是使用常見的 AT指令,而是WEBEE自己設的 FA格式。

  所有的設置指令格式為: 0xFa + TYPE + DATA_Length + DATA + 0xAA, Frame 的開頭是 FA,TYPE 是類型, DATA_Length 是資料長度, AA 是 Frame 的結尾,用來告知接收端結束。  

指令種類

設定的指令集


切換 peripheral 腳色

切換 Central 腳色

修改 Baud Rate

設置連接對象的位址
  傳的 DATA 是目標的 mac address(6 Bytes),設置成 00 00 00 00 00 00 00 00 00 00 00 00 (全0) 的時候,模組會預設連接第一個搜尋到的藍芽裝置(出廠預設)

設定廣播間隔

設定連接參數

設定模組的名稱

設定廣播報文中的資訊

設定模組掃描周邊的藍芽裝置

設定模組停止掃描

設定模組重啟

設定模組回復出場設置


查看參數的指令集


查看指令的內容包含查看當前的角色, Baud Rate,廣播間隔,連接間隔,廣播名稱。查看模組訊息的指令格式為 FB + TYPE + 00 + AA


查看當前的角色

獲取模組的 Baud Rate

查看目前連接目標的位址

查看模組本身的藍芽位址

獲取當前的廣播間隔

獲取當前的連接參數

查看模組的名稱